* [PATCH 09/20] ARM64 / ACPI: Implement core functions for parsing MADT table
From: Marc Zyngier @ 2014-01-23 17:54 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389961514-13562-10-git-send-email-hanjun.guo@linaro.org>
Hi Hanjun,
On 17/01/14 12:25, Hanjun Guo wrote:
> Implement core functions for parsing MADT table to get the information
> about GIC cpu interface and GIC distributor to prepare for SMP and GIC
> initialization.
>
> Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
> ---
> arch/arm64/include/asm/acpi.h | 3 +
> drivers/acpi/plat/arm-core.c | 139 ++++++++++++++++++++++++++++++++++++++++-
> drivers/acpi/tables.c | 21 +++++++
> 3 files changed, 162 insertions(+), 1 deletion(-)
>
> diff --git a/arch/arm64/include/asm/acpi.h b/arch/arm64/include/asm/acpi.h
> index e108d9c..c335c6d 100644
> --- a/arch/arm64/include/asm/acpi.h
> +++ b/arch/arm64/include/asm/acpi.h
> @@ -83,6 +83,9 @@ void arch_fix_phys_package_id(int num, u32 slot);
> extern int (*acpi_suspend_lowlevel)(void);
> #define acpi_wakeup_address (0)
>
> +#define MAX_GIC_CPU_INTERFACE 256
I'll bite. Where on Earth is this value coming from? If that's for
GICv2, 8 is the maximum. For GICv3+, that's incredibly low, and should
be probed probed at runtime anyway.
> +#define MAX_GIC_DISTRIBUTOR 1 /* should be the same as MAX_GIC_NR */
No support for cascaded GICs?
> +
> #else /* !CONFIG_ACPI */
> #define acpi_disabled 1 /* ACPI sometimes enabled on ARM */
> #define acpi_noirq 1 /* ACPI sometimes enabled on ARM */
> diff --git a/drivers/acpi/plat/arm-core.c b/drivers/acpi/plat/arm-core.c
> index 1835b21..8ba3e6f 100644
> --- a/drivers/acpi/plat/arm-core.c
> +++ b/drivers/acpi/plat/arm-core.c
> @@ -46,6 +46,16 @@ EXPORT_SYMBOL(acpi_disabled);
> int acpi_pci_disabled; /* skip ACPI PCI scan and IRQ initialization */
> EXPORT_SYMBOL(acpi_pci_disabled);
>
> +/*
> + * Local interrupt controller address,
> + * GIC cpu interface base address on ARM/ARM64
> + */
> +static u64 acpi_lapic_addr __initdata;
If that's a GIC address, why not call it as such?
> +#define BAD_MADT_ENTRY(entry, end) ( \
> + (!entry) || (unsigned long)entry + sizeof(*entry) > end || \
> + ((struct acpi_subtable_header *)entry)->length < sizeof(*entry))
> +
> #define PREFIX "ACPI: "
Just do:
#define pr_fmt(fmt) "ACPI: " fmt
and remove all the occurrences of PREFIX.
> /* FIXME: this function should be moved to topology.c when it is ready */
> @@ -92,6 +102,115 @@ void __init __acpi_unmap_table(char *map, unsigned long size)
> return;
> }
>
> +static int __init acpi_parse_madt(struct acpi_table_header *table)
> +{
> + struct acpi_table_madt *madt = NULL;
No need to initialize this to NULL, you're doing an assignment at the
next line...
> +
> + madt = (struct acpi_table_madt *)table;
> + if (!madt) {
> + pr_warn(PREFIX "Unable to map MADT\n");
There is no mapping here, please fix the message accordingly.
> + return -ENODEV;
> + }
> +
> + if (madt->address) {
> + acpi_lapic_addr = (u64) madt->address;
So you're updating this static variable, for the distributor and each
CPU interface? /me puzzled...
> + pr_info(PREFIX "Local APIC address 0x%08x\n", madt->address);
Away with this APIC madness. GICC and GICD are the concepts we're all
familiar with here, and using the proper terminology would certainly
help reviewing these patches...
> + }
> +
> + return 0;
> +}
> +
> +/*
> + * GIC structures on ARM are somthing like Local APIC structures on x86,
> + * which means GIC cpu interfaces for GICv2/v3. Every GIC structure in
> + * MADT table represents a cpu in the system.
And what do you do when your GICv3 doesn't have a memory-mapped
interface, but only uses system registers?
> + * GIC distributor structures are somthing like IOAPIC on x86. GIC can
> + * be initialized with information in this structure.
> + *
> + * Please refer to chapter5.2.12.14/15 of ACPI 5.0
A pointer to that documentation?
> + */
> +
> +static int __init
> +acpi_parse_gic(struct acpi_subtable_header *header, const unsigned long end)
> +{
> + struct acpi_madt_generic_interrupt *processor = NULL;
> +
> + processor = (struct acpi_madt_generic_interrupt *)header;
> +
> + if (BAD_MADT_ENTRY(processor, end))
> + return -EINVAL;
> +
> + acpi_table_print_madt_entry(header);
> +
> + return 0;
> +}
> +
> +static int __init
> +acpi_parse_gic_distributor(struct acpi_subtable_header *header,
> + const unsigned long end)
> +{
> + struct acpi_madt_generic_distributor *distributor = NULL;
> +
> + distributor = (struct acpi_madt_generic_distributor *)header;
> +
> + if (BAD_MADT_ENTRY(distributor, end))
> + return -EINVAL;
> +
> + acpi_table_print_madt_entry(header);
> +
> + return 0;
> +}
> +
> +/*
> + * Parse GIC cpu interface related entries in MADT
> + * returns 0 on success, < 0 on error
> + */
> +static int __init acpi_parse_madt_gic_entries(void)
> +{
> + int count;
> +
> + /*
> + * do a partial walk of MADT to determine how many CPUs
> + * we have including disabled CPUs
> + */
> + count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_INTERRUPT,
> + acpi_parse_gic, MAX_GIC_CPU_INTERFACE);
> +
> + if (!count) {
> + pr_err(PREFIX "No GIC entries present\n");
> + return -ENODEV;
> + } else if (count < 0) {
> + pr_err(PREFIX "Error parsing GIC entry\n");
> + return count;
> + }
So you do a lot of parsing to count stuff, and then discard the number
of counted objects... You might as well check that there is at least one
valid object and stop there.
> + return 0;
> +}
> +
> +/*
> + * Parse GIC distributor related entries in MADT
> + * returns 0 on success, < 0 on error
> + */
> +static int __init acpi_parse_madt_gic_distributor_entries(void)
> +{
> + int count;
> +
> + count = acpi_table_parse_madt(ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR,
> + acpi_parse_gic_distributor, MAX_GIC_DISTRIBUTOR);
> +
> + if (!count) {
> + pr_err(PREFIX "No GIC distributor entries present\n");
> + return -ENODEV;
> + } else if (count < 0) {
> + pr_err(PREFIX "Error parsing GIC distributor entry\n");
> + return count;
> + }
> +
> + return 0;
> +}
> +
> int acpi_gsi_to_irq(u32 gsi, unsigned int *irq)
> {
> *irq = gsi_to_irq(gsi);
> @@ -141,11 +260,29 @@ static int __init acpi_parse_fadt(struct acpi_table_header *table)
>
> static void __init early_acpi_process_madt(void)
> {
> - return;
> + acpi_table_parse(ACPI_SIG_MADT, acpi_parse_madt);
> }
>
> static void __init acpi_process_madt(void)
> {
> + int error;
> +
> + if (!acpi_table_parse(ACPI_SIG_MADT, acpi_parse_madt)) {
How many times are you going to parse the same table? Surely you can
stash whatever information you need and be done with it?
> + /*
> + * Parse MADT GIC cpu interface entries
> + */
> + error = acpi_parse_madt_gic_entries();
> + if (!error) {
> + /*
> + * Parse MADT GIC distributor entries
> + */
> + acpi_parse_madt_gic_distributor_entries();
> + }
> + }
> +
> + pr_info("Using ACPI for processor (GIC) configuration information\n");
> +
> return;
> }
>
> diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c
> index d67a1fe..b3e4615 100644
> --- a/drivers/acpi/tables.c
> +++ b/drivers/acpi/tables.c
> @@ -191,6 +191,27 @@ void acpi_table_print_madt_entry(struct acpi_subtable_header *header)
> }
> break;
>
> + case ACPI_MADT_TYPE_GENERIC_INTERRUPT:
> + {
> + struct acpi_madt_generic_interrupt *p =
> + (struct acpi_madt_generic_interrupt *)header;
> + printk(KERN_INFO PREFIX
Use pr_info
> + "GIC (acpi_id[0x%04x] gic_id[0x%04x] %s)\n",
> + p->uid, p->gic_id,
> + (p->flags & ACPI_MADT_ENABLED) ? "enabled" : "disabled");
> + }
> + break;
> +
> + case ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR:
> + {
> + struct acpi_madt_generic_distributor *p =
> + (struct acpi_madt_generic_distributor *)header;
> + printk(KERN_INFO PREFIX
> + "GIC Distributor (id[0x%04x] address[0x%08llx] gsi_base[%d])\n",
> + p->gic_id, p->base_address, p->global_irq_base);
> + }
> + break;
> +
> default:
> printk(KERN_WARNING PREFIX
> "Found unsupported MADT entry (type = 0x%x)\n",
>
Most of that code seems to be repeatedly parsing and printing stuff, and
I fail to see what it actually does.
M.
--
Jazz is not dead. It just smells funny...
^ permalink raw reply
* [PATCH] ARM: OMAP4+: move errata initialization to omap4_pm_init_early
From: Kevin Hilman @ 2014-01-23 17:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <52E139C5.3090207@ti.com>
Grygorii Strashko <grygorii.strashko@ti.com> writes:
> On 01/20/2014 10:06 PM, Nishanth Menon wrote:
>> Move all OMAP4 PM errata initializations to centralized location in
>> omap4_pm_init_early. This allows for users to utilize the erratas
>> in various submodules as needed.
>>
> Tested-by: Grygorii Strashko <grygorii.strashko@ti.com>
>
> This patch fixes build failure caused by patch
> https://patchwork.kernel.org/patch/3084521/
> in case if SMP is not enabled.
So does that mean that that patch can now be applied as is?
We could sure use that fix (or equivalent) for CPUidle breakage on 4460.
Kevin
^ permalink raw reply
* [PATCH v2] dma: Add Xilinx AXI Video Direct Memory Access Engine driver support
From: Srikanth Thokala @ 2014-01-23 17:52 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390483954.7619.79.camel@smile>
Hi Andy,
On Thu, Jan 23, 2014 at 7:02 PM, Andy Shevchenko
<andriy.shevchenko@linux.intel.com> wrote:
> On Wed, 2014-01-22 at 22:22 +0530, 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.
>
> Few comments below.
Ok,
>
>>
>> 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
>>
>> 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.
>> +- xlnx,flush-fsync: Tells whether which channel to Flush on Frame sync.
>> + It takes following values:
>> + {1}, flush both channels
>> + {2}, flush mm2s channel
>> + {3}, flush s2mm channel
>> +
>> +Required child node properties:
>> +- compatible: It should be either "xlnx,axi-vdma-mm2s-channel" or
>> + "xlnx,axi-vdma-s2mm-channel".
>> +- interrupts: Should contain per channel VDMA interrupts.
>> +- xlnx,data-width: Should contain the stream data width, take values
>> + {32,64...1024}.
>> +
>> +Option child node properties:
>> +- xlnx,include-dre: Tells whether hardware is configured for Data
>> + Realignment Engine.
>> +- xlnx,genlock-mode: Tells whether Genlock synchronization 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,num-fstores = <0x8>;
>> + xlnx,flush-fsync = <0x1>;
>> + dma-channel at 40030000 {
>> + compatible = "xlnx,axi-vdma-mm2s-channel";
>> + interrupts = < 0 54 4 >;
>> + xlnx,datawidth = <0x40>;
>> + } ;
>> + dma-channel at 40030030 {
>> + compatible = "xlnx,axi-vdma-s2mm-channel";
>> + interrupts = < 0 53 4 >;
>> + xlnx,datawidth = <0x40>;
>> + } ;
>> +} ;
>> +
>> +
>> +* 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
>> +
>> +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/drivers/dma/Kconfig b/drivers/dma/Kconfig
>> index c823daa..2a74651 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
>>
>> 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..3c4e9f2
>> --- /dev/null
>> +++ b/drivers/dma/xilinx/Makefile
>> @@ -0,0 +1 @@
>> +obj-$(CONFIG_XILINX_VDMA) += xilinx_vdma.o
>> diff --git a/drivers/dma/xilinx/xilinx_vdma.c b/drivers/dma/xilinx/xilinx_vdma.c
>> new file mode 100644
>> index 0000000..4c0d04c
>> --- /dev/null
>> +++ b/drivers/dma/xilinx/xilinx_vdma.c
>> @@ -0,0 +1,1486 @@
>> +/*
>> + * DMA driver for Xilinx Video DMA Engine
>> + *
>> + * Copyright (C) 2010-2014 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 BIT(14)
>> +#define XILINX_VDMA_DMACR_DLY_CNT_IRQ BIT(13)
>> +#define XILINX_VDMA_DMACR_FRM_CNT_IRQ BIT(12)
>> +#define XILINX_VDMA_DMACR_MASTER_SHIFT 8
>> +#define XILINX_VDMA_DMACR_FSYNCSRC_SHIFT 5
>> +#define XILINX_VDMA_DMACR_FRAMECNT_EN BIT(4)
>> +#define XILINX_VDMA_DMACR_GENLOCK_EN BIT(3)
>> +#define XILINX_VDMA_DMACR_RESET BIT(2)
>> +#define XILINX_VDMA_DMACR_CIRC_EN BIT(1)
>> +#define XILINX_VDMA_DMACR_RUNSTOP BIT(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 BIT(15)
>> +#define XILINX_VDMA_DMASR_ERR_IRQ BIT(14)
>> +#define XILINX_VDMA_DMASR_DLY_CNT_IRQ BIT(13)
>> +#define XILINX_VDMA_DMASR_FRM_CNT_IRQ BIT(12)
>> +#define XILINX_VDMA_DMASR_SOF_LATE_ERR BIT(11)
>> +#define XILINX_VDMA_DMASR_SG_DEC_ERR BIT(10)
>> +#define XILINX_VDMA_DMASR_SG_SLV_ERR BIT(9)
>> +#define XILINX_VDMA_DMASR_EOF_EARLY_ERR BIT(8)
>> +#define XILINX_VDMA_DMASR_SOF_EARLY_ERR BIT(7)
>> +#define XILINX_VDMA_DMASR_DMA_DEC_ERR BIT(6)
>> +#define XILINX_VDMA_DMASR_DMA_SLAVE_ERR BIT(5)
>> +#define XILINX_VDMA_DMASR_DMA_INT_ERR BIT(4)
>> +#define XILINX_VDMA_DMASR_IDLE BIT(1)
>> +#define XILINX_VDMA_DMASR_HALTED BIT(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)
>
> Does 0xff require to be a separate definition in both above cases?
Ok, will reuse DELAY/FRAME_MAX masks.
>
>> +
>> +#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 */
>
> HW or Hardware
Ok.
>
>> +#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 | \
>
> Do you need so many tabs for an indentation here and in other places?
> May be better to keep some style here (I mean on which line you start
> the value of the definition).
Ok.
>
>> + 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
>> + * @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;
>> + 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
>
> May be return 0 on success as it usual practice? Here and in the other
> places as well.
Ok.
>
>> + */
>> +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.
>> + */
>
> Keep style of multiline comment the same over the code.
Sure.
>
>> + 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;
>> + }
>> +
>> + 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");
>> + 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);
>> +
>> + /* 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);
>
> You could move this assignment to a variables block, it's normal
> practice.
Ok. I will send v3 fixing the comments.
Srikanth
>
>> + 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
>
> --
> Andy Shevchenko <andriy.shevchenko@linux.intel.com>
> Intel Finland Oy
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
^ permalink raw reply
* [PATCH 11/20] ARM64 / ACPI: Get the enable method for SMP initialization
From: Catalin Marinas @ 2014-01-23 17:50 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389961514-13562-12-git-send-email-hanjun.guo@linaro.org>
On Fri, Jan 17, 2014 at 12:25:05PM +0000, Hanjun Guo wrote:
> --- a/arch/arm64/kernel/smp.c
> +++ b/arch/arm64/kernel/smp.c
> @@ -48,6 +48,7 @@
> #include <asm/sections.h>
> #include <asm/tlbflush.h>
> #include <asm/ptrace.h>
> +#include <asm/acpi.h>
>
> /*
> * as from 2.5, kernels no longer have an init_tasks structure
> @@ -280,7 +281,7 @@ static void (*smp_cross_call)(const struct cpumask *, unsigned int);
> * cpu logical map array containing MPIDR values related to logical
> * cpus. Assumes that cpu_logical_map(0) has already been initialized.
> */
> -void __init smp_init_cpus(void)
> +static int __init of_smp_init_cpus(void)
> {
> struct device_node *dn = NULL;
> unsigned int i, cpu = 1;
> @@ -364,6 +365,10 @@ next:
> cpu++;
> }
>
> + /* No device tree or no CPU node in DT */
> + if (cpu == 1 && !bootcpu_valid)
> + return -ENODEV;
> +
> /* sanity check */
> if (cpu > NR_CPUS)
> pr_warning("no. of cores (%d) greater than configured maximum of %d - clipping\n",
> @@ -371,7 +376,7 @@ next:
>
> if (!bootcpu_valid) {
> pr_err("DT missing boot CPU MPIDR, not enabling secondaries\n");
> - return;
> + return -EINVAL;
> }
>
> /*
> @@ -381,6 +386,39 @@ next:
> for (i = 0; i < NR_CPUS; i++)
> if (cpu_logical_map(i) != INVALID_HWID)
> set_cpu_possible(i, true);
> +
> + return 0;
> +}
> +
> +/*
> + * In ACPI mode, the cpu possible map was enumerated before SMP
> + * initialization when MADT table was parsed, so we can get the
> + * possible map here to initialize CPUs.
> + */
> +static void __init acpi_smp_init_cpus(void)
> +{
> + int cpu;
> + const char *enable_method;
> +
> + for_each_possible_cpu(cpu) {
> + enable_method = acpi_get_enable_method(cpu);
> + if (!enable_method)
> + continue;
> +
> + cpu_ops[cpu] = cpu_get_ops(enable_method);
> + if (!cpu_ops[cpu])
> + continue;
> +
> + cpu_ops[cpu]->cpu_init(NULL, cpu);
> + }
> +}
> +
> +void __init smp_init_cpus(void)
> +{
> + if (!of_smp_init_cpus())
> + return;
> +
> + acpi_smp_init_cpus();
> }
With DT we initialise the cpu_ops[0] via cpu_read_bootcpu_ops() called
from setup_arch(). This is needed because with PSCI we use cpu_ops for
power management even if it's a UP system. Do you get a some kernel
warning about device node for the boot cpu not found?
> --- a/drivers/acpi/plat/arm-core.c
> +++ b/drivers/acpi/plat/arm-core.c
> @@ -367,6 +367,32 @@ static void __init acpi_process_madt(void)
> }
>
> /*
> + * To see PCSI is enabled or not.
> + *
> + * PSCI is not available for ACPI 5.0, return FALSE for now.
> + *
> + * FIXME: should we introduce early_param("psci", func) for test purpose?
> + */
> +static bool acpi_psci_smp_available(int cpu)
> +{
> + return FALSE;
> +}
> +
> +static const char *enable_method[] = {
> + "psci",
> + "spin-table",
> + NULL
> +};
> +
> +const char *acpi_get_enable_method(int cpu)
> +{
> + if (acpi_psci_smp_available(cpu))
> + return enable_method[0];
> + else
> + return enable_method[1];
> +}
You could just use literal strings here, actually even ignoring PSCI
until available.
--
Catalin
^ permalink raw reply
* [PATCH 1/2] PWM: let of_xlate handlers check args count
From: Russell King - ARM Linux @ 2014-01-23 17:36 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140123165349.GY15937@n2100.arm.linux.org.uk>
On Thu, Jan 23, 2014 at 04:53:50PM +0000, Russell King - ARM Linux wrote:
> On Thu, Jan 23, 2014 at 12:04:44PM +0100, Sascha Hauer wrote:
> > On Thu, Jan 23, 2014 at 11:56:32AM +0100, Lothar Wa?mann wrote:
> > > Hi,
> > >
> > > Sascha Hauer wrote:
> > > > of_pwm_n_cells for the of_xlate handler is stored in struct pwm_chip,
> > > > but it is only ever used by the of_xlate handler itsel. Remove
> > > > of_pwm_n_cells from struct pwm_chip and let the handler do the argument
> > > > count checking to simplify the code.
> > > >
> > > This still does not make the PWM_POLARITY flag in the pwms node
> > > optional as was the goal because of_parse_phandle_with_args() requires
> > > at least #pwm-cells arguments in the node.
> > >
> > > So, with a DT configuration like:
> > > pwm0: pwm at 0 {
> > > #pwm-cells = <3>;
> > > };
> > > backlight {
> > > pwms = <&pwm0 0 100000>;
> > > };
> >
> > We misunderstood each other. My goal was to allow the driver to also
> > work with old devicetrees which specify #pwm-cells = <2>, not to allow
> > inconsistent devicetrees like the snippet above.
>
> In which case, the patch I've posted seems to do that job too... I'm
> just about to test out the three-cell version.
Okay, this works, but there's a problem with pwm-leds.
When the duty cycle is set to zero (when you set the brightness to zero)
pwm-leds decides to disable the PWM after configuring it. This causes
the PWM output to be driven low, causing the LED to go to maximum
brightness.
So, using the inversion at PWM level doesn't work.
To make this work correctly, we really need pwm-leds to do the inversion
rather than setting the inversion bit in hardware.
--
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: Srikanth Thokala @ 2014-01-23 17:35 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAAsK9AFGM+852N-F98p32JSHEpg5ZKv-0wyzFGx9675GBp7gQg@mail.gmail.com>
Hi Levente,
On Thu, Jan 23, 2014 at 3:00 AM, Levente Kurusa <levex@linux.com> wrote:
> 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. :-)
Sure. 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);
>> +}
>> +
[...]
>> + /* 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.
Ok. I will fix it in v3.
>> +
>> + 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.
Ok.
>
>> + 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);
[...]
>> + 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?
Sure, with a warning message as it is optional DT property. I will
fix it in v3.
Srikanth
[...]
>> --
>
> --
> Regards,
> Levente Kurusa
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at http://www.tux.org/lkml/
^ permalink raw reply
* [PATCH 0/2] Enable clock controllers on MSM
From: Kevin Hilman @ 2014-01-23 17:25 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389921904-3777-1-git-send-email-sboyd@codeaurora.org>
Stephen Boyd <sboyd@codeaurora.org> writes:
> These patches add the clock controller nodes, enable the clock drivers
> on MSM based platforms, and hook it up enough to get the serial console
> working. This is based on the merge of Mike's clk-next branch with
> linux-next-20140116. The changes need the clk-next branch because that's
> where the DTS include files landed.
I forgot to repond to this earlier, but I tested this on top of -next
and it gets the dragonboard booting w/mainline. Yay!
> Perhaps this can be applied after 3.14-rc1 is out?
Yeah, sounds good.
Kevin
> Stephen Boyd (2):
> ARM: dts: msm: Add clock controller nodes and hook into uart
> ARM: msm_defconfig: Enable MSM clock drivers
>
> arch/arm/boot/dts/qcom-msm8660-surf.dts | 11 +++++++++++
> arch/arm/boot/dts/qcom-msm8960-cdp.dts | 18 ++++++++++++++++++
> arch/arm/boot/dts/qcom-msm8974.dtsi | 24 ++++++++++++++++++++++++
> arch/arm/configs/msm_defconfig | 4 ++++
> 4 files changed, 57 insertions(+)
^ permalink raw reply
* [GIT PULL] bcm dt updates for 3.14
From: Kevin Hilman @ 2014-01-23 17:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAEPRUYrYNR=63oS91Bq-viYJwXfjtY4+cchY0hMUpaJQAw+NNA@mail.gmail.com>
Christian Daudt <bcm@fixthebug.org> writes:
> The following changes since commit 413541dd66d51f791a0b169d9b9014e4f56be13c:
>
> Linux 3.13-rc5 (2013-12-22 13:08:32 -0800)
>
> are available in the git repository at:
>
> git://github.com/broadcom/bcm11351.git tags/bcm-for-3.14-dt
>
> for you to fetch changes up to 30d831156b3a7f249c08cbe457a0d728160db39b:
>
> clk: bcm281xx: define kona clock binding (2013-12-31 09:08:38 -0800)
>
> ----------------------------------------------------------------
> Add i2c, usb and clock DT configuration to bcm mobile.
>
> ----------------------------------------------------------------
Pulled into late/dt.
Kevin
^ permalink raw reply
* [GIT PULL] bcm driver updates for 3.14
From: Kevin Hilman @ 2014-01-23 17:04 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAEPRUYpgHhrVCoRAzF_WbQZGTpwSahQq8G40+9=pf36-n9V_hg@mail.gmail.com>
Christian Daudt <bcm@fixthebug.org> writes:
> The following changes since commit 319e2e3f63c348a9b66db4667efa73178e18b17d:
>
> Linux 3.13-rc4 (2013-12-15 12:31:33 -0800)
>
> are available in the git repository at:
>
> git://github.com/broadcom/bcm11351.git tags/bcm-for-3.14-drivers
>
> for you to fetch changes up to e257c5f6e1cad35d15c06d713b2d1a26f883bbcb:
>
> clk: bcm281xx: define U32_MAX conditionally for now (2013-12-31
> 09:54:45 -0800)
>
> ----------------------------------------------------------------
> Clk driver for bcm mobile SoCs
>
> ----------------------------------------------------------------
Pulled into late/soc.
Kevin
^ permalink raw reply
* [PATCH 1/2] PWM: let of_xlate handlers check args count
From: Russell King - ARM Linux @ 2014-01-23 16:53 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140123110444.GI16215@pengutronix.de>
On Thu, Jan 23, 2014 at 12:04:44PM +0100, Sascha Hauer wrote:
> On Thu, Jan 23, 2014 at 11:56:32AM +0100, Lothar Wa?mann wrote:
> > Hi,
> >
> > Sascha Hauer wrote:
> > > of_pwm_n_cells for the of_xlate handler is stored in struct pwm_chip,
> > > but it is only ever used by the of_xlate handler itsel. Remove
> > > of_pwm_n_cells from struct pwm_chip and let the handler do the argument
> > > count checking to simplify the code.
> > >
> > This still does not make the PWM_POLARITY flag in the pwms node
> > optional as was the goal because of_parse_phandle_with_args() requires
> > at least #pwm-cells arguments in the node.
> >
> > So, with a DT configuration like:
> > pwm0: pwm at 0 {
> > #pwm-cells = <3>;
> > };
> > backlight {
> > pwms = <&pwm0 0 100000>;
> > };
>
> We misunderstood each other. My goal was to allow the driver to also
> work with old devicetrees which specify #pwm-cells = <2>, not to allow
> inconsistent devicetrees like the snippet above.
In which case, the patch I've posted seems to do that job too... I'm
just about to test out the three-cell version.
--
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
* [PATCHv2 2/2] pwm: imx: support polarity inversion
From: Russell King - ARM Linux @ 2014-01-23 16:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20140123115203.GV15937@n2100.arm.linux.org.uk>
On Thu, Jan 23, 2014 at 11:52:03AM +0000, Russell King - ARM Linux wrote:
> On Thu, Jan 23, 2014 at 08:37:14AM +0100, Lothar Wa?mann wrote:
> > This wouldn't buy much without a material change to of_pwm_get().
> > The function of_parse_phandle_with_args() called by of_pwm_get()
> > requires the number of args in the pwms property be greater or equal to
> > the #pwm-cells property in the pwm node. Thus, the interesting case of
> > having #pwm-cells = <3> without changing the existing users is
> > prohibited by of_parse_phandle_with_args().
>
> I really don't think that's a problem we need to be concerned with at
> the moment. What we need is for the kernel to be able to parse files
> with #pwm-cells = <2> with the pwms property containing two arguments,
> and when they're updated to #pwm-cells = <3> with the pwms property
> containing three arguments.
>
> Yes, that means all the board dt files need to be updated at the same
> time to include the additional argument, but I don't see that as a big
> problem.
>
> What we do need to do is to adjust the PWM parsing code such that it's
> possible to use either specification without causing any side effects.
>
> I would test this, but as u-boot is rather fscked at the moment and the
> networking has broken on my cubox-i as a result... and it seems that the
> u-boot developers have pissed off cubox-i u-boot hackers soo much that
> they've dropped u-boot in favour of barebox...
Okay, finally confirmed that this works with #pwm-cells = 2.
--
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 07/20] ARM64 / ACPI: Enable ARM64 in Kconfig
From: Catalin Marinas @ 2014-01-23 16:39 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389961514-13562-8-git-send-email-hanjun.guo@linaro.org>
On Fri, Jan 17, 2014 at 12:25:01PM +0000, Hanjun Guo wrote:
> Add Kconfigs to build ACPI on ARM64, and make ACPI runable on ARM64.
>
> acpi_idle driver is x86/IA64 dependent now, so make CONFIG_ACPI_PROCESSOR
> depends on X86 || IA64, and implement it on ARM/ARM64 in the furture.
>
> In order to make arm-core.c can both run on ARM and ARM64, introduce
> CONFIG_ACPI_ARM to support it.
>
> Signed-off-by: Graeme Gregory <graeme.gregory@linaro.org>
> Signed-off-by: Al Stone <al.stone@linaro.org>
> Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
> ---
> arch/arm64/Kconfig | 2 ++
> drivers/acpi/Kconfig | 11 ++++++++---
> drivers/acpi/plat/Makefile | 2 +-
> 3 files changed, 11 insertions(+), 4 deletions(-)
>
> diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig
> index 6d4dd22..2b1fb1d 100644
> --- a/arch/arm64/Kconfig
> +++ b/arch/arm64/Kconfig
> @@ -279,6 +279,8 @@ source "net/Kconfig"
>
> source "drivers/Kconfig"
>
> +source "drivers/acpi/Kconfig"
> +
> source "fs/Kconfig"
>
> source "arch/arm64/kvm/Kconfig"
> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
> index 4770de5..cae5dc9 100644
> --- a/drivers/acpi/Kconfig
> +++ b/drivers/acpi/Kconfig
> @@ -2,13 +2,16 @@
> # ACPI Configuration
> #
>
> +config ACPI_ARM
> + bool
Could be better as def_bool ARM64
> +
> menuconfig ACPI
> bool "ACPI (Advanced Configuration and Power Interface) Support"
> depends on !IA64_HP_SIM
> - depends on IA64 || X86
> - depends on PCI
> + depends on ((IA64 || X86) && PCI) || ARM64
> select PNP
> - default y
> + select ACPI_ARM if ARM64
And remove this select here.
--
Catalin
^ permalink raw reply
* [PATCH 03/20] ARM64 / ACPI: Introduce the skeleton of _PDC related for ARM64
From: Catalin Marinas @ 2014-01-23 16:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <52DCEA66.90903@linaro.org>
On Mon, Jan 20, 2014 at 09:20:38AM +0000, Hanjun Guo wrote:
> On 2014-1-17 22:25, Sudeep Holla wrote:
> > On 17/01/14 12:24, Hanjun Guo wrote:
> >> --- a/arch/arm64/kernel/process.c
> >> +++ b/arch/arm64/kernel/process.c
> >> @@ -89,6 +89,9 @@ void arch_cpu_idle_prepare(void)
> >> local_fiq_enable();
> >> }
> >>
> >> +unsigned long boot_option_idle_override = IDLE_NO_OVERRIDE;
> >> +EXPORT_SYMBOL(boot_option_idle_override);
> >> +
> >
> > This is what I mentioned in other email. Do we really foresee use of this in
> > ARM64 or it's just added to avoid build issues ?
>
> Just avoid build issues, can not foresee use of this in ARM64 :)
So ideally we should look for a better solution here.
--
Catalin
^ permalink raw reply
* [PATCH 02/20] ARM64 : Add dummy asm/cpu.h
From: Catalin Marinas @ 2014-01-23 16:15 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389961514-13562-3-git-send-email-hanjun.guo@linaro.org>
On Fri, Jan 17, 2014 at 12:24:56PM +0000, Hanjun Guo wrote:
> ACPI requires a cpu.h, add a dummy one copied from arm. This will need
> updated or replaced as ACPI based cpu hotplug or cpu topology for armv8
> is worked out.
>
> Signed-off-by: Graeme Gregory <graeme.gregory@linaro.org>
> Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
> ---
> arch/arm64/include/asm/cpu.h | 25 +++++++++++++++++++++++++
> 1 file changed, 25 insertions(+)
> create mode 100644 arch/arm64/include/asm/cpu.h
>
> diff --git a/arch/arm64/include/asm/cpu.h b/arch/arm64/include/asm/cpu.h
> new file mode 100644
> index 0000000..8625eb1
> --- /dev/null
> +++ b/arch/arm64/include/asm/cpu.h
> @@ -0,0 +1,25 @@
> +/*
> + * Copyright (C) 2004-2005 ARM Ltd.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +#ifndef __ASM_ARM_CPU_H
> +#define __ASM_ARM_CPU_H
> +
> +#include <linux/percpu.h>
> +#include <linux/cpu.h>
> +#include <linux/topology.h>
> +
> +struct cpuinfo_arm {
> + struct cpu cpu;
> + u64 cpuid;
> +#ifdef CONFIG_SMP
> + unsigned int loops_per_jiffy;
> +#endif
> +};
> +
> +DECLARE_PER_CPU(struct cpuinfo_arm, cpu_data);
> +
> +#endif
Could you not leave this file empty until you add the code that actually
makes use of cpu_data?
--
Catalin
^ permalink raw reply
* [RESEND][RFC PATCH 2/2] arm: Get rid of meminfo
From: Grygorii Strashko @ 2014-01-23 16:09 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1389813057-26572-3-git-send-email-lauraa@codeaurora.org>
Hi Laura,
On 01/15/2014 09:10 PM, Laura Abbott wrote:
> memblock is now fully integrated into the kernel and is the prefered
> method for tracking memory. Rather than reinvent the wheel with
> meminfo, migrate to using memblock directly instead of meminfo as
> an intermediate.
>
> TODO: fix early_mem, get rid of NR_BANKS?
There are few comments below.
>
> Signed-off-by: Laura Abbott <lauraa@codeaurora.org>
> ---
> arch/arm/include/asm/mach/arch.h | 4 +-
> arch/arm/include/asm/memblock.h | 3 +-
> arch/arm/include/asm/setup.h | 23 ------
> arch/arm/kernel/atags_parse.c | 5 +-
> arch/arm/kernel/setup.c | 33 +++------
> arch/arm/mach-clps711x/board-clep7312.c | 7 +-
> arch/arm/mach-clps711x/board-edb7211.c | 10 +--
> arch/arm/mach-clps711x/board-p720t.c | 2 +-
> arch/arm/mach-footbridge/cats-hw.c | 2 +-
> arch/arm/mach-footbridge/netwinder-hw.c | 2 +-
> arch/arm/mach-msm/board-halibut.c | 6 --
> arch/arm/mach-msm/board-mahimahi.c | 13 +--
> arch/arm/mach-msm/board-msm7x30.c | 3 +-
> arch/arm/mach-msm/board-sapphire.c | 13 +--
> arch/arm/mach-msm/board-trout.c | 8 +-
> arch/arm/mach-orion5x/common.c | 3 +-
> arch/arm/mach-orion5x/common.h | 3 +-
> arch/arm/mach-pxa/cm-x300.c | 3 +-
> arch/arm/mach-pxa/corgi.c | 10 +--
> arch/arm/mach-pxa/eseries.c | 9 +-
> arch/arm/mach-pxa/poodle.c | 8 +-
> arch/arm/mach-pxa/spitz.c | 9 +--
> arch/arm/mach-pxa/tosa.c | 8 +-
> arch/arm/mach-realview/core.c | 11 +--
> arch/arm/mach-realview/core.h | 3 +-
> arch/arm/mach-realview/realview_pb1176.c | 8 +-
> arch/arm/mach-realview/realview_pbx.c | 17 ++---
> arch/arm/mach-s3c24xx/mach-smdk2413.c | 8 +-
> arch/arm/mach-s3c24xx/mach-vstms.c | 8 +-
> arch/arm/mach-sa1100/assabet.c | 2 +-
> arch/arm/mm/init.c | 61 ++++++----------
> arch/arm/mm/mmu.c | 122 ++++++++++--------------------
> 32 files changed, 143 insertions(+), 284 deletions(-)
>
> diff --git a/arch/arm/include/asm/mach/arch.h b/arch/arm/include/asm/mach/arch.h
> index 17a3fa2..c43473a 100644
[...]
>
> @@ -692,6 +685,12 @@ int __init arm_add_memory(u64 start, u64 size)
> * Pick out the memory size. We look for mem=size at start,
> * where start and size are "size[KkMm]"
> */
> +
> +/*
> + * XXX this is busted when just using memblock. Need to add memblock
> + * hook to reset.
> + */
> +
> static int __init early_mem(char *p)
> {
> static int usermem __initdata = 0;
> @@ -706,7 +705,6 @@ static int __init early_mem(char *p)
> */
> if (usermem == 0) {
> usermem = 1;
> - meminfo.nr_banks = 0;
> }
The below code might work here:
memblock_remove(memblock_start_of_DRAM(),
memblock_end_of_DRAM() - memblock_start_of_DRAM());
>
> start = PHYS_OFFSET;
> @@ -851,13 +849,6 @@ static void __init reserve_crashkernel(void)
> static inline void reserve_crashkernel(void) {}
> #endif /* CONFIG_KEXEC */
>
[...]
> diff --git a/arch/arm/mm/init.c b/arch/arm/mm/init.c
> index e0e3968..c6ea491 100644
> --- a/arch/arm/mm/init.c
> +++ b/arch/arm/mm/init.c
> @@ -81,24 +81,21 @@ __tagtable(ATAG_INITRD2, parse_tag_initrd2);
> * initialization functions, as well as show_mem() for the skipping
> * of holes in the memory map. It is populated by arm_add_memory().
> */
> -struct meminfo meminfo;
> -
> void show_mem(unsigned int filter)
> {
> int free = 0, total = 0, reserved = 0;
> - int shared = 0, cached = 0, slab = 0, i;
> - struct meminfo * mi = &meminfo;
> + int shared = 0, cached = 0, slab = 0;
> + struct memblock_region *reg;
>
> printk("Mem-info:\n");
> show_free_areas(filter);
>
> - for_each_bank (i, mi) {
> - struct membank *bank = &mi->bank[i];
> + for_each_memblock (memory, reg) {
> unsigned int pfn1, pfn2;
> struct page *page, *end;
>
> - pfn1 = bank_pfn_start(bank);
> - pfn2 = bank_pfn_end(bank);
> + pfn1 = memblock_region_memory_base_pfn(reg);
> + pfn2 = memblock_region_memory_end_pfn(reg);
>
> page = pfn_to_page(pfn1);
> end = pfn_to_page(pfn2 - 1) + 1;
> @@ -130,16 +127,9 @@ void show_mem(unsigned int filter)
> static void __init find_limits(unsigned long *min, unsigned long *max_low,
> unsigned long *max_high)
> {
> - struct meminfo *mi = &meminfo;
> - int i;
> -
> - /* This assumes the meminfo array is properly sorted */
> - *min = bank_pfn_start(&mi->bank[0]);
> - for_each_bank (i, mi)
> - if (mi->bank[i].highmem)
> - break;
> - *max_low = bank_pfn_end(&mi->bank[i - 1]);
> - *max_high = bank_pfn_end(&mi->bank[mi->nr_banks - 1]);
> + *max_low = PFN_DOWN(memblock_get_current_limit());
> + *min = PFN_UP(memblock_start_of_DRAM());
> + *max_high = PFN_DOWN(memblock_end_of_DRAM());
Just to notify. Above values may have different values after your
change, because of usage arm_memblock_steal(). Is it ok?
> }
>
> static void __init arm_bootmem_init(unsigned long start_pfn,
> @@ -327,14 +317,8 @@ phys_addr_t __init arm_memblock_steal(phys_addr_t size, phys_addr_t align)
> return phys;
> }
>
> -void __init arm_memblock_init(struct meminfo *mi,
> - const struct machine_desc *mdesc)
> +void __init arm_memblock_init(const struct machine_desc *mdesc)
> {
> - int i;
> -
> - for (i = 0; i < mi->nr_banks; i++)
> - memblock_add(mi->bank[i].start, mi->bank[i].size);
> -
> /* Register the kernel text, kernel data and initrd with memblock. */
> #ifdef CONFIG_XIP_KERNEL
> memblock_reserve(__pa(_sdata), _end - _sdata);
> @@ -466,48 +450,47 @@ free_memmap(unsigned long start_pfn, unsigned long end_pfn)
> /*
> * The mem_map array can get very big. Free the unused area of the memory map.
> */
> -static void __init free_unused_memmap(struct meminfo *mi)
> +static void __init free_unused_memmap(void)
> {
> - unsigned long bank_start, prev_bank_end = 0;
> - unsigned int i;
> + unsigned long start, prev_end = 0;
> + struct memblock_region *reg;
>
> /*
> * This relies on each bank being in address order.
> * The banks are sorted previously in bootmem_init().
> */
> - for_each_bank(i, mi) {
> - struct membank *bank = &mi->bank[i];
> -
> - bank_start = bank_pfn_start(bank);
> + for_each_memblock(memory, reg) {
> + start = __phys_to_pfn(reg->base);
>
> #ifdef CONFIG_SPARSEMEM
> /*
> * Take care not to free memmap entries that don't exist
> * due to SPARSEMEM sections which aren't present.
> */
> - bank_start = min(bank_start,
> - ALIGN(prev_bank_end, PAGES_PER_SECTION));
> + start = min(start,
> + ALIGN(prev_end, PAGES_PER_SECTION));
> #else
> /*
> * Align down here since the VM subsystem insists that the
> * memmap entries are valid from the bank start aligned to
> * MAX_ORDER_NR_PAGES.
> */
> - bank_start = round_down(bank_start, MAX_ORDER_NR_PAGES);
> + start = round_down(start, MAX_ORDER_NR_PAGES);
> #endif
> /*
> * If we had a previous bank, and there is a space
> * between the current bank and the previous, free it.
> */
> - if (prev_bank_end && prev_bank_end < bank_start)
> - free_memmap(prev_bank_end, bank_start);
> + if (prev_end && prev_end < start)
> + free_memmap(prev_end, start);
>
> /*
> * Align up here since the VM subsystem insists that the
> * memmap entries are valid from the bank end aligned to
> * MAX_ORDER_NR_PAGES.
> */
> - prev_bank_end = ALIGN(bank_pfn_end(bank), MAX_ORDER_NR_PAGES);
> + prev_end = ALIGN(start + __phys_to_pfn(reg->size),
> + MAX_ORDER_NR_PAGES);
> }
>
> #ifdef CONFIG_SPARSEMEM
> @@ -589,7 +572,7 @@ void __init mem_init(void)
> max_mapnr = pfn_to_page(max_pfn + PHYS_PFN_OFFSET) - mem_map;
>
> /* this will put all unused low memory onto the freelists */
> - free_unused_memmap(&meminfo);
> + free_unused_memmap();
> free_all_bootmem();
>
> #ifdef CONFIG_SA1111
> diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c
> index 4f08c13..394701c 100644
> --- a/arch/arm/mm/mmu.c
> +++ b/arch/arm/mm/mmu.c
> @@ -1043,77 +1043,54 @@ early_param("vmalloc", early_vmalloc);
>
> phys_addr_t arm_lowmem_limit __initdata = 0;
>
> +static void remove_memblock(phys_addr_t base, phys_addr_t size)
> +{
> + memblock_reserve(base, size);
> + memblock_free(base, size);
> + memblock_remove(base, size);
> +}
I think it'll be ok to use just memblock_remove(base, size); below.
> +
> void __init sanity_check_meminfo(void)
> {
> phys_addr_t memblock_limit = 0;
> - int i, j, highmem = 0;
> + int highmem = 0;
> phys_addr_t vmalloc_limit = __pa(vmalloc_min - 1) + 1;
> + struct memblock_region *reg;
>
> - for (i = 0, j = 0; i < meminfo.nr_banks; i++) {
> - struct membank *bank = &meminfo.bank[j];
> - phys_addr_t size_limit;
> -
> - *bank = meminfo.bank[i];
> - size_limit = bank->size;
> + for_each_memblock(memory, reg) {
> + phys_addr_t block_start = reg->base;
> + phys_addr_t block_end = reg->base + reg->size;
> + phys_addr_t size_limit = reg->size;
>
> - if (bank->start >= vmalloc_limit)
> + if (reg->base >= vmalloc_limit)
> highmem = 1;
> else
> - size_limit = vmalloc_limit - bank->start;
> + size_limit = vmalloc_limit - reg->base;
>
> - bank->highmem = highmem;
>
> -#ifdef CONFIG_HIGHMEM
> - /*
> - * Split those memory banks which are partially overlapping
> - * the vmalloc area greatly simplifying things later.
> - */
> - if (!highmem && bank->size > size_limit) {
> - if (meminfo.nr_banks >= NR_BANKS) {
> - printk(KERN_CRIT "NR_BANKS too low, "
> - "ignoring high memory\n");
> - } else {
> - memmove(bank + 1, bank,
> - (meminfo.nr_banks - i) * sizeof(*bank));
> - meminfo.nr_banks++;
> - i++;
> - bank[1].size -= size_limit;
> - bank[1].start = vmalloc_limit;
> - bank[1].highmem = highmem = 1;
> - j++;
> + if (!IS_ENABLED(CONFIG_HIGHMEM) || cache_is_vipt_aliasing()) {
> +
> + if (highmem) {
> + pr_notice("Ignoring ram at %pa-%pa (!CONFIG_HIGHMEM)\n",
> + &block_start, &block_end);
> + remove_memblock(block_start, block_end);
> + continue;
> }
> - bank->size = size_limit;
> - }
> -#else
> - /*
> - * Highmem banks not allowed with !CONFIG_HIGHMEM.
> - */
> - if (highmem) {
> - printk(KERN_NOTICE "Ignoring RAM at %.8llx-%.8llx "
> - "(!CONFIG_HIGHMEM).\n",
> - (unsigned long long)bank->start,
> - (unsigned long long)bank->start + bank->size - 1);
> - continue;
> - }
>
> - /*
> - * Check whether this memory bank would partially overlap
> - * the vmalloc area.
> - */
> - if (bank->size > size_limit) {
> - printk(KERN_NOTICE "Truncating RAM at %.8llx-%.8llx "
> - "to -%.8llx (vmalloc region overlap).\n",
> - (unsigned long long)bank->start,
> - (unsigned long long)bank->start + bank->size - 1,
> - (unsigned long long)bank->start + size_limit - 1);
> - bank->size = size_limit;
> + if (reg->size > size_limit) {
> + phys_addr_t overlap_size = reg->size - size_limit;
> +
> + pr_notice("Truncating RAM at %pa-%pa to -%pa",
> + &block_start, &block_end, &overlap_size);
> + remove_memblock(vmalloc_limit, overlap_size);
> + block_end = vmalloc_limit;
> + }
> }
> -#endif
> - if (!bank->highmem) {
> - phys_addr_t bank_end = bank->start + bank->size;
>
> - if (bank_end > arm_lowmem_limit)
> - arm_lowmem_limit = bank_end;
> + if (!highmem) {
> + if (block_end > arm_lowmem_limit)
> + arm_lowmem_limit = reg->base + size_limit;
> +
>
> /*
> * Find the first non-section-aligned page, and point
> @@ -1129,35 +1106,16 @@ void __init sanity_check_meminfo(void)
> * occurs before any free memory is mapped.
> */
> if (!memblock_limit) {
> - if (!IS_ALIGNED(bank->start, SECTION_SIZE))
> - memblock_limit = bank->start;
> - else if (!IS_ALIGNED(bank_end, SECTION_SIZE))
> - memblock_limit = bank_end;
> + if (!IS_ALIGNED(block_start, SECTION_SIZE))
> + memblock_limit = block_start;
> + else if (!IS_ALIGNED(block_end, SECTION_SIZE))
> + memblock_limit = block_end;
> }
> - }
> - j++;
> - }
> -#ifdef CONFIG_HIGHMEM
> - if (highmem) {
> - const char *reason = NULL;
>
> - if (cache_is_vipt_aliasing()) {
> - /*
> - * Interactions between kmap and other mappings
> - * make highmem support with aliasing VIPT caches
> - * rather difficult.
> - */
> - reason = "with VIPT aliasing cache";
> - }
> - if (reason) {
> - printk(KERN_CRIT "HIGHMEM is not supported %s, ignoring high memory\n",
> - reason);
> - while (j > 0 && meminfo.bank[j - 1].highmem)
> - j--;
> }
> +
> }
> -#endif
> - meminfo.nr_banks = j;
> +
> high_memory = __va(arm_lowmem_limit - 1) + 1;
>
> /*
>
Regards,
- grygorii
^ permalink raw reply
* [PATCH v3 15/15] at91: dt: at91sam9261ek: Enabled the USB device port
From: Jean-Jacques Hiblot @ 2014-01-23 15:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390492639-7299-1-git-send-email-jjhiblot@traphandler.com>
Signed-off-by: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
---
arch/arm/boot/dts/at91sam9261ek.dts | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/boot/dts/at91sam9261ek.dts b/arch/arm/boot/dts/at91sam9261ek.dts
index 8bdbed9..36cf677 100644
--- a/arch/arm/boot/dts/at91sam9261ek.dts
+++ b/arch/arm/boot/dts/at91sam9261ek.dts
@@ -106,6 +106,11 @@
};
apb {
+ usb1: gadget at fffa4000 {
+ atmel,vbus-gpio = <&pioB 29 GPIO_ACTIVE_HIGH>;
+ status = "okay";
+ };
+
dbgu: serial at fffff200 {
status = "okay";
};
--
1.8.5.2
^ permalink raw reply related
* [PATCH v3 14/15] at91: dt: at91sam9261ek: Enabled the USB host port (OHCI)
From: Jean-Jacques Hiblot @ 2014-01-23 15:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390492639-7299-1-git-send-email-jjhiblot@traphandler.com>
Signed-off-by: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
---
arch/arm/boot/dts/at91sam9261ek.dts | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/arch/arm/boot/dts/at91sam9261ek.dts b/arch/arm/boot/dts/at91sam9261ek.dts
index 287b283..8bdbed9 100644
--- a/arch/arm/boot/dts/at91sam9261ek.dts
+++ b/arch/arm/boot/dts/at91sam9261ek.dts
@@ -32,6 +32,10 @@
};
ahb {
+ usb0: ohci at 00500000 {
+ status = "okay";
+ };
+
fb0: fb at 0x00600000 {
display = <&display0>;
atmel,power-control-gpio = <&pioA 12 GPIO_ACTIVE_LOW>;
--
1.8.5.2
^ permalink raw reply related
* [PATCH v3 13/15] at91: dt: sam9261: CCF: Added USB clocks
From: Jean-Jacques Hiblot @ 2014-01-23 15:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390492639-7299-1-git-send-email-jjhiblot@traphandler.com>
Signed-off-by: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
---
arch/arm/boot/dts/at91sam9261.dtsi | 33 +++++++++++++++++++++++++++++++++
1 file changed, 33 insertions(+)
diff --git a/arch/arm/boot/dts/at91sam9261.dtsi b/arch/arm/boot/dts/at91sam9261.dtsi
index 112e587..2a87c19 100644
--- a/arch/arm/boot/dts/at91sam9261.dtsi
+++ b/arch/arm/boot/dts/at91sam9261.dtsi
@@ -54,6 +54,8 @@
compatible = "atmel,at91rm9200-ohci", "usb-ohci";
reg = <0x00500000 0x100000>;
interrupts = <20 IRQ_TYPE_LEVEL_HIGH 2>;
+ clocks = <&usb>, <&ohci_clk>, <&hclk0>, <&uhpck>;
+ clock-names = "usb_clk", "ohci_clk", "hclk", "uhpck";
status = "disabled";
};
@@ -107,6 +109,8 @@
compatible = "atmel,at91rm9200-udc";
reg = <0xfffa4000 0x4000>;
interrupts = <10 IRQ_TYPE_LEVEL_HIGH 2>;
+ clocks = <&usb>, <&udc_clk>, <&udpck>;
+ clock-names = "usb_clk", "udc_clk", "udpck";
status = "disabled";
};
@@ -563,11 +567,30 @@
atmel,clk-divisors = <1 2 4 3>;
};
+ usb: usbck {
+ compatible = "atmel,at91rm9200-clk-usb";
+ #clock-cells = <0>;
+ atmel,clk-divisors = <1 2 4 3>;
+ clocks = <&pllb>;
+ };
+
systemck {
compatible = "atmel,at91rm9200-clk-system";
#address-cells = <1>;
#size-cells = <0>;
+ uhpck: uhpck {
+ #clock-cells = <0>;
+ reg = <6>;
+ clocks = <&usb>;
+ };
+
+ udpck: udpck {
+ #clock-cells = <0>;
+ reg = <7>;
+ clocks = <&usb>;
+ };
+
hclk0: hclk0 {
#clock-cells = <0>;
reg = <16>;
@@ -623,6 +646,11 @@
reg = <9>;
};
+ udc_clk: udc_clk {
+ #clock-cells = <0>;
+ reg = <10>;
+ };
+
twi0_clk: twi0_clk {
reg = <11>;
#clock-cells = <0>;
@@ -643,6 +671,11 @@
reg = <17>;
};
+ ohci_clk: ohci_clk {
+ #clock-cells = <0>;
+ reg = <20>;
+ };
+
lcd_clk: lcd_clk {
#clock-cells = <0>;
reg = <21>;
--
1.8.5.2
^ permalink raw reply related
* [PATCH v3 12/15] at91: dt: sam9261: adds description for the bus matrix
From: Jean-Jacques Hiblot @ 2014-01-23 15:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390492639-7299-1-git-send-email-jjhiblot@traphandler.com>
Signed-off-by: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
---
arch/arm/boot/dts/at91sam9261.dtsi | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/arch/arm/boot/dts/at91sam9261.dtsi b/arch/arm/boot/dts/at91sam9261.dtsi
index 79d7554..112e587 100644
--- a/arch/arm/boot/dts/at91sam9261.dtsi
+++ b/arch/arm/boot/dts/at91sam9261.dtsi
@@ -220,6 +220,11 @@
reg = <0xffffea00 0x200>;
};
+ matrix: matrix at ffffee00 {
+ compatible = "atmel,at91sam9261-bus-matrix";
+ reg = <0xffffee00 0x200>;
+ };
+
aic: interrupt-controller at fffff000 {
#interrupt-cells = <3>;
compatible = "atmel,at91rm9200-aic";
--
1.8.5.2
^ permalink raw reply related
* [PATCH v3 11/15] at91: dt: Adds support for the bus matrix declaration in the device tree
From: Jean-Jacques Hiblot @ 2014-01-23 15:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390492639-7299-1-git-send-email-jjhiblot@traphandler.com>
Signed-off-by: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
---
arch/arm/mach-at91/setup.c | 23 +++++++++++++++++++++++
1 file changed, 23 insertions(+)
diff --git a/arch/arm/mach-at91/setup.c b/arch/arm/mach-at91/setup.c
index 7d3f7cc..f61638d 100644
--- a/arch/arm/mach-at91/setup.c
+++ b/arch/arm/mach-at91/setup.c
@@ -483,6 +483,28 @@ end:
of_node_put(np);
}
+static struct of_device_id matrix_ids[] = {
+ { .compatible = "atmel,at91sam9261-bus-matrix", },
+ { /*sentinel*/ }
+};
+
+static void at91_dt_matrix(void)
+{
+ struct device_node *np;
+
+ np = of_find_matching_node(NULL, matrix_ids);
+ if (!np) {
+ pr_debug("AT91: unable to find compatible bus matrix controller node in dtb\n");
+ return;
+ }
+
+ at91_matrix_base = of_iomap(np, 0);
+ if (!at91_matrix_base)
+ panic("Impossible to ioremap at91_matrix_base\n");
+
+ of_node_put(np);
+}
+
void __init at91rm9200_dt_initialize(void)
{
at91_dt_ramc();
@@ -502,6 +524,7 @@ void __init at91_dt_initialize(void)
at91_dt_rstc();
at91_dt_ramc();
at91_dt_shdwc();
+ at91_dt_matrix();
/* Init clock subsystem */
at91_dt_clock_init();
--
1.8.5.2
^ permalink raw reply related
* [PATCH v3 10/15] at91: dt: at91sam9261ek: Added support for the LCD display
From: Jean-Jacques Hiblot @ 2014-01-23 15:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390492639-7299-1-git-send-email-jjhiblot@traphandler.com>
Signed-off-by: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
---
arch/arm/boot/dts/at91sam9261ek.dts | 31 +++++++++++++++++++++++++++++++
1 file changed, 31 insertions(+)
diff --git a/arch/arm/boot/dts/at91sam9261ek.dts b/arch/arm/boot/dts/at91sam9261ek.dts
index 5555e9f5..287b283 100644
--- a/arch/arm/boot/dts/at91sam9261ek.dts
+++ b/arch/arm/boot/dts/at91sam9261ek.dts
@@ -32,6 +32,37 @@
};
ahb {
+ fb0: fb at 0x00600000 {
+ display = <&display0>;
+ atmel,power-control-gpio = <&pioA 12 GPIO_ACTIVE_LOW>;
+ status = "okay";
+
+ display0: display {
+ bits-per-pixel = <16>;
+ atmel,lcdcon-backlight;
+ atmel,dmacon = <0x1>;
+ atmel,lcdcon2 = <0x80008002>;
+ atmel,guard-time = <1>;
+ atmel,lcd-wiring-mode = "BRG";
+
+ display-timings {
+ native-mode = <&timing0>;
+ timing0: timing0 {
+ clock-frequency = <4965000>;
+ hactive = <240>;
+ vactive = <320>;
+ hback-porch = <1>;
+ hfront-porch = <33>;
+ vback-porch = <1>;
+ vfront-porch = <0>;
+ hsync-len = <5>;
+ vsync-len = <1>;
+ hsync-active = <1>;
+ vsync-active = <1>;
+ };
+ };
+ };
+ };
nand0: nand at 40000000 {
nand-bus-width = <8>;
--
1.8.5.2
^ permalink raw reply related
* [PATCH v3 09/15] at91: dt: sam9261: Added support for the LCD display
From: Jean-Jacques Hiblot @ 2014-01-23 15:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390492639-7299-1-git-send-email-jjhiblot@traphandler.com>
Signed-off-by: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
---
arch/arm/boot/dts/at91sam9261.dtsi | 39 ++++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/arch/arm/boot/dts/at91sam9261.dtsi b/arch/arm/boot/dts/at91sam9261.dtsi
index 97e6b62..79d7554 100644
--- a/arch/arm/boot/dts/at91sam9261.dtsi
+++ b/arch/arm/boot/dts/at91sam9261.dtsi
@@ -57,6 +57,17 @@
status = "disabled";
};
+ fb0: fb at 0x00600000 {
+ compatible = "atmel,at91sam9261-lcdc";
+ reg = <0x00600000 0x1000>;
+ interrupts = <21 IRQ_TYPE_LEVEL_HIGH 3>;
+ pinctrl-names = "default";
+ pinctrl-0 = <&pinctrl_fb>;
+ clocks = <&lcd_clk>, <&hclk1>;
+ clock-names = "lcdc_clk", "hclk";
+ status = "disabled";
+ };
+
nand0: nand at 40000000 {
compatible = "atmel,at91rm9200-nand";
#address-cells = <1>;
@@ -432,6 +443,34 @@
};
};
+ fb {
+ pinctrl_fb: fb-0 {
+ atmel,pins =
+ <AT91_PIOB 1 AT91_PERIPH_A AT91_PINCTRL_NONE
+ AT91_PIOB 2 AT91_PERIPH_A AT91_PINCTRL_NONE
+ AT91_PIOB 3 AT91_PERIPH_A AT91_PINCTRL_NONE
+ AT91_PIOB 7 AT91_PERIPH_A AT91_PINCTRL_NONE
+ AT91_PIOB 8 AT91_PERIPH_A AT91_PINCTRL_NONE
+ AT91_PIOB 9 AT91_PERIPH_A AT91_PINCTRL_NONE
+ AT91_PIOB 10 AT91_PERIPH_A AT91_PINCTRL_NONE
+ AT91_PIOB 11 AT91_PERIPH_A AT91_PINCTRL_NONE
+ AT91_PIOB 12 AT91_PERIPH_A AT91_PINCTRL_NONE
+ AT91_PIOB 15 AT91_PERIPH_A AT91_PINCTRL_NONE
+ AT91_PIOB 16 AT91_PERIPH_A AT91_PINCTRL_NONE
+ AT91_PIOB 17 AT91_PERIPH_A AT91_PINCTRL_NONE
+ AT91_PIOB 18 AT91_PERIPH_A AT91_PINCTRL_NONE
+ AT91_PIOB 19 AT91_PERIPH_A AT91_PINCTRL_NONE
+ AT91_PIOB 20 AT91_PERIPH_A AT91_PINCTRL_NONE
+ AT91_PIOB 23 AT91_PERIPH_B AT91_PINCTRL_NONE
+ AT91_PIOB 24 AT91_PERIPH_B AT91_PINCTRL_NONE
+ AT91_PIOB 25 AT91_PERIPH_B AT91_PINCTRL_NONE
+ AT91_PIOB 26 AT91_PERIPH_B AT91_PINCTRL_NONE
+ AT91_PIOB 27 AT91_PERIPH_B AT91_PINCTRL_NONE
+ AT91_PIOB 28 AT91_PERIPH_B AT91_PINCTRL_NONE
+ >;
+ };
+ };
+
pioA: gpio at fffff400 {
compatible = "atmel,at91rm9200-gpio";
reg = <0xfffff400 0x200>;
--
1.8.5.2
^ permalink raw reply related
* [PATCH v3 08/15] at91: dt: sam9261: Added hclk declaration for the fb driver (non-CCF)
From: Jean-Jacques Hiblot @ 2014-01-23 15:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390492639-7299-1-git-send-email-jjhiblot@traphandler.com>
When CCF is not used the clocks are not described in the DTS. This patch adds
a clock required for the fb driver
Signed-off-by: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
---
arch/arm/mach-at91/at91sam9261.c | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/mach-at91/at91sam9261.c b/arch/arm/mach-at91/at91sam9261.c
index 2c0e940..f17268f 100644
--- a/arch/arm/mach-at91/at91sam9261.c
+++ b/arch/arm/mach-at91/at91sam9261.c
@@ -199,6 +199,7 @@ static struct clk_lookup periph_clocks_lookups[] = {
/* more tc lookup table for DT entries */
CLKDEV_CON_DEV_ID("t0_clk", "fffa0000.timer", &tc0_clk),
CLKDEV_CON_DEV_ID("hclk", "500000.ohci", &hck0),
+ CLKDEV_CON_DEV_ID("hclk", "600000.fb", &hck1),
CLKDEV_CON_DEV_ID("spi_clk", "fffc8000.spi", &spi0_clk),
CLKDEV_CON_DEV_ID("spi_clk", "fffcc000.spi", &spi1_clk),
CLKDEV_CON_DEV_ID("mci_clk", "fffa8000.mmc", &mmc_clk),
--
1.8.5.2
^ permalink raw reply related
* [PATCH v3 07/15] at91: dt: sam9261: Added the descriptions of hck0 and hck1 clocks (CCF)
From: Jean-Jacques Hiblot @ 2014-01-23 15:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390492639-7299-1-git-send-email-jjhiblot@traphandler.com>
Signed-off-by: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
---
arch/arm/boot/dts/at91sam9261.dtsi | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/arch/arm/boot/dts/at91sam9261.dtsi b/arch/arm/boot/dts/at91sam9261.dtsi
index 2730611..97e6b62 100644
--- a/arch/arm/boot/dts/at91sam9261.dtsi
+++ b/arch/arm/boot/dts/at91sam9261.dtsi
@@ -518,6 +518,26 @@
atmel,clk-output-range = <0 94000000>;
atmel,clk-divisors = <1 2 4 3>;
};
+
+ systemck {
+ compatible = "atmel,at91rm9200-clk-system";
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ hclk0: hclk0 {
+ #clock-cells = <0>;
+ reg = <16>;
+ clocks = <&mck>;
+ };
+
+ hclk1: hclk1 {
+ #clock-cells = <0>;
+ reg = <17>;
+ clocks = <&mck>;
+ };
+ };
+
+
periphck {
compatible = "atmel,at91rm9200-clk-peripheral";
#address-cells = <1>;
--
1.8.5.2
^ permalink raw reply related
* [PATCH v3 06/15] at91: dt: at91sam9261ek: Adds DT entries for the 4 user buttons
From: Jean-Jacques Hiblot @ 2014-01-23 15:57 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1390492639-7299-1-git-send-email-jjhiblot@traphandler.com>
Signed-off-by: Jean-Jacques Hiblot <jjhiblot@traphandler.com>
---
arch/arm/boot/dts/at91sam9261ek.dts | 39 +++++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)
diff --git a/arch/arm/boot/dts/at91sam9261ek.dts b/arch/arm/boot/dts/at91sam9261ek.dts
index 8909217..5555e9f5 100644
--- a/arch/arm/boot/dts/at91sam9261ek.dts
+++ b/arch/arm/boot/dts/at91sam9261ek.dts
@@ -83,6 +83,15 @@
AT91_PIOA 23 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>;
};
};
+
+ keys {
+ pinctrl_keys: keys-0 {
+ atmel,pins = <AT91_PIOA 27 AT91_PERIPH_GPIO AT91_PINCTRL_NONE
+ AT91_PIOA 26 AT91_PERIPH_GPIO AT91_PINCTRL_NONE
+ AT91_PIOA 25 AT91_PERIPH_GPIO AT91_PINCTRL_NONE
+ AT91_PIOA 24 AT91_PERIPH_GPIO AT91_PINCTRL_NONE>;
+ };
+ };
};
watchdog at fffffd40 {
@@ -109,4 +118,34 @@
linux,default-trigger = "heartbeat";
};
};
+
+ gpio_keys {
+ compatible = "gpio-keys";
+ pinctrl-0 = <&pinctrl_keys>;
+
+ button_0 {
+ label = "button_0";
+ gpios = <&pioA 27 GPIO_ACTIVE_LOW>;
+ linux,code = <256>;
+ gpio-key,wakeup;
+ };
+ button_1 {
+ label = "button_1";
+ gpios = <&pioA 26 GPIO_ACTIVE_LOW>;
+ linux,code = <257>;
+ gpio-key,wakeup;
+ };
+ button_2 {
+ label = "button_2";
+ gpios = <&pioA 25 GPIO_ACTIVE_LOW>;
+ linux,code = <258>;
+ gpio-key,wakeup;
+ };
+ button_3 {
+ label = "button_3";
+ gpios = <&pioA 24 GPIO_ACTIVE_LOW>;
+ linux,code = <259>;
+ gpio-key,wakeup;
+ };
+ };
};
--
1.8.5.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