summaryrefslogtreecommitdiffstats
path: root/patches/linux-3.9-rc1/0001-irqchip-Add-support-for-ARMv7-M-s-NVIC.patch
diff options
context:
space:
mode:
Diffstat (limited to 'patches/linux-3.9-rc1/0001-irqchip-Add-support-for-ARMv7-M-s-NVIC.patch')
-rw-r--r--patches/linux-3.9-rc1/0001-irqchip-Add-support-for-ARMv7-M-s-NVIC.patch231
1 files changed, 231 insertions, 0 deletions
diff --git a/patches/linux-3.9-rc1/0001-irqchip-Add-support-for-ARMv7-M-s-NVIC.patch b/patches/linux-3.9-rc1/0001-irqchip-Add-support-for-ARMv7-M-s-NVIC.patch
new file mode 100644
index 0000000..f73158a
--- /dev/null
+++ b/patches/linux-3.9-rc1/0001-irqchip-Add-support-for-ARMv7-M-s-NVIC.patch
@@ -0,0 +1,231 @@
+From 35aa7416d459f7d8a84a3aa93955dd5c21594655 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Uwe=20Kleine-K=C3=B6nig?= <u.kleine-koenig@pengutronix.de>
+Date: Fri, 21 May 2010 18:06:43 +0100
+Subject: [PATCH 01/18] irqchip: Add support for ARMv7-M's NVIC
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+This interrupt controller is found on Cortex-M3 and Cortex-M4 machines.
+
+Support for this controller appeared in Catalin's Cortex tree based on
+2.6.33 but was nearly completely rewritten.
+
+Signed-off-by: Catalin Marinas <catalin.marinas@arm.com>
+Signed-off-by: Uwe Kleine-König <u.kleine-koenig@pengutronix.de>
+Forwarded: id:1363612826-15623-1-git-send-email-u.kleine-koenig@pengutronix.de (v2)
+---
+ drivers/irqchip/Kconfig | 4 ++
+ drivers/irqchip/Makefile | 1 +
+ drivers/irqchip/irq-nvic.c | 176 +++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 181 insertions(+)
+ create mode 100644 drivers/irqchip/irq-nvic.c
+
+diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
+index a350969..18657fd 100644
+--- a/drivers/irqchip/Kconfig
++++ b/drivers/irqchip/Kconfig
+@@ -10,6 +10,10 @@ config ARM_GIC
+ config GIC_NON_BANKED
+ bool
+
++config ARM_NVIC
++ bool
++ select IRQ_DOMAIN
++
+ config ARM_VIC
+ bool
+ select IRQ_DOMAIN
+diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile
+index 98e3b87..7227c5f 100644
+--- a/drivers/irqchip/Makefile
++++ b/drivers/irqchip/Makefile
+@@ -7,5 +7,6 @@ obj-$(CONFIG_METAG_PERFCOUNTER_IRQS) += irq-metag.o
+ obj-$(CONFIG_ARCH_SUNXI) += irq-sunxi.o
+ obj-$(CONFIG_ARCH_SPEAR3XX) += spear-shirq.o
+ obj-$(CONFIG_ARM_GIC) += irq-gic.o
++obj-$(CONFIG_ARM_NVIC) += irq-nvic.o
+ obj-$(CONFIG_ARM_VIC) += irq-vic.o
+ obj-$(CONFIG_VERSATILE_FPGA_IRQ) += irq-versatile-fpga.o
+diff --git a/drivers/irqchip/irq-nvic.c b/drivers/irqchip/irq-nvic.c
+new file mode 100644
+index 0000000..721c328
+--- /dev/null
++++ b/drivers/irqchip/irq-nvic.c
+@@ -0,0 +1,176 @@
++/*
++ * drivers/irq/irq-nvic.c
++ *
++ * Copyright (C) 2008 ARM Limited, All Rights Reserved.
++ * Copyright (C) 2013 Pengutronix
++ *
++ * 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.
++ *
++ * Support for the Nested Vectored Interrupt Controller found on the
++ * ARMv7-M CPUs (Cortex-M3/M4)
++ */
++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
++
++#include <linux/init.h>
++#include <linux/kernel.h>
++#include <linux/export.h>
++#include <linux/slab.h>
++#include <linux/err.h>
++#include <linux/io.h>
++#include <linux/of.h>
++#include <linux/of_address.h>
++#include <linux/irq.h>
++#include <linux/irqdomain.h>
++
++#include <asm/v7m.h>
++
++#include "irqchip.h"
++
++#define NVIC_ISER 0x000
++#define NVIC_ICER 0x080
++#define NVIC_IPR 0x300
++
++/*
++ * Each bank handles 32 irqs. Only the 16th (= last) bank handles only
++ * 16 irqs yielding a maximum of 15 * 32 + 16 = 496 interrupts.
++ */
++#define NVIC_MAX_IRQ 496
++
++struct nvic_bank_data {
++ /*
++ * For irq i base holds nvic_base + 4 * i / 32. So you can access the
++ * right ISER register (i.e ISER[i / 32]) by just taking base + ISER.
++ * Ditto for ICER.
++ */
++ void __iomem *base;
++};
++
++static inline void __iomem *nvic_bank_base(struct irq_data *d)
++{
++ struct nvic_bank_data *data = irq_data_get_irq_chip_data(d);
++ return data->base;
++}
++
++static void nvic_mask_irq(struct irq_data *d)
++{
++ u32 mask = 1 << (d->hwirq % 32);
++
++ writel_relaxed(mask, nvic_bank_base(d) + NVIC_ICER);
++}
++
++static void nvic_unmask_irq(struct irq_data *d)
++{
++ u32 mask = 1 << (d->hwirq % 32);
++
++ writel_relaxed(mask, nvic_bank_base(d) + NVIC_ISER);
++}
++
++static void nvic_eoi(struct irq_data *d)
++{
++ /*
++ * This is a no-op as end of interrupt is signaled by the exception
++ * return sequence.
++ */
++}
++
++static struct irq_chip nvic_chip = {
++ .name = "NVIC",
++ .irq_mask = nvic_mask_irq,
++ .irq_unmask = nvic_unmask_irq,
++ .irq_eoi = nvic_eoi,
++};
++
++static int __init nvic_init_bases(struct device_node *node,
++ void __iomem *nvic_base)
++{
++ unsigned int irqs, i;
++ int irq_base, ret = -ENOMEM;
++ struct irq_domain *irq_domain;
++ struct nvic_bank_data *bank_data;
++ unsigned numbanks = (readl_relaxed(V7M_SCS_ICTR) &
++ V7M_SCS_ICTR_INTLINESNUM_MASK) + 1;
++
++ irqs = numbanks * 32;
++ if (irqs > NVIC_MAX_IRQ)
++ irqs = NVIC_MAX_IRQ;
++
++ bank_data = kzalloc(sizeof(*bank_data) * numbanks, GFP_KERNEL);
++ if (!bank_data) {
++ pr_warn("Failed to allocate chip data");
++ goto err_alloc_bank_data;
++ }
++ for (i = 0; i < numbanks; ++i)
++ bank_data[i].base = nvic_base + 4 * i;
++
++ irq_base = irq_alloc_descs_from(16, irqs, numa_node_id());
++ if (irq_base < 0) {
++ pr_warn("Cannot allocate irq_descs\n");
++ ret = irq_base;
++ goto err_irq_alloc_descs;
++ }
++ if (irq_base != 16) {
++ /*
++ * The entry code just passes the exception number (i.e. irq
++ * number + 16) to asm_do_IRQ, so the offset needs to be fixed
++ * here.
++ */
++ pr_warn("Failed to allocate irq_descs at offset 16\n");
++ goto err_wrong_irq_base;
++ }
++
++ irq_domain = irq_domain_add_legacy(node, irqs, irq_base, 0,
++ &irq_domain_simple_ops, NULL);
++ if (!irq_domain) {
++ pr_warn("Failed to allocate irq domain\n");
++ goto err_domain_add;
++ }
++
++ /* Disable all interrupts */
++ for (i = 0; i < irqs; i += 32)
++ writel_relaxed(~0, nvic_base + NVIC_ICER + i * 4 / 32);
++
++ /* Set priority on all interrupts */
++ for (i = 0; i < irqs; i += 4)
++ writel_relaxed(0, nvic_base + NVIC_IPR + i);
++
++ /* Setup the Linux IRQ subsystem */
++ for (i = 0; i < irqs; i++) {
++ irq_set_chip_and_handler(irq_base + i, &nvic_chip,
++ handle_fasteoi_irq);
++ irq_set_chip_data(irq_base + i, bank_data + i / 32);
++ set_irq_flags(irq_base + i, IRQF_VALID | IRQF_PROBE);
++ }
++
++ return 0;
++
++err_domain_add:
++err_wrong_irq_base:
++
++ irq_free_descs(irq_base, irqs - 16);
++err_irq_alloc_descs:
++
++ kfree(bank_data);
++err_alloc_bank_data:
++
++ return ret;
++}
++
++static int __init nvic_of_init(struct device_node *node,
++ struct device_node *parent)
++{
++ void __iomem *nvic_base;
++
++ if (!node)
++ return -ENODEV;
++
++ nvic_base = of_iomap(node, 0);
++ if (!nvic_base) {
++ pr_warn("unable to map nvic registers\n");
++ return -ENOMEM;
++ }
++
++ return nvic_init_bases(node, nvic_base);
++}
++IRQCHIP_DECLARE(armv7m_nvic, "arm,armv7m-nvic", nvic_of_init);