summaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2013-05-07 11:22:14 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2013-05-07 11:22:14 -0700
commit1bf25e78af317e6d5d9b5594dfeb0036e0d589d6 (patch)
tree49dbd2d7bd6856b8ae1864c2dd0c0eb5e36d5398 /drivers
parent38f56f33ca381751f9b8910f67e7a805ec0b68cb (diff)
parent0592c2189ece16f3e0c5a56245634aba93d0c9dd (diff)
downloadblackbird-op-linux-1bf25e78af317e6d5d9b5594dfeb0036e0d589d6.tar.gz
blackbird-op-linux-1bf25e78af317e6d5d9b5594dfeb0036e0d589d6.zip
Merge tag 'cleanup-for-linus-2' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc
Pull ARM SoC late cleanups from Arnd Bergmann: "These are cleanups and smaller changes that either depend on earlier feature branches or came in late during the development cycle. We normally try to get all cleanups early, so these are the exceptions: - A follow-up on the clocksource reworks, hopefully the last time we need to merge clocksource subsystem changes through arm-soc. A first set of patches was part of the original 3.10 arm-soc cleanup series because of interdependencies with timer drivers now moved out of arch/arm. - Migrating the SPEAr13xx platform away from using auxdata for DMA channel descriptions towards using information in device tree, based on the earlier SPEAr multiplatform series - A few follow-ups on the Atmel SAMA5 support and other changes for Atmel at91 based on the larger at91 reworks. - Moving the armada irqchip implementation to drivers/irqchip - Several OMAP cleanups following up on the larger series already merged in 3.10." * tag 'cleanup-for-linus-2' of git://git.kernel.org/pub/scm/linux/kernel/git/arm/arm-soc: (50 commits) ARM: OMAP4: change the device names in usb_bind_phy ARM: OMAP2+: Fix mismerge for timer.c between ff931c82 and da4a686a ARM: SPEAr: conditionalize SMP code ARM: arch_timer: Silence debug preempt warnings ARM: OMAP: remove unused variable serial: amba-pl011: fix !CONFIG_DMA_ENGINE case ata: arasan: remove the need for platform_data ARM: at91/sama5d34ek.dts: remove not needed compatibility string ARM: at91: dts: add MCI DMA support ARM: at91: dts: add i2c dma support ARM: at91: dts: set #dma-cells to the correct value ARM: at91: suspend both memory controllers on at91sam9263 irqchip: armada-370-xp: slightly cleanup irq controller driver irqchip: armada-370-xp: move IRQ handler to avoid forward declaration irqchip: move IRQ driver for Armada 370/XP ARM: mvebu: move L2 cache initialization in init_early() devtree: add binding documentation for sp804 ARM: integrator-cp: convert use CLKSRC_OF for timer init ARM: versatile: use OF init for sp804 timer ARM: versatile: add versatile dtbs to dtbs target ...
Diffstat (limited to 'drivers')
-rw-r--r--drivers/ata/pata_arasan_cf.c37
-rw-r--r--drivers/clocksource/Kconfig1
-rw-r--r--drivers/clocksource/arm_arch_timer.c33
-rw-r--r--drivers/clocksource/exynos_mct.c1
-rw-r--r--drivers/irqchip/Makefile1
-rw-r--r--drivers/irqchip/irq-armada-370-xp.c288
-rw-r--r--drivers/spi/spi-pl022.c43
-rw-r--r--drivers/tty/serial/amba-pl011.c64
8 files changed, 403 insertions, 65 deletions
diff --git a/drivers/ata/pata_arasan_cf.c b/drivers/ata/pata_arasan_cf.c
index 405022d302c3..7638121cb5d1 100644
--- a/drivers/ata/pata_arasan_cf.c
+++ b/drivers/ata/pata_arasan_cf.c
@@ -209,8 +209,6 @@ struct arasan_cf_dev {
struct dma_chan *dma_chan;
/* Mask for DMA transfers */
dma_cap_mask_t mask;
- /* dma channel private data */
- void *dma_priv;
/* DMA transfer work */
struct work_struct work;
/* DMA delayed finish work */
@@ -308,6 +306,7 @@ static void cf_card_detect(struct arasan_cf_dev *acdev, bool hotplugged)
static int cf_init(struct arasan_cf_dev *acdev)
{
struct arasan_cf_pdata *pdata = dev_get_platdata(acdev->host->dev);
+ unsigned int if_clk;
unsigned long flags;
int ret = 0;
@@ -325,8 +324,12 @@ static int cf_init(struct arasan_cf_dev *acdev)
spin_lock_irqsave(&acdev->host->lock, flags);
/* configure CF interface clock */
- writel((pdata->cf_if_clk <= CF_IF_CLK_200M) ? pdata->cf_if_clk :
- CF_IF_CLK_166M, acdev->vbase + CLK_CFG);
+ /* TODO: read from device tree */
+ if_clk = CF_IF_CLK_166M;
+ if (pdata && pdata->cf_if_clk <= CF_IF_CLK_200M)
+ if_clk = pdata->cf_if_clk;
+
+ writel(if_clk, acdev->vbase + CLK_CFG);
writel(TRUE_IDE_MODE | CFHOST_ENB, acdev->vbase + OP_MODE);
cf_interrupt_enable(acdev, CARD_DETECT_IRQ, 1);
@@ -357,12 +360,6 @@ static void dma_callback(void *dev)
complete(&acdev->dma_completion);
}
-static bool filter(struct dma_chan *chan, void *slave)
-{
- chan->private = slave;
- return true;
-}
-
static inline void dma_complete(struct arasan_cf_dev *acdev)
{
struct ata_queued_cmd *qc = acdev->qc;
@@ -530,8 +527,7 @@ static void data_xfer(struct work_struct *work)
/* request dma channels */
/* dma_request_channel may sleep, so calling from process context */
- acdev->dma_chan = dma_request_channel(acdev->mask, filter,
- acdev->dma_priv);
+ acdev->dma_chan = dma_request_slave_channel(acdev->host->dev, "data");
if (!acdev->dma_chan) {
dev_err(acdev->host->dev, "Unable to get dma_chan\n");
goto chan_request_fail;
@@ -798,6 +794,7 @@ static int arasan_cf_probe(struct platform_device *pdev)
struct ata_host *host;
struct ata_port *ap;
struct resource *res;
+ u32 quirk;
irq_handler_t irq_handler = NULL;
int ret = 0;
@@ -817,12 +814,17 @@ static int arasan_cf_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ if (pdata)
+ quirk = pdata->quirk;
+ else
+ quirk = CF_BROKEN_UDMA; /* as it is on spear1340 */
+
/* if irq is 0, support only PIO */
acdev->irq = platform_get_irq(pdev, 0);
if (acdev->irq)
irq_handler = arasan_cf_interrupt;
else
- pdata->quirk |= CF_BROKEN_MWDMA | CF_BROKEN_UDMA;
+ quirk |= CF_BROKEN_MWDMA | CF_BROKEN_UDMA;
acdev->pbase = res->start;
acdev->vbase = devm_ioremap_nocache(&pdev->dev, res->start,
@@ -859,17 +861,16 @@ static int arasan_cf_probe(struct platform_device *pdev)
INIT_WORK(&acdev->work, data_xfer);
INIT_DELAYED_WORK(&acdev->dwork, delayed_finish);
dma_cap_set(DMA_MEMCPY, acdev->mask);
- acdev->dma_priv = pdata->dma_priv;
/* Handle platform specific quirks */
- if (pdata->quirk) {
- if (pdata->quirk & CF_BROKEN_PIO) {
+ if (quirk) {
+ if (quirk & CF_BROKEN_PIO) {
ap->ops->set_piomode = NULL;
ap->pio_mask = 0;
}
- if (pdata->quirk & CF_BROKEN_MWDMA)
+ if (quirk & CF_BROKEN_MWDMA)
ap->mwdma_mask = 0;
- if (pdata->quirk & CF_BROKEN_UDMA)
+ if (quirk & CF_BROKEN_UDMA)
ap->udma_mask = 0;
}
ap->flags |= ATA_FLAG_PIO_POLLING | ATA_FLAG_NO_ATAPI;
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 7bc6e51757ee..c20de4a85cbd 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -65,6 +65,7 @@ config CLKSRC_DBX500_PRCMU_SCHED_CLOCK
config ARM_ARCH_TIMER
bool
+ select CLKSRC_OF if OF
config CLKSRC_METAG_GENERIC
def_bool y if METAG
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index d7ad425ab9b3..a2b254189782 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -248,14 +248,16 @@ static void __cpuinit arch_timer_stop(struct clock_event_device *clk)
static int __cpuinit arch_timer_cpu_notify(struct notifier_block *self,
unsigned long action, void *hcpu)
{
- struct clock_event_device *evt = this_cpu_ptr(arch_timer_evt);
-
+ /*
+ * Grab cpu pointer in each case to avoid spurious
+ * preemptible warnings
+ */
switch (action & ~CPU_TASKS_FROZEN) {
case CPU_STARTING:
- arch_timer_setup(evt);
+ arch_timer_setup(this_cpu_ptr(arch_timer_evt));
break;
case CPU_DYING:
- arch_timer_stop(evt);
+ arch_timer_stop(this_cpu_ptr(arch_timer_evt));
break;
}
@@ -337,22 +339,14 @@ out:
return err;
}
-static const struct of_device_id arch_timer_of_match[] __initconst = {
- { .compatible = "arm,armv7-timer", },
- { .compatible = "arm,armv8-timer", },
- {},
-};
-
-int __init arch_timer_init(void)
+static void __init arch_timer_init(struct device_node *np)
{
- struct device_node *np;
u32 freq;
int i;
- np = of_find_matching_node(NULL, arch_timer_of_match);
- if (!np) {
- pr_err("arch_timer: can't find DT node\n");
- return -ENODEV;
+ if (arch_timer_get_rate()) {
+ pr_warn("arch_timer: multiple nodes in dt, skipping\n");
+ return;
}
/* Try to determine the frequency from the device tree or CNTFRQ */
@@ -378,7 +372,7 @@ int __init arch_timer_init(void)
if (!arch_timer_ppi[PHYS_SECURE_PPI] ||
!arch_timer_ppi[PHYS_NONSECURE_PPI]) {
pr_warn("arch_timer: No interrupt available, giving up\n");
- return -EINVAL;
+ return;
}
}
@@ -387,5 +381,8 @@ int __init arch_timer_init(void)
else
arch_timer_read_counter = arch_counter_get_cntpct;
- return arch_timer_register();
+ arch_timer_register();
+ arch_timer_arch_init();
}
+CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_init);
+CLOCKSOURCE_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_init);
diff --git a/drivers/clocksource/exynos_mct.c b/drivers/clocksource/exynos_mct.c
index 661026834b23..13a9e4923a03 100644
--- a/drivers/clocksource/exynos_mct.c
+++ b/drivers/clocksource/exynos_mct.c
@@ -24,7 +24,6 @@
#include <linux/of_address.h>
#include <linux/clocksource.h>
-#include <asm/arch_timer.h>
#include <asm/localtimer.h>
#include <plat/cpu.h>
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
index c28fcccf4a0d..cda4cb5f7327 100644
--- a/drivers/irqchip/Makefile
+++ b/drivers/irqchip/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_IRQCHIP) += irqchip.o
obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o
obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o
+obj-$(CONFIG_ARCH_MVEBU) += irq-armada-370-xp.o
obj-$(CONFIG_ARCH_MXS) += irq-mxs.o
obj-$(CONFIG_ARCH_S3C24XX) += irq-s3c24xx.o
obj-$(CONFIG_METAG) += irq-metag-ext.o
diff --git a/drivers/irqchip/irq-armada-370-xp.c b/drivers/irqchip/irq-armada-370-xp.c
new file mode 100644
index 000000000000..bb328a366122
--- /dev/null
+++ b/drivers/irqchip/irq-armada-370-xp.c
@@ -0,0 +1,288 @@
+/*
+ * Marvell Armada 370 and Armada XP SoC IRQ handling
+ *
+ * Copyright (C) 2012 Marvell
+ *
+ * Lior Amsalem <alior@marvell.com>
+ * Gregory CLEMENT <gregory.clement@free-electrons.com>
+ * Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+ * Ben Dooks <ben.dooks@codethink.co.uk>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/irqdomain.h>
+#include <asm/mach/arch.h>
+#include <asm/exception.h>
+#include <asm/smp_plat.h>
+#include <asm/mach/irq.h>
+
+#include "irqchip.h"
+
+/* Interrupt Controller Registers Map */
+#define ARMADA_370_XP_INT_SET_MASK_OFFS (0x48)
+#define ARMADA_370_XP_INT_CLEAR_MASK_OFFS (0x4C)
+
+#define ARMADA_370_XP_INT_CONTROL (0x00)
+#define ARMADA_370_XP_INT_SET_ENABLE_OFFS (0x30)
+#define ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS (0x34)
+#define ARMADA_370_XP_INT_SOURCE_CTL(irq) (0x100 + irq*4)
+
+#define ARMADA_370_XP_CPU_INTACK_OFFS (0x44)
+
+#define ARMADA_370_XP_SW_TRIG_INT_OFFS (0x4)
+#define ARMADA_370_XP_IN_DRBEL_MSK_OFFS (0xc)
+#define ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS (0x8)
+
+#define ARMADA_370_XP_MAX_PER_CPU_IRQS (28)
+
+#define ARMADA_370_XP_TIMER0_PER_CPU_IRQ (5)
+
+#define IPI_DOORBELL_START (0)
+#define IPI_DOORBELL_END (8)
+#define IPI_DOORBELL_MASK 0xFF
+
+static DEFINE_RAW_SPINLOCK(irq_controller_lock);
+
+static void __iomem *per_cpu_int_base;
+static void __iomem *main_int_base;
+static struct irq_domain *armada_370_xp_mpic_domain;
+
+/*
+ * In SMP mode:
+ * For shared global interrupts, mask/unmask global enable bit
+ * For CPU interrupts, mask/unmask the calling CPU's bit
+ */
+static void armada_370_xp_irq_mask(struct irq_data *d)
+{
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+ if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ)
+ writel(hwirq, main_int_base +
+ ARMADA_370_XP_INT_CLEAR_ENABLE_OFFS);
+ else
+ writel(hwirq, per_cpu_int_base +
+ ARMADA_370_XP_INT_SET_MASK_OFFS);
+}
+
+static void armada_370_xp_irq_unmask(struct irq_data *d)
+{
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+
+ if (hwirq != ARMADA_370_XP_TIMER0_PER_CPU_IRQ)
+ writel(hwirq, main_int_base +
+ ARMADA_370_XP_INT_SET_ENABLE_OFFS);
+ else
+ writel(hwirq, per_cpu_int_base +
+ ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
+}
+
+#ifdef CONFIG_SMP
+static int armada_xp_set_affinity(struct irq_data *d,
+ const struct cpumask *mask_val, bool force)
+{
+ unsigned long reg;
+ unsigned long new_mask = 0;
+ unsigned long online_mask = 0;
+ unsigned long count = 0;
+ irq_hw_number_t hwirq = irqd_to_hwirq(d);
+ int cpu;
+
+ for_each_cpu(cpu, mask_val) {
+ new_mask |= 1 << cpu_logical_map(cpu);
+ count++;
+ }
+
+ /*
+ * Forbid mutlicore interrupt affinity
+ * This is required since the MPIC HW doesn't limit
+ * several CPUs from acknowledging the same interrupt.
+ */
+ if (count > 1)
+ return -EINVAL;
+
+ for_each_cpu(cpu, cpu_online_mask)
+ online_mask |= 1 << cpu_logical_map(cpu);
+
+ raw_spin_lock(&irq_controller_lock);
+
+ reg = readl(main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq));
+ reg = (reg & (~online_mask)) | new_mask;
+ writel(reg, main_int_base + ARMADA_370_XP_INT_SOURCE_CTL(hwirq));
+
+ raw_spin_unlock(&irq_controller_lock);
+
+ return 0;
+}
+#endif
+
+static struct irq_chip armada_370_xp_irq_chip = {
+ .name = "armada_370_xp_irq",
+ .irq_mask = armada_370_xp_irq_mask,
+ .irq_mask_ack = armada_370_xp_irq_mask,
+ .irq_unmask = armada_370_xp_irq_unmask,
+#ifdef CONFIG_SMP
+ .irq_set_affinity = armada_xp_set_affinity,
+#endif
+};
+
+static int armada_370_xp_mpic_irq_map(struct irq_domain *h,
+ unsigned int virq, irq_hw_number_t hw)
+{
+ armada_370_xp_irq_mask(irq_get_irq_data(virq));
+ if (hw != ARMADA_370_XP_TIMER0_PER_CPU_IRQ)
+ writel(hw, per_cpu_int_base +
+ ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
+ else
+ writel(hw, main_int_base + ARMADA_370_XP_INT_SET_ENABLE_OFFS);
+ irq_set_status_flags(virq, IRQ_LEVEL);
+
+ if (hw == ARMADA_370_XP_TIMER0_PER_CPU_IRQ) {
+ irq_set_percpu_devid(virq);
+ irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
+ handle_percpu_devid_irq);
+
+ } else {
+ irq_set_chip_and_handler(virq, &armada_370_xp_irq_chip,
+ handle_level_irq);
+ }
+ set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);
+
+ return 0;
+}
+
+#ifdef CONFIG_SMP
+void armada_mpic_send_doorbell(const struct cpumask *mask, unsigned int irq)
+{
+ int cpu;
+ unsigned long map = 0;
+
+ /* Convert our logical CPU mask into a physical one. */
+ for_each_cpu(cpu, mask)
+ map |= 1 << cpu_logical_map(cpu);
+
+ /*
+ * Ensure that stores to Normal memory are visible to the
+ * other CPUs before issuing the IPI.
+ */
+ dsb();
+
+ /* submit softirq */
+ writel((map << 8) | irq, main_int_base +
+ ARMADA_370_XP_SW_TRIG_INT_OFFS);
+}
+
+void armada_xp_mpic_smp_cpu_init(void)
+{
+ /* Clear pending IPIs */
+ writel(0, per_cpu_int_base + ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
+
+ /* Enable first 8 IPIs */
+ writel(IPI_DOORBELL_MASK, per_cpu_int_base +
+ ARMADA_370_XP_IN_DRBEL_MSK_OFFS);
+
+ /* Unmask IPI interrupt */
+ writel(0, per_cpu_int_base + ARMADA_370_XP_INT_CLEAR_MASK_OFFS);
+}
+#endif /* CONFIG_SMP */
+
+static struct irq_domain_ops armada_370_xp_mpic_irq_ops = {
+ .map = armada_370_xp_mpic_irq_map,
+ .xlate = irq_domain_xlate_onecell,
+};
+
+static asmlinkage void __exception_irq_entry
+armada_370_xp_handle_irq(struct pt_regs *regs)
+{
+ u32 irqstat, irqnr;
+
+ do {
+ irqstat = readl_relaxed(per_cpu_int_base +
+ ARMADA_370_XP_CPU_INTACK_OFFS);
+ irqnr = irqstat & 0x3FF;
+
+ if (irqnr > 1022)
+ break;
+
+ if (irqnr > 0) {
+ irqnr = irq_find_mapping(armada_370_xp_mpic_domain,
+ irqnr);
+ handle_IRQ(irqnr, regs);
+ continue;
+ }
+#ifdef CONFIG_SMP
+ /* IPI Handling */
+ if (irqnr == 0) {
+ u32 ipimask, ipinr;
+
+ ipimask = readl_relaxed(per_cpu_int_base +
+ ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS)
+ & IPI_DOORBELL_MASK;
+
+ writel(~IPI_DOORBELL_MASK, per_cpu_int_base +
+ ARMADA_370_XP_IN_DRBEL_CAUSE_OFFS);
+
+ /* Handle all pending doorbells */
+ for (ipinr = IPI_DOORBELL_START;
+ ipinr < IPI_DOORBELL_END; ipinr++) {
+ if (ipimask & (0x1 << ipinr))
+ handle_IPI(ipinr, regs);
+ }
+ continue;
+ }
+#endif
+
+ } while (1);
+}
+
+static int __init armada_370_xp_mpic_of_init(struct device_node *node,
+ struct device_node *parent)
+{
+ u32 control;
+
+ main_int_base = of_iomap(node, 0);
+ per_cpu_int_base = of_iomap(node, 1);
+
+ BUG_ON(!main_int_base);
+ BUG_ON(!per_cpu_int_base);
+
+ control = readl(main_int_base + ARMADA_370_XP_INT_CONTROL);
+
+ armada_370_xp_mpic_domain =
+ irq_domain_add_linear(node, (control >> 2) & 0x3ff,
+ &armada_370_xp_mpic_irq_ops, NULL);
+
+ if (!armada_370_xp_mpic_domain)
+ panic("Unable to add Armada_370_Xp MPIC irq domain (DT)\n");
+
+ irq_set_default_host(armada_370_xp_mpic_domain);
+
+#ifdef CONFIG_SMP
+ armada_xp_mpic_smp_cpu_init();
+
+ /*
+ * Set the default affinity from all CPUs to the boot cpu.
+ * This is required since the MPIC doesn't limit several CPUs
+ * from acknowledging the same interrupt.
+ */
+ cpumask_clear(irq_default_affinity);
+ cpumask_set_cpu(smp_processor_id(), irq_default_affinity);
+
+#endif
+
+ set_handle_irq(armada_370_xp_handle_irq);
+
+ return 0;
+}
+
+IRQCHIP_DECLARE(armada_370_xp_mpic, "marvell,mpic", armada_370_xp_mpic_of_init);
diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
index b0fe393c882c..371cc66f1a0e 100644
--- a/drivers/spi/spi-pl022.c
+++ b/drivers/spi/spi-pl022.c
@@ -1139,6 +1139,35 @@ err_no_rxchan:
return -ENODEV;
}
+static int pl022_dma_autoprobe(struct pl022 *pl022)
+{
+ struct device *dev = &pl022->adev->dev;
+
+ /* automatically configure DMA channels from platform, normally using DT */
+ pl022->dma_rx_channel = dma_request_slave_channel(dev, "rx");
+ if (!pl022->dma_rx_channel)
+ goto err_no_rxchan;
+
+ pl022->dma_tx_channel = dma_request_slave_channel(dev, "tx");
+ if (!pl022->dma_tx_channel)
+ goto err_no_txchan;
+
+ pl022->dummypage = kmalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!pl022->dummypage)
+ goto err_no_dummypage;
+
+ return 0;
+
+err_no_dummypage:
+ dma_release_channel(pl022->dma_tx_channel);
+ pl022->dma_tx_channel = NULL;
+err_no_txchan:
+ dma_release_channel(pl022->dma_rx_channel);
+ pl022->dma_rx_channel = NULL;
+err_no_rxchan:
+ return -ENODEV;
+}
+
static void terminate_dma(struct pl022 *pl022)
{
struct dma_chan *rxchan = pl022->dma_rx_channel;
@@ -1167,6 +1196,11 @@ static inline int configure_dma(struct pl022 *pl022)
return -ENODEV;
}
+static inline int pl022_dma_autoprobe(struct pl022 *pl022)
+{
+ return 0;
+}
+
static inline int pl022_dma_probe(struct pl022 *pl022)
{
return 0;
@@ -2226,8 +2260,13 @@ static int pl022_probe(struct amba_device *adev, const struct amba_id *id)
goto err_no_irq;
}
- /* Get DMA channels */
- if (platform_info->enable_dma) {
+ /* Get DMA channels, try autoconfiguration first */
+ status = pl022_dma_autoprobe(pl022);
+
+ /* If that failed, use channels from platform_info */
+ if (status == 0)
+ platform_info->enable_dma = 1;
+ else if (platform_info->enable_dma) {
status = pl022_dma_probe(pl022);
if (status != 0)
platform_info->enable_dma = 0;
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index b2e9e177a354..8ab70a620919 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -267,7 +267,7 @@ static void pl011_sgbuf_free(struct dma_chan *chan, struct pl011_sgbuf *sg,
}
}
-static void pl011_dma_probe_initcall(struct uart_amba_port *uap)
+static void pl011_dma_probe_initcall(struct device *dev, struct uart_amba_port *uap)
{
/* DMA is the sole user of the platform data right now */
struct amba_pl011_data *plat = uap->port.dev->platform_data;
@@ -281,20 +281,25 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap)
struct dma_chan *chan;
dma_cap_mask_t mask;
- /* We need platform data */
- if (!plat || !plat->dma_filter) {
- dev_info(uap->port.dev, "no DMA platform data\n");
- return;
- }
+ chan = dma_request_slave_channel(dev, "tx");
- /* Try to acquire a generic DMA engine slave TX channel */
- dma_cap_zero(mask);
- dma_cap_set(DMA_SLAVE, mask);
-
- chan = dma_request_channel(mask, plat->dma_filter, plat->dma_tx_param);
if (!chan) {
- dev_err(uap->port.dev, "no TX DMA channel!\n");
- return;
+ /* We need platform data */
+ if (!plat || !plat->dma_filter) {
+ dev_info(uap->port.dev, "no DMA platform data\n");
+ return;
+ }
+
+ /* Try to acquire a generic DMA engine slave TX channel */
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+
+ chan = dma_request_channel(mask, plat->dma_filter,
+ plat->dma_tx_param);
+ if (!chan) {
+ dev_err(uap->port.dev, "no TX DMA channel!\n");
+ return;
+ }
}
dmaengine_slave_config(chan, &tx_conf);
@@ -304,7 +309,18 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap)
dma_chan_name(uap->dmatx.chan));
/* Optionally make use of an RX channel as well */
- if (plat->dma_rx_param) {
+ chan = dma_request_slave_channel(dev, "rx");
+
+ if (!chan && plat->dma_rx_param) {
+ chan = dma_request_channel(mask, plat->dma_filter, plat->dma_rx_param);
+
+ if (!chan) {
+ dev_err(uap->port.dev, "no RX DMA channel!\n");
+ return;
+ }
+ }
+
+ if (chan) {
struct dma_slave_config rx_conf = {
.src_addr = uap->port.mapbase + UART01x_DR,
.src_addr_width = DMA_SLAVE_BUSWIDTH_1_BYTE,
@@ -313,12 +329,6 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap)
.device_fc = false,
};
- chan = dma_request_channel(mask, plat->dma_filter, plat->dma_rx_param);
- if (!chan) {
- dev_err(uap->port.dev, "no RX DMA channel!\n");
- return;
- }
-
dmaengine_slave_config(chan, &rx_conf);
uap->dmarx.chan = chan;
@@ -360,6 +370,7 @@ static void pl011_dma_probe_initcall(struct uart_amba_port *uap)
struct dma_uap {
struct list_head node;
struct uart_amba_port *uap;
+ struct device *dev;
};
static LIST_HEAD(pl011_dma_uarts);
@@ -370,7 +381,7 @@ static int __init pl011_dma_initcall(void)
list_for_each_safe(node, tmp, &pl011_dma_uarts) {
struct dma_uap *dmau = list_entry(node, struct dma_uap, node);
- pl011_dma_probe_initcall(dmau->uap);
+ pl011_dma_probe_initcall(dmau->dev, dmau->uap);
list_del(node);
kfree(dmau);
}
@@ -379,18 +390,19 @@ static int __init pl011_dma_initcall(void)
device_initcall(pl011_dma_initcall);
-static void pl011_dma_probe(struct uart_amba_port *uap)
+static void pl011_dma_probe(struct device *dev, struct uart_amba_port *uap)
{
struct dma_uap *dmau = kzalloc(sizeof(struct dma_uap), GFP_KERNEL);
if (dmau) {
dmau->uap = uap;
+ dmau->dev = dev;
list_add_tail(&dmau->node, &pl011_dma_uarts);
}
}
#else
-static void pl011_dma_probe(struct uart_amba_port *uap)
+static void pl011_dma_probe(struct device *dev, struct uart_amba_port *uap)
{
- pl011_dma_probe_initcall(uap);
+ pl011_dma_probe_initcall(dev, uap);
}
#endif
@@ -1096,7 +1108,7 @@ static inline bool pl011_dma_rx_running(struct uart_amba_port *uap)
#else
/* Blank functions if the DMA engine is not available */
-static inline void pl011_dma_probe(struct uart_amba_port *uap)
+static inline void pl011_dma_probe(struct device *dev, struct uart_amba_port *uap)
{
}
@@ -2155,7 +2167,7 @@ static int pl011_probe(struct amba_device *dev, const struct amba_id *id)
uap->port.ops = &amba_pl011_pops;
uap->port.flags = UPF_BOOT_AUTOCONF;
uap->port.line = i;
- pl011_dma_probe(uap);
+ pl011_dma_probe(&dev->dev, uap);
/* Ensure interrupts from this UART are masked and cleared */
writew(0, uap->port.membase + UART011_IMSC);
OpenPOWER on IntegriCloud