* [PATCH v2 5/7] ARM: perf_event: Fully support Krait CPU PMU events
From: Stephen Boyd @ 2014-01-22 20:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <52DEBE6B.7000904@codeaurora.org>
On 01/21/14 10:37, Stephen Boyd wrote:
> On 01/21/14 10:07, Will Deacon wrote:
>> Do you need isbs to ensure the pmresrn side-effects have happened, or are
>> the registers self-synchronising? Similarly for your other IMP DEF
>> registers.
> There aren't any isbs in the downstream android sources so I assume
> they're self synchronizing. I'll confirm with the CPU designers to make
> sure.
>
CPU folks say no need for isb. They mentioned that the lack of an isb
after the armv7_pmnc_enable_counter() call will leave the action of
enabling the counter "in-flight". The window is probably pretty short on
an SMP kernel because of the spin_unlock right after with the barriers
in it, but the same can't be said for a UP kernel.
Also, the fuzzer didn't find anything else, but I found a bug in the
bitmap logic, updated and reran the fuzzer this morning. Everything
looks good.
--
Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum,
hosted by The Linux Foundation
^ permalink raw reply
* [PATCH v2 06/15] watchdog: orion: Remove unneeded BRIDGE_CAUSE clear
From: Jason Gunthorpe @ 2014-01-22 20:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <52E02AB6.7040104@gmail.com>
> Clearing BRIDGE_CAUSE will only clear all currently pending upstream
> IRQs, of course. If WDT IRQ will be re-raised right after that in
> BRIDGE_CAUSE depends on the actual HW implementation, i.e. we do no
> clear the causing IRQ itself but just what it raised in BRIDGE_CAUSE.
Which is why it makes no sense to clear it one time at kernel start.
Either you only get new edge triggered interrupts after request_irq
(sane behavior) or you might sometimes get an old pending edge
triggered interrupt after request_irq (crazy behavior).
Clearing BRIDGE_CAUSE at kernel start only shortens the racy window it
doesn't eliminate it.
In the more familiar level triggered world the driver would go to the
device and ensure it wasn't asserting an IRQ level and then do the
request_irq. This guarentees it won't get an interrupt callback.
In a edge triggered world the driver should go to the device an ensure
that it won't create a new IRQ, then do request_irq - confident that
there will NEVER be a call to the IRQ handler, under any
circumstances.
So I think edge triggered interrupts need to ack any possible old edge
trigger in the cause register before the first unmask - eg in the
setup callback.
> So, you should also clear WDT's irq in the driver yourself to clear a
> possible pending upstream BRIDGE_CAUSE.
Which isn't possible - the BRIDGE_CAUSE is owned by the irq driver and
it must be cleared there, and it must only be cleared after the wdt
has been stopped so it doesn't set it again.
Notice that Ezequiel has added an IRQ handler that just calls panic,
so a spurious interrupt call is VERY VERY BAD.
Jason
^ permalink raw reply
* [PATCH 2/2] ehci-platform: Add support for controllers with big-endian regs / descriptors
From: Hans de Goede @ 2014-01-22 20:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140122213434.00000f25@openwrt.org>
Hi,
On 01/22/2014 09:34 PM, Jonas Gorski wrote:
> Hi,
>
> On Wed, 22 Jan 2014 20:28:26 +0100
> Hans de Goede <hdegoede@redhat.com> wrote:
>> Hi,
>>
>> On 01/21/2014 08:39 PM, Florian Fainelli wrote:
>>> 2014/1/21 Hans de Goede <hdegoede@redhat.com>:
>>>> This uses the already documented devicetree booleans for this.
>>>
>>> (I would greatly appreciate if you could CC people who gave you
>>> feedback on this before)
>>
>> Will do.
>>
>>> A more informative commit message would be welcome, along with a
>>> reference to which Device Tree binding documentation you are referring
>>> to.
>>
>> I've added a reference to the bindings doc in the commit msg for my next version.
>>
>>>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>>>> ---
>>>> drivers/usb/host/Kconfig | 3 +++
>>>> drivers/usb/host/ehci-platform.c | 33 +++++++++++++++++++++++++++++++--
>>>> 2 files changed, 34 insertions(+), 2 deletions(-)
>>>>
>>>> diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
>>>> index 237d7b1..4af41f3 100644
>>>> --- a/drivers/usb/host/Kconfig
>>>> +++ b/drivers/usb/host/Kconfig
>>>> @@ -256,6 +256,9 @@ config USB_EHCI_ATH79
>>>> config USB_EHCI_HCD_PLATFORM
>>>> tristate "Generic EHCI driver for a platform device"
>>>> depends on !PPC_OF
>>>> + # Support BE on architectures which have readl_be
>>>> + select USB_EHCI_BIG_ENDIAN_DESC if (AVR32 || MIPS || MICROBLAZE || SPARC || PPC32 || PPC64)
>>>> + select USB_EHCI_BIG_ENDIAN_MMIO if (AVR32 || MIPS || MICROBLAZE || SPARC || PPC32 || PPC64)
>>>
>>> I do not think this is that simple nor correct for at least Microblaze
>>> and MIPS since they can run in either BE or LE mode, and those
>>> specific platforms should already do the proper select at the
>>> board/SoC level. This *might* be correct for SPARC, PPC32 and PPC64,
>>> although I believe some specific PPC64 boards can run in little-endian
>>> mode like the P-series, SPARC might too.
>>>
>>> It seems to me that you should not touch this and keep the existing
>>> selects in place, if it turns out that the selects are missing the
>>> error messages you added below are catching those misuses.
>>
>> As discussed with Alan, I will drop these lines from my next version.
>>
>>>> default n
>>>> ---help---
>>>> Adds an EHCI host driver for a generic platform device, which
>>>> diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c
>>>> index d8aebc0..5888abb 100644
>>>> --- a/drivers/usb/host/ehci-platform.c
>>>> +++ b/drivers/usb/host/ehci-platform.c
>>>> @@ -55,8 +55,10 @@ static int ehci_platform_reset(struct usb_hcd *hcd)
>>>>
>>>> hcd->has_tt = pdata->has_tt;
>>>> ehci->has_synopsys_hc_bug = pdata->has_synopsys_hc_bug;
>>>> - ehci->big_endian_desc = pdata->big_endian_desc;
>>>> - ehci->big_endian_mmio = pdata->big_endian_mmio;
>>>> + if (pdata->big_endian_desc)
>>>> + ehci->big_endian_desc = 1;
>>>> + if (pdata->big_endian_mmio)
>>>> + ehci->big_endian_mmio = 1;
>>>>
>>>> if (pdata->pre_setup) {
>>>> retval = pdata->pre_setup(hcd);
>>>> @@ -142,6 +144,7 @@ static int ehci_platform_probe(struct platform_device *dev)
>>>> struct resource *res_mem;
>>>> struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
>>>> struct ehci_platform_priv *priv;
>>>> + struct ehci_hcd *ehci;
>>>> int err, irq, clk = 0;
>>>>
>>>> if (usb_disabled())
>>>> @@ -177,8 +180,34 @@ static int ehci_platform_probe(struct platform_device *dev)
>>>> platform_set_drvdata(dev, hcd);
>>>> dev->dev.platform_data = pdata;
>>>> priv = hcd_to_ehci_priv(hcd);
>>>> + ehci = hcd_to_ehci(hcd);
>>>>
>>>> if (pdata == &ehci_platform_defaults && dev->dev.of_node) {
>>>> + if (of_property_read_bool(dev->dev.of_node, "big-endian-regs"))
>>>> + ehci->big_endian_mmio = 1;
>>>> +
>>>> + if (of_property_read_bool(dev->dev.of_node, "big-endian-desc"))
>>>> + ehci->big_endian_desc = 1;
>>>> +
>>>> + if (of_property_read_bool(dev->dev.of_node, "big-endian"))
>>>> + ehci->big_endian_mmio = ehci->big_endian_desc = 1;
>>>
>>> Ok, so I am confused now, should you update
>>> pdata->ehci_big_endian_{desc,mmio} here or is it valid to directly
>>> modify ehci->big_endian_{desc,mmio}, is not there any risk to undo
>>> what is done in ehci_platform_reset(), or is ehci_platform_reset()
>>> only called for non-DT cases?
>>
>> Both the pdata checks in ehci_platform_reset() and the dt checks here only
>> ever set these flags, neither code path clears them. And in the dt case pdata
>> will be NULL and vice versa.
>
> If it's safe to set ehci->big_endian_{desc,mmio} from the _probe()
> routine, then maybe the pdata sets in _reset() should be moved into here
> instead of adding extra cludges/checks into _reset().
That seems like an entire separate patch / improvement on top of adding support
for big-endian controllers to the dt code.
If people won't to improve on thus further and / or clean things up further I'm
sure the usb-subsys maintainers would welcome patches.
<snip more suggestions best done in a separate patch written by someone else>
Regards,
Hans
^ permalink raw reply
* [PATCH 2/2] ehci-platform: Add support for controllers with big-endian regs / descriptors
From: Hans de Goede @ 2014-01-22 21:02 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <52E02F84.70705@redhat.com>
Hi,
On 01/22/2014 09:52 PM, Hans de Goede wrote:
> Hi,
>
> On 01/22/2014 09:34 PM, Jonas Gorski wrote:
>> Hi,
>>
>> On Wed, 22 Jan 2014 20:28:26 +0100
>> Hans de Goede <hdegoede@redhat.com> wrote:
>>> Hi,
>>>
>>> On 01/21/2014 08:39 PM, Florian Fainelli wrote:
>>>> 2014/1/21 Hans de Goede <hdegoede@redhat.com>:
>>>>> This uses the already documented devicetree booleans for this.
>>>>
>>>> (I would greatly appreciate if you could CC people who gave you
>>>> feedback on this before)
>>>
>>> Will do.
>>>
>>>> A more informative commit message would be welcome, along with a
>>>> reference to which Device Tree binding documentation you are referring
>>>> to.
>>>
>>> I've added a reference to the bindings doc in the commit msg for my next version.
>>>
>>>>> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
>>>>> ---
>>>>> drivers/usb/host/Kconfig | 3 +++
>>>>> drivers/usb/host/ehci-platform.c | 33 +++++++++++++++++++++++++++++++--
>>>>> 2 files changed, 34 insertions(+), 2 deletions(-)
>>>>>
>>>>> diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig
>>>>> index 237d7b1..4af41f3 100644
>>>>> --- a/drivers/usb/host/Kconfig
>>>>> +++ b/drivers/usb/host/Kconfig
>>>>> @@ -256,6 +256,9 @@ config USB_EHCI_ATH79
>>>>> config USB_EHCI_HCD_PLATFORM
>>>>> tristate "Generic EHCI driver for a platform device"
>>>>> depends on !PPC_OF
>>>>> + # Support BE on architectures which have readl_be
>>>>> + select USB_EHCI_BIG_ENDIAN_DESC if (AVR32 || MIPS || MICROBLAZE || SPARC || PPC32 || PPC64)
>>>>> + select USB_EHCI_BIG_ENDIAN_MMIO if (AVR32 || MIPS || MICROBLAZE || SPARC || PPC32 || PPC64)
>>>>
>>>> I do not think this is that simple nor correct for at least Microblaze
>>>> and MIPS since they can run in either BE or LE mode, and those
>>>> specific platforms should already do the proper select at the
>>>> board/SoC level. This *might* be correct for SPARC, PPC32 and PPC64,
>>>> although I believe some specific PPC64 boards can run in little-endian
>>>> mode like the P-series, SPARC might too.
>>>>
>>>> It seems to me that you should not touch this and keep the existing
>>>> selects in place, if it turns out that the selects are missing the
>>>> error messages you added below are catching those misuses.
>>>
>>> As discussed with Alan, I will drop these lines from my next version.
>>>
>>>>> default n
>>>>> ---help---
>>>>> Adds an EHCI host driver for a generic platform device, which
>>>>> diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c
>>>>> index d8aebc0..5888abb 100644
>>>>> --- a/drivers/usb/host/ehci-platform.c
>>>>> +++ b/drivers/usb/host/ehci-platform.c
>>>>> @@ -55,8 +55,10 @@ static int ehci_platform_reset(struct usb_hcd *hcd)
>>>>>
>>>>> hcd->has_tt = pdata->has_tt;
>>>>> ehci->has_synopsys_hc_bug = pdata->has_synopsys_hc_bug;
>>>>> - ehci->big_endian_desc = pdata->big_endian_desc;
>>>>> - ehci->big_endian_mmio = pdata->big_endian_mmio;
>>>>> + if (pdata->big_endian_desc)
>>>>> + ehci->big_endian_desc = 1;
>>>>> + if (pdata->big_endian_mmio)
>>>>> + ehci->big_endian_mmio = 1;
>>>>>
>>>>> if (pdata->pre_setup) {
>>>>> retval = pdata->pre_setup(hcd);
>>>>> @@ -142,6 +144,7 @@ static int ehci_platform_probe(struct platform_device *dev)
>>>>> struct resource *res_mem;
>>>>> struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
>>>>> struct ehci_platform_priv *priv;
>>>>> + struct ehci_hcd *ehci;
>>>>> int err, irq, clk = 0;
>>>>>
>>>>> if (usb_disabled())
>>>>> @@ -177,8 +180,34 @@ static int ehci_platform_probe(struct platform_device *dev)
>>>>> platform_set_drvdata(dev, hcd);
>>>>> dev->dev.platform_data = pdata;
>>>>> priv = hcd_to_ehci_priv(hcd);
>>>>> + ehci = hcd_to_ehci(hcd);
>>>>>
>>>>> if (pdata == &ehci_platform_defaults && dev->dev.of_node) {
>>>>> + if (of_property_read_bool(dev->dev.of_node, "big-endian-regs"))
>>>>> + ehci->big_endian_mmio = 1;
>>>>> +
>>>>> + if (of_property_read_bool(dev->dev.of_node, "big-endian-desc"))
>>>>> + ehci->big_endian_desc = 1;
>>>>> +
>>>>> + if (of_property_read_bool(dev->dev.of_node, "big-endian"))
>>>>> + ehci->big_endian_mmio = ehci->big_endian_desc = 1;
>>>>
>>>> Ok, so I am confused now, should you update
>>>> pdata->ehci_big_endian_{desc,mmio} here or is it valid to directly
>>>> modify ehci->big_endian_{desc,mmio}, is not there any risk to undo
>>>> what is done in ehci_platform_reset(), or is ehci_platform_reset()
>>>> only called for non-DT cases?
>>>
>>> Both the pdata checks in ehci_platform_reset() and the dt checks here only
>>> ever set these flags, neither code path clears them. And in the dt case pdata
>>> will be NULL and vice versa.
>>
>> If it's safe to set ehci->big_endian_{desc,mmio} from the _probe()
>> routine, then maybe the pdata sets in _reset() should be moved into here
>> instead of adding extra cludges/checks into _reset().
>
> That seems like an entire separate patch / improvement on top of adding support
> for big-endian controllers to the dt code.
>
> If people won't to improve on thus further and / or clean things up further I'm
> sure the usb-subsys maintainers would welcome patches.
s/won't to improve on thus/want to improve this/
>
> <snip more suggestions best done in a separate patch written by someone else>
>
> Regards,
>
> Hans
Regards,
Hans
^ permalink raw reply
* [PATCH 1/2] ARM: omapfb: add coherent dma memory support
From: Ivaylo Dimitrov @ 2014-01-22 21:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <52D146E8.4090105@gmail.com>
Hmm, maybe this https://lkml.org/lkml/2014/1/22/386 will solve our issues
Ivo
^ permalink raw reply
* [PATCH 2/2] ehci-platform: Add support for controllers with big-endian regs / descriptors
From: Alan Stern @ 2014-01-22 21:17 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140122213434.00000f25@openwrt.org>
On Wed, 22 Jan 2014, Jonas Gorski wrote:
> If it's safe to set ehci->big_endian_{desc,mmio} from the _probe()
> routine, then maybe the pdata sets in _reset() should be moved into here
> instead of adding extra cludges/checks into _reset().
Why? What difference would it make?
Alan Stern
^ permalink raw reply
* [PATCH v3 02/24] drm/i2c: tda998x: check more I/O errors
From: Russell King - ARM Linux @ 2014-01-22 21:21 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140119195838.348f8d6d@armhf>
On Sun, Jan 19, 2014 at 07:58:38PM +0100, Jean-Francois Moine wrote:
> This patch adds more error checking inn I2C I/O functions.
> In case of I/O error, this permits to avoid writing in bad controller
> pages, a bad chipset detection or looping when getting the EDID.
>
> Tested-by: Russell King <rmk+kernel@arm.linux.org.uk>
> Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
Thanks.
Acked-by: Russell King <rmk+kernel@arm.linux.org.uk>
--
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 v3 03/24] drm/i2c: tda998x: code cleanup
From: Russell King - ARM Linux @ 2014-01-22 21:22 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140119195838.372fbbaa@armhf>
On Sun, Jan 19, 2014 at 07:58:38PM +0100, Jean-Francois Moine wrote:
> This patch:
> - replaces ARRAY_SIZE() by sizeof() when a number of bytes is needed,
> - adds a linefeed in an error message and
> - removes an useless variable setting.
>
> Tested-by: Russell King <rmk+kernel@arm.linux.org.uk>
> Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
Thanks.
Acked-by: Russell King <rmk+kernel@arm.linux.org.uk>
--
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 v3 07/24] drm/i2c: tda998x: set the video mode from the adjusted value
From: Russell King - ARM Linux @ 2014-01-22 21:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140119195840.1ecab03b@armhf>
On Sun, Jan 19, 2014 at 07:58:40PM +0100, Jean-Francois Moine wrote:
> This patch uses always the adjusted video mode instead of a mix of
> original and adjusted mode.
>
> Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
Nothing obviously wrong and appears to work, thanks.
Acked-by: Russell King <rmk+kernel@arm.linux.org.uk>
Tested-by: Russell King <rmk+kernel@arm.linux.org.uk>
--
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 v2] dma: Add Xilinx AXI Video Direct Memory Access Engine driver support
From: Levente Kurusa @ 2014-01-22 21:30 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390409565-4200-2-git-send-email-sthokal@xilinx.com>
Hello,
2014/1/22 Srikanth Thokala <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>
> ---
Another two remarks, after you fixed them ( or not :-) )
you can have my:
Reviewed-by: Levente Kurusa <levex@linux.com>
Oh, and next time please if you post a patch that fixes something I pointed out,
CC me as I had a hard time finding this patch, thanks. :-)
> 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
>
> Changes in v2:
> - Removed DMA Test client module from the patchset as suggested
> by Andy Shevchenko
> - Removed device-id DT property, as suggested by Arnd Bergmann
> - Properly documented DT bindings as suggested by Arnd Bergmann
> - Returning with error, if registration of DMA to node fails
> - Fixed typo errors
> - Used BIT() macro at applicable places
> - Added missing header file to the patchset
> - Changed copyright year to include 2014
> ---
> .../devicetree/bindings/dma/xilinx/xilinx_vdma.txt | 75 +
> drivers/dma/Kconfig | 14 +
> drivers/dma/Makefile | 1 +
> drivers/dma/xilinx/Makefile | 1 +
> drivers/dma/xilinx/xilinx_vdma.c | 1486 ++++++++++++++++++++
> include/linux/amba/xilinx_dma.h | 50 +
> 6 files changed, 1627 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma.txt
> create mode 100644 drivers/dma/xilinx/Makefile
> create mode 100644 drivers/dma/xilinx/xilinx_vdma.c
> create mode 100644 include/linux/amba/xilinx_dma.h
>
> 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..ab8be1a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/dma/xilinx/xilinx_vdma.txt
> @@ -0,0 +1,75 @@
> +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.
> +- xlnx,num-fstores: Should be the number of framebuffers as configured in h/w.
> +- dma-channel child node: Should have atleast one channel and can have upto
> + two channels per device. This node specifies the properties of each
> + DMA channel (see child node properties below).
> +
> +Optional properties:
> +- xlnx,include-sg: Tells whether configured for Scatter-mode in
> + the hardware.
> [...]
> +
> +/**
> + * 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 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);
> + } else {
> + dev_err(xdev->dev, "missing xlnx,datawidth property\n");
> + return err;
> + }
Can you please convert this to:
if (err) {
dev_err(...);
return err;
}
That way we can avoid the else clause.
> +
> + 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;
> + }
> +
> + /* 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");
It might be worth to also tell the IRQ number that failed
to register.
> + return err;
> + }
> +
> + /* Initialize the DMA channel and add it to the DMA engine channels
> + * list.
> + */
> + chan->common.device = &xdev->common;
> +
> + 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);
Error check?
> +
> + /* 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");
> + dma_async_device_unregister(&xdev->common);
> + goto error;
> + }
> +
> + 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/include/linux/amba/xilinx_dma.h b/include/linux/amba/xilinx_dma.h
> new file mode 100644
> index 0000000..48a8c8b
> --- /dev/null
> +++ b/include/linux/amba/xilinx_dma.h
> @@ -0,0 +1,50 @@
> +/*
> + * Xilinx DMA Engine drivers support header file
> + *
> + * Copyright (C) 2010-2014 Xilinx, Inc. All rights reserved.
> + *
> + * This 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.
> + */
> +
> +#ifndef __DMA_XILINX_DMA_H
> +#define __DMA_XILINX_DMA_H
> +
> +#include <linux/dma-mapping.h>
> +#include <linux/dmaengine.h>
> +
> +/**
> + * struct xilinx_vdma_config - VDMA Configuration structure
> + * @vsize: Vertical size
> + * @hsize: Horizontal size
> + * @stride: Stride
> + * @frm_dly: Frame delay
> + * @gen_lock: Whether in gen-lock mode
> + * @master: Master that it syncs to
> + * @frm_cnt_en: Enable frame count enable
> + * @park: Whether wants to park
> + * @park_frm: Frame to park on
> + * @coalesc: Interrupt coalescing threshold
> + * @delay: Delay counter
> + * @reset: Reset Channel
> + * @ext_fsync: External Frame Sync source
> + */
> +struct xilinx_vdma_config {
> + int vsize;
> + int hsize;
> + int stride;
> + int frm_dly;
> + int gen_lock;
> + int master;
> + int frm_cnt_en;
> + int park;
> + int park_frm;
> + int coalesc;
> + int delay;
> + int reset;
> + int ext_fsync;
> +};
> +
> +#endif
> --
--
Regards,
Levente Kurusa
^ permalink raw reply
* [PATCH v3 10/24] drm/i2c: tda998x: don't read write-only registers
From: Russell King - ARM Linux @ 2014-01-22 21:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140119195842.24c40e22@armhf>
On Sun, Jan 19, 2014 at 07:58:42PM +0100, Jean-Francois Moine wrote:
> This patch takes care of the write-only registers of the tda998x.
>
> The value 'MAT_CONTRL_MAT_SC(1)' in the register MAT_CONTRL has been
> set as it is at reset time.
>
> Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
> ---
> v3
> - remarks from Russell King
> - don't move the sync polarity setting after the setting of the
> register TBG_CNTRL_0 which must be the last setting of the
> init sequence
This is better, except I find that there's an additional change in this
version which wasn't in the original patch 9:
> /* must be last register set: */
> - reg_clear(priv, REG_TBG_CNTRL_0, TBG_CNTRL_0_SYNC_ONCE);
> + reg_write(priv, REG_TBG_CNTRL_0, 0);
Register changes which have a potential effect shouldn't be part of a
patch which is really only trying to avoid reading from write only
registers.
This could be a potential functional change - and it's probably one
which Rob Clark should at least be made aware of. As I commented last
time, when you're changing register values in an otherwise innocuous
patch, you should comment about them in the patch description.
--
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 v2 0/5] clk: sunxi: Add support for USB clocks and reset bits
From: Hans de Goede @ 2014-01-22 21:36 UTC (permalink / raw)
To: linux-arm-kernel
Hi Emilio, Maxime, et al,
Emilio, here is v2 of my patch-set adding support for sunxi-clk USB clocks and
reset bits. This addresses all your review comments from v1.
Can you add the first 2 patches to your queue of patches for Mike for 3.15 ?
Maxime, can you add patch 3-5 which add the dt bindings for this to your
tree please ?
Thanks & Regards,
Hans
^ permalink raw reply
* [PATCH v2 1/5] clk: sunxi: Add support for USB clock-register reset bits
From: Hans de Goede @ 2014-01-22 21:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390426587-16287-1-git-send-email-hdegoede@redhat.com>
The usb-clk register is special in that it not only contains clk gate bits,
but also has a few reset bits. This commit adds support for this by allowing
gates type sunxi clks to also register a reset controller.
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
drivers/clk/sunxi/clk-sunxi.c | 71 +++++++++++++++++++++++++++++++++++++++++++
1 file changed, 71 insertions(+)
diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
index 03bb8b8..f1a147c 100644
--- a/drivers/clk/sunxi/clk-sunxi.c
+++ b/drivers/clk/sunxi/clk-sunxi.c
@@ -18,6 +18,7 @@
#include <linux/clkdev.h>
#include <linux/of.h>
#include <linux/of_address.h>
+#include <linux/reset-controller.h>
#include "clk-factors.h"
@@ -741,6 +742,59 @@ static void __init sunxi_divider_clk_setup(struct device_node *node,
/**
+ * sunxi_gates_reset... - reset bits in leaf gate clk registers handling
+ */
+
+struct gates_reset_data {
+ void __iomem *reg;
+ spinlock_t *lock;
+ struct reset_controller_dev rcdev;
+};
+
+static int sunxi_gates_reset_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct gates_reset_data *data = container_of(rcdev,
+ struct gates_reset_data,
+ rcdev);
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(data->lock, flags);
+
+ reg = readl(data->reg);
+ writel(reg & ~BIT(id), data->reg);
+
+ spin_unlock_irqrestore(data->lock, flags);
+
+ return 0;
+}
+
+static int sunxi_gates_reset_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct gates_reset_data *data = container_of(rcdev,
+ struct gates_reset_data,
+ rcdev);
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(data->lock, flags);
+
+ reg = readl(data->reg);
+ writel(reg | BIT(id), data->reg);
+
+ spin_unlock_irqrestore(data->lock, flags);
+
+ return 0;
+}
+
+static struct reset_control_ops sunxi_gates_reset_ops = {
+ .assert = sunxi_gates_reset_assert,
+ .deassert = sunxi_gates_reset_deassert,
+};
+
+/**
* sunxi_gates_clk_setup() - Setup function for leaf gates on clocks
*/
@@ -748,6 +802,7 @@ static void __init sunxi_divider_clk_setup(struct device_node *node,
struct gates_data {
DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE);
+ u32 reset_mask;
};
static const struct gates_data sun4i_axi_gates_data __initconst = {
@@ -818,6 +873,7 @@ static void __init sunxi_gates_clk_setup(struct device_node *node,
struct gates_data *data)
{
struct clk_onecell_data *clk_data;
+ struct gates_reset_data *reset_data;
const char *clk_parent;
const char *clk_name;
void *reg;
@@ -861,6 +917,21 @@ static void __init sunxi_gates_clk_setup(struct device_node *node,
clk_data->clk_num = i;
of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+
+ /* Register a reset controler for gates with reset bits */
+ if (data->reset_mask == 0)
+ return;
+
+ reset_data = kzalloc(sizeof(*reset_data), GFP_KERNEL);
+ if (!reset_data)
+ return;
+
+ reset_data->reg = reg;
+ reset_data->lock = &clk_lock;
+ reset_data->rcdev.nr_resets = hweight32(data->reset_mask);
+ reset_data->rcdev.ops = &sunxi_gates_reset_ops;
+ reset_data->rcdev.of_node = node;
+ reset_controller_register(&reset_data->rcdev);
}
--
1.8.5.3
^ permalink raw reply related
* [PATCH v2 2/5] clk: sunxi: Add USB clock register defintions
From: Hans de Goede @ 2014-01-22 21:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390426587-16287-1-git-send-email-hdegoede@redhat.com>
From: Roman Byshko <rbyshko@gmail.com>
Add register definitions for the usb-clk register found on sun4i, sun5i and
sun7i SoCs.
Signed-off-by: Roman Byshko <rbyshko@gmail.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
Documentation/devicetree/bindings/clock/sunxi.txt | 5 +++++
drivers/clk/sunxi/clk-sunxi.c | 12 ++++++++++++
2 files changed, 17 insertions(+)
diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt
index 79c7197..8bccb6a 100644
--- a/Documentation/devicetree/bindings/clock/sunxi.txt
+++ b/Documentation/devicetree/bindings/clock/sunxi.txt
@@ -37,6 +37,8 @@ Required properties:
"allwinner,sun6i-a31-apb2-gates-clk" - for the APB2 gates on A31
"allwinner,sun4i-mod0-clk" - for the module 0 family of clocks
"allwinner,sun7i-a20-out-clk" - for the external output clocks
+ "allwinner,sun4i-usb-gates-clk" - for usb gates + resets on A10 / A20
+ "allwinner,sun5i-a13-usb-gates-clk" - for usb gates + resets on A13
Required properties for all clocks:
- reg : shall be the control register address for the clock.
@@ -49,6 +51,9 @@ Required properties for all clocks:
Additionally, "allwinner,*-gates-clk" clocks require:
- clock-output-names : the corresponding gate names that the clock controls
+And "allwinner,*-usb-gates-clk" clocks also require:
+- reset-cells : shall be set to 1
+
Clock consumers should specify the desired clocks they use with a
"clocks" phandle cell. Consumers that are using a gated clock should
provide an additional ID in their clock property. This ID is the
diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
index f1a147c..18cbc3c 100644
--- a/drivers/clk/sunxi/clk-sunxi.c
+++ b/drivers/clk/sunxi/clk-sunxi.c
@@ -813,6 +813,16 @@ static const struct gates_data sun4i_ahb_gates_data __initconst = {
.mask = {0x7F77FFF, 0x14FB3F},
};
+static const struct gates_data sun4i_usb_gates_data __initconst = {
+ .mask = {0x1C0},
+ .reset_mask = 0x07,
+};
+
+static const struct gates_data sun5i_a13_usb_gates_data __initconst = {
+ .mask = {0x140},
+ .reset_mask = 0x03,
+};
+
static const struct gates_data sun5i_a10s_ahb_gates_data __initconst = {
.mask = {0x147667e7, 0x185915},
};
@@ -1159,6 +1169,8 @@ static const struct of_device_id clk_gates_match[] __initconst = {
{.compatible = "allwinner,sun6i-a31-apb1-gates-clk", .data = &sun6i_a31_apb1_gates_data,},
{.compatible = "allwinner,sun7i-a20-apb1-gates-clk", .data = &sun7i_a20_apb1_gates_data,},
{.compatible = "allwinner,sun6i-a31-apb2-gates-clk", .data = &sun6i_a31_apb2_gates_data,},
+ {.compatible = "allwinner,sun4i-usb-gates-clk", .data = &sun4i_usb_gates_data,},
+ {.compatible = "allwinner,sun5i-a13-usb-gates-clk", .data = &sun5i_a13_usb_gates_data,},
{}
};
--
1.8.5.3
^ permalink raw reply related
* [PATCH v2 3/5] ARM: sun4i: dt: Add bindings for USB clocks
From: Hans de Goede @ 2014-01-22 21:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390426587-16287-1-git-send-email-hdegoede@redhat.com>
From: Roman Byshko <rbyshko@gmail.com>
Signed-off-by: Roman Byshko <rbyshko@gmail.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
arch/arm/boot/dts/sun4i-a10.dtsi | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/arm/boot/dts/sun4i-a10.dtsi b/arch/arm/boot/dts/sun4i-a10.dtsi
index 7264ef9..913674b 100644
--- a/arch/arm/boot/dts/sun4i-a10.dtsi
+++ b/arch/arm/boot/dts/sun4i-a10.dtsi
@@ -290,6 +290,15 @@
clock-output-names = "ir1";
};
+ usb_clk: clk at 01c200cc {
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ compatible = "allwinner,sun4i-usb-gates-clk";
+ reg = <0x01c200cc 0x4>;
+ clocks = <&pll6 1>;
+ clock-output-names = "usb_ohci0", "usb_ohci1", "usb_phy";
+ };
+
spi3_clk: clk at 01c200d4 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-mod0-clk";
--
1.8.5.3
^ permalink raw reply related
* [PATCH v2 4/5] ARM: sun5i: dt: Add bindings for USB clocks
From: Hans de Goede @ 2014-01-22 21:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390426587-16287-1-git-send-email-hdegoede@redhat.com>
From: Roman Byshko <rbyshko@gmail.com>
Signed-off-by: Roman Byshko <rbyshko@gmail.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
arch/arm/boot/dts/sun5i-a10s.dtsi | 9 +++++++++
arch/arm/boot/dts/sun5i-a13.dtsi | 9 +++++++++
2 files changed, 18 insertions(+)
diff --git a/arch/arm/boot/dts/sun5i-a10s.dtsi b/arch/arm/boot/dts/sun5i-a10s.dtsi
index 9591959..44235e7 100644
--- a/arch/arm/boot/dts/sun5i-a10s.dtsi
+++ b/arch/arm/boot/dts/sun5i-a10s.dtsi
@@ -255,6 +255,15 @@
clock-output-names = "ir0";
};
+ usb_clk: clk at 01c200cc {
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ compatible = "allwinner,sun5i-a13-usb-gates-clk";
+ reg = <0x01c200cc 0x4>;
+ clocks = <&pll6 1>;
+ clock-output-names = "usb_ohci0", "usb_phy";
+ };
+
mbus_clk: clk at 01c2015c {
#clock-cells = <0>;
compatible = "allwinner,sun4i-mod0-clk";
diff --git a/arch/arm/boot/dts/sun5i-a13.dtsi b/arch/arm/boot/dts/sun5i-a13.dtsi
index f6f2c81..4193fdf 100644
--- a/arch/arm/boot/dts/sun5i-a13.dtsi
+++ b/arch/arm/boot/dts/sun5i-a13.dtsi
@@ -252,6 +252,15 @@
clock-output-names = "ir0";
};
+ usb_clk: clk at 01c200cc {
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ compatible = "allwinner,sun5i-a13-usb-gates-clk";
+ reg = <0x01c200cc 0x4>;
+ clocks = <&pll6 1>;
+ clock-output-names = "usb_ohci0", "usb_phy";
+ };
+
mbus_clk: clk at 01c2015c {
#clock-cells = <0>;
compatible = "allwinner,sun4i-mod0-clk";
--
1.8.5.3
^ permalink raw reply related
* [PATCH v2 5/5] ARM: sun7i: dt: Add bindings for USB clocks
From: Hans de Goede @ 2014-01-22 21:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390426587-16287-1-git-send-email-hdegoede@redhat.com>
From: Roman Byshko <rbyshko@gmail.com>
Signed-off-by: Roman Byshko <rbyshko@gmail.com>
Signed-off-by: Hans de Goede <hdegoede@redhat.com>
---
arch/arm/boot/dts/sun7i-a20.dtsi | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/arm/boot/dts/sun7i-a20.dtsi b/arch/arm/boot/dts/sun7i-a20.dtsi
index 57f1fc9..a44b3b3 100644
--- a/arch/arm/boot/dts/sun7i-a20.dtsi
+++ b/arch/arm/boot/dts/sun7i-a20.dtsi
@@ -280,6 +280,15 @@
clock-output-names = "ir1";
};
+ usb_clk: clk at 01c200cc {
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ compatible = "allwinner,sun4i-usb-gates-clk";
+ reg = <0x01c200cc 0x4>;
+ clocks = <&pll6 1>;
+ clock-output-names = "usb_ohci0", "usb_ohci1", "usb_phy";
+ };
+
spi3_clk: clk at 01c200d4 {
#clock-cells = <0>;
compatible = "allwinner,sun4i-mod0-clk";
--
1.8.5.3
^ permalink raw reply related
* [PATCH v3 14/24] drm/i2c: tda998x: add DT support
From: Russell King - ARM Linux @ 2014-01-22 21:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140119195843.1491091f@armhf>
On Sun, Jan 19, 2014 at 07:58:43PM +0100, Jean-Francois Moine wrote:
> This patch adds DT support to the tda998x.
>
> Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
Acked-by: Russell King <rmk+kernel@arm.linux.org.uk>
Tested-by: Russell King <rmk+kernel@arm.linux.org.uk>
--
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 v3 13/24] drm/i2c: tda998x: fix a NULL pointer dereference
From: Russell King - ARM Linux @ 2014-01-22 21:41 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140119195843.59810a46@armhf>
On Sun, Jan 19, 2014 at 07:58:43PM +0100, Jean-Francois Moine wrote:
> This patch fixes a NULL pointer dereference when the set_config
> function has not been called (priv->params == NULL).
No, that's not what this patch is doing. Maybe you could enlighten me
how priv->params could ever be NULL when that is _not_ a pointer? That's
completely impossible as it isn't a pointer.
If you tried "priv->params = NULL" the C compiler would barf on it.
I suspect you've misunderstood the code, and this change isn't actually
necessary.
--
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
* Freescale FEC packet loss
From: Marek Vasut @ 2014-01-22 21:55 UTC (permalink / raw)
To: linux-arm-kernel
Hi guys,
I am running stock Linux 3.13 on i.MX6Q SabreLite board. The CPU is i.MX6Q TO
1.0 .
I am hitting a WARNING when I use the FEC ethernet to transfer data, thus I
started investigating this problem. TL;DR I am not able to figure this problem
out, so I am not attaching a patch :-(
Steps to reproduce:
-------------------
1) Boot stock Linux 3.13 on i.MX6Q SabreLite board
2) Plug in an SD card into one of the SD slots (I use the full-size one)
3) Plug in an USB stick into one of the USB ports (I use the upper one)
4) Plug in an ethernet cable into the board
-> Connect the other side into a gigabit-capable PC
-> Let us assume the PC has IP address 192.168.1.1/24 and
the board 192.168.1.2/24
5) Install iperf on both boards
6) Bring up the ethernet link on both ends:
$ ifconfig ...
7) On the PC, run:
$ iperf -s -i 1
8) Start producing load on the in-CPU busses. Run this on the board:
$ sha1sum /dev/mmcblk0 &
$ sha1sum /dev/sda &
9) Now let the board generate ethernet traffic:
$ iperf -c 192.168.1.1 -t 1000 -i 1
Now wait about 10 minutes and the system will produce a warning and you will
observe dips in the transmission speed. You can see the output at the end of the
email.
I observe that this happens more often when there is a severe load on the
busses, which in this case I simulate by running the sha1sum on /dev/mmcblk0 and
sha1sum /dev/sda in the background in step 8) . I can also well simulate this by
running 'sha1sum /dev/mmcblk0 & sha1sum /dev/mmcblk1 &' when I have both SD
cards populated on the MX6Q sabrelite with the same result (WARNING and dips in
speed).
There was apparently a thread about this problem already [1] where the person
used SATA to produce high bus load and had exactly the same result.
One more observation is that I managed to replicate this problem all the way
back to Linux 3.3.8 , which is one of the first kernel versions where sabrelite
was supported. I also see this one the Freescale's 3.0.35-4.1.0 .
I have trouble figuring out what this problem is all about. Can you please help
me? Thank you!
[1] http://lists.infradead.org/pipermail/linux-arm-kernel/2013-
October/202519.html
root@(none):~# iperf -c 192.168.1.1 -t 1000 -i 1
------------------------------------------------------------
Client connecting to 192.168.1.1, TCP port 5001
TCP window size: 43.8 KByte (default)
------------------------------------------------------------
[ 3] local 192.168.1.128 port 36760 connected with 192.168.1.1 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0- 1.0 sec 25.5 MBytes 214 Mbits/sec
[ 3] 1.0- 2.0 sec 28.9 MBytes 242 Mbits/sec
[ 3] 2.0- 3.0 sec 24.1 MBytes 202 Mbits/sec
[ 3] 3.0- 4.0 sec 21.1 MBytes 177 Mbits/sec
[ 3] 4.0- 5.0 sec 20.2 MBytes 170 Mbits/sec
[ 3] 5.0- 6.0 sec 20.0 MBytes 168 Mbits/sec
[ 3] 6.0- 7.0 sec 20.0 MBytes 168 Mbits/sec
[ 3] 7.0- 8.0 sec 20.1 MBytes 169 Mbits/sec
[ 3] 8.0- 9.0 sec 20.5 MBytes 172 Mbits/sec
[ 3] 9.0-10.0 sec 21.5 MBytes 180 Mbits/sec
[ 3] 10.0-11.0 sec 20.0 MBytes 168 Mbits/sec
[ 3] 11.0-12.0 sec 19.4 MBytes 163 Mbits/sec
[ 3] 12.0-13.0 sec 19.6 MBytes 165 Mbits/sec
[ 3] 13.0-14.0 sec 19.8 MBytes 166 Mbits/sec
[ 3] 14.0-15.0 sec 19.4 MBytes 163 Mbits/sec
[ 275.945247] ------------[ cut here ]------------
[ 275.949920] WARNING: CPU: 3 PID: 1155 at net/sched/sch_generic.c:264
dev_watchdog+0x280/0x2a4()
[ 275.958679] NETDEV WATCHDOG: eth3 (fec): transmit queue 0 timed out
[ 275.964985] Modules linked in:
[ 275.968096] CPU: 3 PID: 1155 Comm: sha1sum Not tainted 3.13.0 #18
[ 275.974217] Backtrace:
[ 275.976787] [<80012410>] (dump_backtrace+0x0/0x10c) from [<800125ac>]
(show_stack+0x18/0x1c)
[ 275.985271] r6:00000108 r5:00000009 r4:00000000 r3:00000000
[ 275.991050] [<80012594>] (show_stack+0x0/0x1c) from [<8060f9c8>]
(dump_stack+0x80/0x9c)
[ 275.999103] [<8060f948>] (dump_stack+0x0/0x9c) from [<8002703c>]
(warn_slowpath_common+0x6c/0x90)
[ 276.008017] r4:bef43e10 r3:bef96c00
[ 276.011663] [<80026fd0>] (warn_slowpath_common+0x0/0x90) from [<80027104>]
(warn_slowpath_fmt+0x3)
[ 276.021170] r8:bf098240 r7:bef42000 r6:bf098200 r5:bf068000 r4:00000000
[ 276.028083] [<800270cc>] (warn_slowpath_fmt+0x0/0x40) from [<804e2754>]
(dev_watchdog+0x280/0x2a4)
[ 276.037095] r3:bf068000 r2:807d2fb4
[ 276.040734] [<804e24d4>] (dev_watchdog+0x0/0x2a4) from [<80030e1c>]
(call_timer_fn+0x70/0xec)
[ 276.049313] [<80030dac>] (call_timer_fn+0x0/0xec) from [<80031a90>]
(run_timer_softirq+0x198/0x23)
[ 276.058407] r8:00200200 r7:00000000 r6:bef43ec8 r5:bf836000 r4:bf068284
[ 276.065257] [<800318f8>] (run_timer_softirq+0x0/0x230) from [<8002b480>]
(__do_softirq+0x100/0x25)
[ 276.074338] [<8002b380>] (__do_softirq+0x0/0x254) from [<8002b9a8>]
(irq_exit+0xb0/0x108)
[ 276.082570] [<8002b8f8>] (irq_exit+0x0/0x108) from [<8000f3dc>]
(handle_IRQ+0x58/0xb8)
[ 276.090528] r4:8086ccc8 r3:00000184
[ 276.094162] [<8000f384>] (handle_IRQ+0x0/0xb8) from [<80008640>]
(gic_handle_irq+0x30/0x64)
[ 276.102552] r8:5590d9fa r7:f4000100 r6:bef43fb0 r5:8086ce14 r4:f400010c
r3:000000a0
[ 276.110575] [<80008610>] (gic_handle_irq+0x0/0x64) from [<800133a0>]
(__irq_usr+0x40/0x60)
[ 276.118871] Exception stack(0xbef43fb0 to 0xbef43ff8)
[ 276.123942] 3fa0: fbe9d585 13e98d33
6ed9eba1 21e67a57
[ 276.132173] 3fc0: 170dd9fc bc58d89e 5d1b7878 c19f2bf4 5590d9fa 2577bcc4
7b2ac1ea 6cee44dd
[ 276.140398] 3fe0: cf486f09 7ea92a58 0000ba1f 0000ab72 a0000030 ffffffff
[ 276.147041] r7:c19f2bf4 r6:ffffffff r5:a0000030 r4:0000ab72
[ 276.152846] ---[ end trace 054500acb8edb763 ]---
[ 3] 15.0-16.0 sec 18.8 MBytes 157 Mbits/sec
[ 3] 16.0-17.0 sec 0.00 Bytes 0.00 bits/sec
[ 3] 17.0-18.0 sec 0.00 Bytes 0.00 bits/sec
[ 3] 18.0-19.0 sec 0.00 Bytes 0.00 bits/sec
[ 3] 19.0-20.0 sec 4.25 MBytes 35.7 Mbits/sec
[ 3] 20.0-21.0 sec 19.8 MBytes 166 Mbits/sec
[ 3] 21.0-22.0 sec 19.8 MBytes 166 Mbits/sec
[ 3] 22.0-23.0 sec 19.5 MBytes 164 Mbits/sec
[ 3] 23.0-24.0 sec 8.38 MBytes 70.3 Mbits/sec
[ 3] 24.0-25.0 sec 0.00 Bytes 0.00 bits/sec
[ 3] 25.0-26.0 sec 7.88 MBytes 66.1 Mbits/sec
[ 3] 26.0-27.0 sec 20.1 MBytes 169 Mbits/sec
[...]
[ 3] 71.0-72.0 sec 23.4 MBytes 196 Mbits/sec
[ 3] 72.0-73.0 sec 12.2 MBytes 103 Mbits/sec
[ 3] 73.0-74.0 sec 0.00 Bytes 0.00 bits/sec
[ 3] 74.0-75.0 sec 0.00 Bytes 0.00 bits/sec
[ 3] 75.0-76.0 sec 10.9 MBytes 91.2 Mbits/sec
[ 3] 76.0-77.0 sec 22.4 MBytes 188 Mbits/sec
[ 3] 77.0-78.0 sec 23.0 MBytes 193 Mbits/sec
^ permalink raw reply
* [PATCH v2 06/15] watchdog: orion: Remove unneeded BRIDGE_CAUSE clear
From: Ezequiel Garcia @ 2014-01-22 22:12 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140122205213.GW18269@obsidianresearch.com>
On Wed, Jan 22, 2014 at 01:52:13PM -0700, Jason Gunthorpe wrote:
> > Clearing BRIDGE_CAUSE will only clear all currently pending upstream
> > IRQs, of course. If WDT IRQ will be re-raised right after that in
> > BRIDGE_CAUSE depends on the actual HW implementation, i.e. we do no
> > clear the causing IRQ itself but just what it raised in BRIDGE_CAUSE.
>
> Which is why it makes no sense to clear it one time at kernel start.
>
So, it seems we need to handle irq_startup(), as you suggested.
I've just tested the attached patch, and it's working fine: the driver's
probe() fully stops the watchdog, and then request_irq() acks and
pending interrupts, through the added irq_startup().
How does it look?
diff --git a/drivers/irqchip/irq-orion.c b/drivers/irqchip/irq-orion.c
index e51d400..91a3955 100644
--- a/drivers/irqchip/irq-orion.c
+++ b/drivers/irqchip/irq-orion.c
@@ -108,6 +108,16 @@ IRQCHIP_DECLARE(orion_intc, "marvell,orion-intc", orion_irq_init);
#define ORION_BRIDGE_IRQ_CAUSE 0x00
#define ORION_BRIDGE_IRQ_MASK 0x04
+static unsigned int orion_bridge_irq_startup(struct irq_data *data)
+{
+ /* Ack pending interrupts */
+ data->chip->irq_ack(data);
+
+ /* Unmask the interrupt */
+ data->chip->irq_unmask(data);
+ return 0;
+}
+
static void orion_bridge_irq_handler(unsigned int irq, struct irq_desc *desc)
{
struct irq_domain *d = irq_get_handler_data(irq);
@@ -176,6 +186,7 @@ static int __init orion_bridge_irq_init(struct device_node *np,
gc->chip_types[0].regs.ack = ORION_BRIDGE_IRQ_CAUSE;
gc->chip_types[0].regs.mask = ORION_BRIDGE_IRQ_MASK;
+ gc->chip_types[0].chip.irq_startup = orion_bridge_irq_startup;
gc->chip_types[0].chip.irq_ack = irq_gc_ack_clr_bit;
gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit;
gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit;
--
Ezequiel Garc?a, Free Electrons
Embedded Linux, Kernel and Android Engineering
http://free-electrons.com
^ permalink raw reply related
* [BUG] FL1009: xHCI host not responding to stop endpoint command.
From: Arnaud Ebalard @ 2014-01-22 22:23 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140121211741.GA12657@xanatos>
Hi Sarah,
Sarah Sharp <sarah.a.sharp@linux.intel.com> writes:
> On Sat, Jan 18, 2014 at 10:49:17PM +0100, Arnaud Ebalard wrote:
>> Hi,
>>
>> I have added Thomas in the recipients, because I guess he may be of some
>> help debugging the issue further. Thomas, the beginning of the thread is
>> here: http://thread.gmane.org/gmane.linux.usb.general/101531
>
> ...
>
>> I started suspecting the introduction of MSI support in Marvell PCIe
>> host controller driver (FL1009 is on the PCIe bus) and compiled a
>> a 3.13.0-rc8 w/ CONFIG_PCI_MSI disabled (it was enabled in all my
>> previous tests): I did not manage to reproduce the issue with this
>> kernel. As a side note, commits 5b4deb6526bd, 31f614edb726 and
>> 627dfcc249e2 are
>>
>> ATM, I do not know if the problem is related to a bug in introduced MSI
>> support or some weird incompatibility of that functionality with the
>> FL1009 which would require some quirk in XHCI stack.
>
> We've actually had issues in the past with Fresco Logic hosts not
> supporting MSI properly, even though the PCI devices claim to have MSI
> support. So turning off CONFIG_PCI_MSI may actually mean the Fresco
> Logic host is to blame, rather than the Marvell patches. I assume MSI
> wouldn't have been turned on for the Fresco Logic host unless the parent
> PCI host controller supported it.
>
> Let's see if the Fresco Logic host is really the root cause. Please
> apply the this patch to 3.13.0-rc8 and recompile with CONFIG_PCI_MSI
> enabled:
>
> diff --git a/drivers/usb/host/xhci-pci.c b/drivers/usb/host/xhci-pci.c
> index 6c03584ac15f..74748444c040 100644
> --- a/drivers/usb/host/xhci-pci.c
> +++ b/drivers/usb/host/xhci-pci.c
> @@ -30,6 +30,7 @@
> /* Device for a quirk */
> #define PCI_VENDOR_ID_FRESCO_LOGIC 0x1b73
> #define PCI_DEVICE_ID_FRESCO_LOGIC_PDK 0x1000
> +#define PCI_DEVICE_ID_FRESCO_LOGIC_FL1009 0x1009
> #define PCI_DEVICE_ID_FRESCO_LOGIC_FL1400 0x1400
>
> #define PCI_VENDOR_ID_ETRON 0x1b6f
> @@ -63,6 +64,9 @@ static void xhci_pci_quirks(struct device *dev, struct xhci_hcd *xhci)
>
> /* Look for vendor-specific quirks */
> if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC &&
> + pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_FL1009)
> + xhci->quirks |= XHCI_BROKEN_MSI;
> + if (pdev->vendor == PCI_VENDOR_ID_FRESCO_LOGIC &&
> (pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_PDK ||
> pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_FL1400)) {
> if (pdev->device == PCI_DEVICE_ID_FRESCO_LOGIC_PDK &&
With the patch applied on top of 3.13.0 kernel recompiled w/
CONFIG_PCI_MSI enabled, I cannot reproduce the bug. I guess
you can add my:
Reported-and-tested-By: Arnaud Ebalard <arno@natisbad.org>
Since you'll have to push the patch to -stable team at least for 3.13,
I wonder if it would not make sense to extend that at least to 3.12.
and possibly 3.10 (3.2 is still widely used but I wonder if it makes
sense to go that far).
Cheers,
a+
^ permalink raw reply
* [BUG] FL1009: xHCI host not responding to stop endpoint command.
From: Jason Cooper @ 2014-01-22 22:26 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <87bnz364h0.fsf@natisbad.org>
On Wed, Jan 22, 2014 at 11:23:23PM +0100, Arnaud Ebalard wrote:
> With the patch applied on top of 3.13.0 kernel recompiled w/
> CONFIG_PCI_MSI enabled, I cannot reproduce the bug. I guess
> you can add my:
>
> Reported-and-tested-By: Arnaud Ebalard <arno@natisbad.org>
>
> Since you'll have to push the patch to -stable team at least for 3.13,
> I wonder if it would not make sense to extend that at least to 3.12.
> and possibly 3.10 (3.2 is still widely used but I wonder if it makes
> sense to go that far).
Can you pinpoint the commit that introduced the regression?
thx,
Jason.
^ permalink raw reply
* [PATCH v3 15/24] drm/i2c: tda998x: use irq for connection status and EDID read
From: Russell King - ARM Linux @ 2014-01-22 22:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140119195843.7ae9753d@armhf>
On Sun, Jan 19, 2014 at 07:58:43PM +0100, Jean-Francois Moine wrote:
> This patch adds the optional treatment of the tda998x IRQ.
>
> The interrupt function is used to know the display connection status
> without polling and to speedup reading the EDID.
>
> The interrupt number may be defined either in the DT or at encoder set
> config time for non-DT boards.
>
> Signed-off-by: Jean-Francois Moine <moinejf@free.fr>
> ---
> v3
> - remarks from Russell King
> - move the setting of the wq_edid_wait flag before asking the
> device to read the EDID
> Thanks about this bug fix, but I don't see the other problem:
> there is no reason for the read_edid_block function to be
> called more than once...
It will happen whenever DRM asks the connector for the modes. However,
since the read process is triggered each time, I think this is fine.
> - use '0' as 'no irq'
> - remove the useless delay after irq init
Some further comments...
> @@ -720,6 +787,10 @@ tda998x_encoder_set_config(struct drm_encoder *encoder, void *params)
> priv->audio_port = p->audio_cfg;
> priv->audio_format = p->audio_format;
> }
> +
> + priv->irq = p->irq;
> + if (p->irq)
> + tda_irq_init(priv);
If we're going to do it this way, this should probably release the IRQ if
there was one before re-claiming it, just in case this function gets called
more than once by some driver using it.
The alternative is, as I said before, to use the infrastructure which is
already there, namely setting the interrupt via struct i2c_client's
irq member. Yes, that doesn't satisfy Sebastian's comment about using
a GPIO, but there's no sign of GPIO usage in here at the moment anyway.
So we might as well use what's already provided.
That would then avoid the need to deal with IRQ changes etc in
tda998x_encoder_set_config().
In any case, I've tested this patch in both non-IRQ and IRQ modes, and it
appears to work - but I'd like the suggestion above before I provide any
attributations.
Thanks.
--
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 v2 06/15] watchdog: orion: Remove unneeded BRIDGE_CAUSE clear
From: Jason Gunthorpe @ 2014-01-22 22:31 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140122221237.GA30763@localhost>
On Wed, Jan 22, 2014 at 07:12:38PM -0300, Ezequiel Garcia wrote:
> On Wed, Jan 22, 2014 at 01:52:13PM -0700, Jason Gunthorpe wrote:
> > > Clearing BRIDGE_CAUSE will only clear all currently pending upstream
> > > IRQs, of course. If WDT IRQ will be re-raised right after that in
> > > BRIDGE_CAUSE depends on the actual HW implementation, i.e. we do no
> > > clear the causing IRQ itself but just what it raised in BRIDGE_CAUSE.
> >
> > Which is why it makes no sense to clear it one time at kernel start.
> >
>
> So, it seems we need to handle irq_startup(), as you suggested.
> I've just tested the attached patch, and it's working fine: the driver's
> probe() fully stops the watchdog, and then request_irq() acks and
> pending interrupts, through the added irq_startup().
>
> How does it look?
Looks sane to me.
I looked some more and there are other drivers (eg irq-metag-ext) that
take this same approach.
Sebastian:
I looked at the irq-orion driver a bit more and noticed this:
ret = irq_alloc_domain_generic_chips(domain, nrirqs, 1, np->name,
handle_level_irq, clr, 0, IRQ_GC_INIT_MASK_CACHE);
^^^^^^^^^^^^^^^^^^^^^
Shouldn't it be handle_edge_irq? Otherwise who is calling irq_ack? How
does this work at all? :)
Jason
^ permalink raw reply
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