This series of patches is all over the place, and touches both
core infrastructure and board ports. The patches also vary quite a
bit in terms of quality and readiness for merging. However, I wanted
to get the whole lot out onto the list together so it can be seen
exactly what needs to be done to convert an ARM platform over to
registering devices directly out of the DT.
This first 3 patches are an early implementation of irq_domains, plus
some helpers to make creating a simple irq_domain easy. I've already
posted the first patch before, and I haven't responded to any comments
yet. These patches are needed so that DT irq connections can be
correctly mapped into the Linux IRQ numberspace.
Patches 4-7 modify the core DT infrastructure to create a new helper
for registering devices (has slightly different semantics from the
older of_platform_bus_probe()), working with AMBA devices, and
allowing platform code to provide a lookup table of 'preferred' names
and platform_data for DT described devices.
The use of platform_data here is sure to raise an eyebrow or two, but
I assure you it is both necessary and temporary. Necessary because it
allows platforms to transition to DT without having to fix all
name-based clock connections and platform_data usage in device
drivers. Temporary because platform lookup tables will get removed
after the drivers do get fixed.
Patch 8 is a trivial powerpc patch to migrate a platform to the newer
infrastructure, and patches 9 & 10 are also trivial infrastructure
additions.
Patch 11 is the main event. It creates a new versatile machine
definition which matches against a versatile device tree, and all
device registrations are generated from DT data. I've tested this one
on the QEMU versatile model, but not real hardware. Most certainly
not everything is working here yet. However, it is an example of how
to migrate a platform over to using the DT.
Cheers,
g.
---
Grant Likely (11):
irq: add irq_domain translation infrastructure
of/address: Add of_find_matching_node_by_address helper
dt/irq: add irq_domain_add_simple() helper
dt: add of_platform_populate() for creating device from the device tree
dt: Add default match table for bus ids
drivers/amba: create devices from device tree
dt/platform: allow device name to be overridden
powerpc/5200: convert mpc5200 to use of_platform_populate()
arm/dt: Add dt machine definition
arm/dt: Add skeleton dtsi file
arm/versatile: Add device tree support
.../devicetree/bindings/arm/primecell.txt | 21 ++
arch/arm/boot/dts/skeleton.dtsi | 13 +
arch/arm/boot/dts/versatile-ab.dts | 185 +++++++++++++++++++
arch/arm/boot/dts/versatile-pb.dts | 48 +++++
arch/arm/include/asm/mach/arch.h | 9 +
arch/arm/include/asm/prom.h | 5 -
arch/arm/kernel/devtree.c | 14 -
arch/arm/mach-versatile/Kconfig | 8 +
arch/arm/mach-versatile/Makefile | 1
arch/arm/mach-versatile/core.c | 61 ++++++
arch/arm/mach-versatile/core.h | 5 +
arch/arm/mach-versatile/versatile_dt.c | 66 +++++++
arch/microblaze/include/asm/irq.h | 10 -
arch/microblaze/kernel/irq.c | 7 -
arch/mips/include/asm/irq.h | 5 -
arch/mips/kernel/prom.c | 14 -
arch/powerpc/include/asm/irq.h | 1
arch/powerpc/platforms/52xx/mpc52xx_common.c | 10 -
arch/x86/include/asm/irq_controller.h | 12 -
arch/x86/include/asm/prom.h | 10 -
arch/x86/kernel/devicetree.c | 101 +++-------
drivers/of/address.c | 18 ++
drivers/of/platform.c | 196 +++++++++++++++++++-
include/linux/irq.h | 92 +++++++++
include/linux/of_address.h | 4
include/linux/of_irq.h | 2
include/linux/of_platform.h | 40 ++++
kernel/irq/Makefile | 2
kernel/irq/irqdomain.c | 193 ++++++++++++++++++++
29 files changed, 994 insertions(+), 159 deletions(-)
create mode 100644 Documentation/devicetree/bindings/arm/primecell.txt
create mode 100644 arch/arm/boot/dts/skeleton.dtsi
create mode 100644 arch/arm/boot/dts/versatile-ab.dts
create mode 100644 arch/arm/boot/dts/versatile-pb.dts
create mode 100644 arch/arm/mach-versatile/versatile_dt.c
delete mode 100644 arch/x86/include/asm/irq_controller.h
create mode 100644 kernel/irq/irqdomain.c
--
Signature
This patch adds irq_domain infrastructure for translating from
hardware irq numbers to linux irqs. This is particularly important
for architectures adding device tree support because the current
implementation (excluding PowerPC and SPARC) cannot handle
translation for more than a single interrupt controller. irq_domain
supports device tree translation for any number of interrupt
controllers.
This patch converts x86, Microblaze, ARM and MIPS to use irq_domain
for device tree irq translation. x86 is untested beyond compiling it,
and the patch definitely breaks MIPS and Microblaze because I haven't
added the code to either of them to actually register an irq_domain
yet (easy to fix), but on ARM it works. I'm circulating this for comments
to make sure I'm on the right path before I fix up the MIPS and
Microblaze bits.
PowerPC has /not/ been converted to use this new infrastructure. It
is still missing some features before it can replace the virq
infrastructure already in powerpc (see documentation on
irq_domain_map/unmap for details). Followup patches will add the
missing pieces and migrate PowerPC to use irq_domain.
SPARC has its own method of managing interrupts from the device tree
and is unaffected by this change.
Signed-off-by: Grant Likely <[email protected]>
---
arch/arm/include/asm/prom.h | 5 -
arch/arm/kernel/devtree.c | 14 ---
arch/microblaze/include/asm/irq.h | 10 --
arch/microblaze/kernel/irq.c | 7 --
arch/mips/include/asm/irq.h | 5 -
arch/mips/kernel/prom.c | 14 ---
arch/powerpc/include/asm/irq.h | 1
arch/x86/include/asm/irq_controller.h | 12 ---
arch/x86/include/asm/prom.h | 10 --
arch/x86/kernel/devicetree.c | 101 ++++++++----------------
include/linux/irq.h | 81 ++++++++++++++++++++
include/linux/of_irq.h | 2
kernel/irq/Makefile | 2
kernel/irq/irqdomain.c | 137 +++++++++++++++++++++++++++++++++
14 files changed, 257 insertions(+), 144 deletions(-)
delete mode 100644 arch/x86/include/asm/irq_controller.h
create mode 100644 kernel/irq/irqdomain.c
diff --git a/arch/arm/include/asm/prom.h b/arch/arm/include/asm/prom.h
index 11b8708..6f65ca8 100644
--- a/arch/arm/include/asm/prom.h
+++ b/arch/arm/include/asm/prom.h
@@ -16,11 +16,6 @@
#include <asm/setup.h>
#include <asm/irq.h>
-static inline void irq_dispose_mapping(unsigned int virq)
-{
- return;
-}
-
extern struct machine_desc *setup_machine_fdt(unsigned int dt_phys);
extern void arm_dt_memblock_reserve(void);
diff --git a/arch/arm/kernel/devtree.c b/arch/arm/kernel/devtree.c
index a701e42..1e0b613 100644
--- a/arch/arm/kernel/devtree.c
+++ b/arch/arm/kernel/devtree.c
@@ -129,17 +129,3 @@ struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
return mdesc_best;
}
-
-/**
- * irq_create_of_mapping - Hook to resolve OF irq specifier into a Linux irq#
- *
- * Currently the mapping mechanism is trivial; simple flat hwirq numbers are
- * mapped 1:1 onto Linux irq numbers. Cascaded irq controllers are not
- * supported.
- */
-unsigned int irq_create_of_mapping(struct device_node *controller,
- const u32 *intspec, unsigned int intsize)
-{
- return intspec[0];
-}
-EXPORT_SYMBOL_GPL(irq_create_of_mapping);
diff --git a/arch/microblaze/include/asm/irq.h b/arch/microblaze/include/asm/irq.h
index cc54187..6a6d83b 100644
--- a/arch/microblaze/include/asm/irq.h
+++ b/arch/microblaze/include/asm/irq.h
@@ -16,6 +16,7 @@
* be big enough to enclose whatever representation is used by a given
* platform.
*/
+#define _IRQ_HW_NUMBER_T
typedef unsigned long irq_hw_number_t;
extern unsigned int nr_irq;
@@ -25,15 +26,6 @@ extern unsigned int nr_irq;
struct pt_regs;
extern void do_IRQ(struct pt_regs *regs);
-/** FIXME - not implement
- * irq_dispose_mapping - Unmap an interrupt
- * @virq: linux virq number of the interrupt to unmap
- */
-static inline void irq_dispose_mapping(unsigned int virq)
-{
- return;
-}
-
struct irq_host;
/**
diff --git a/arch/microblaze/kernel/irq.c b/arch/microblaze/kernel/irq.c
index ce7ac84..59bb560 100644
--- a/arch/microblaze/kernel/irq.c
+++ b/arch/microblaze/kernel/irq.c
@@ -54,10 +54,3 @@ unsigned int irq_create_mapping(struct irq_host *host, irq_hw_number_t hwirq)
return hwirq;
}
EXPORT_SYMBOL_GPL(irq_create_mapping);
-
-unsigned int irq_create_of_mapping(struct device_node *controller,
- const u32 *intspec, unsigned int intsize)
-{
- return intspec[0];
-}
-EXPORT_SYMBOL_GPL(irq_create_of_mapping);
diff --git a/arch/mips/include/asm/irq.h b/arch/mips/include/asm/irq.h
index 0ec0129..dc650ae 100644
--- a/arch/mips/include/asm/irq.h
+++ b/arch/mips/include/asm/irq.h
@@ -16,11 +16,6 @@
#include <irq.h>
-static inline void irq_dispose_mapping(unsigned int virq)
-{
- return;
-}
-
#ifdef CONFIG_I8259
static inline int irq_canonicalize(int irq)
{
diff --git a/arch/mips/kernel/prom.c b/arch/mips/kernel/prom.c
index 5b7eade..010aaaf 100644
--- a/arch/mips/kernel/prom.c
+++ b/arch/mips/kernel/prom.c
@@ -60,20 +60,6 @@ void __init early_init_dt_setup_initrd_arch(unsigned long start,
}
#endif
-/*
- * irq_create_of_mapping - Hook to resolve OF irq specifier into a Linux irq#
- *
- * Currently the mapping mechanism is trivial; simple flat hwirq numbers are
- * mapped 1:1 onto Linux irq numbers. Cascaded irq controllers are not
- * supported.
- */
-unsigned int irq_create_of_mapping(struct device_node *controller,
- const u32 *intspec, unsigned int intsize)
-{
- return intspec[0];
-}
-EXPORT_SYMBOL_GPL(irq_create_of_mapping);
-
void __init early_init_devtree(void *params)
{
/* Setup flat device-tree pointer */
diff --git a/arch/powerpc/include/asm/irq.h b/arch/powerpc/include/asm/irq.h
index 1bff591..66bf09e 100644
--- a/arch/powerpc/include/asm/irq.h
+++ b/arch/powerpc/include/asm/irq.h
@@ -45,6 +45,7 @@ extern atomic_t ppc_n_lost_interrupts;
* be big enough to enclose whatever representation is used by a given
* platform.
*/
+#define _IRQ_HW_NUMBER_T
typedef unsigned long irq_hw_number_t;
/* Interrupt controller "host" data structure. This could be defined as a
diff --git a/arch/x86/include/asm/irq_controller.h b/arch/x86/include/asm/irq_controller.h
deleted file mode 100644
index 423bbbd..0000000
--- a/arch/x86/include/asm/irq_controller.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef __IRQ_CONTROLLER__
-#define __IRQ_CONTROLLER__
-
-struct irq_domain {
- int (*xlate)(struct irq_domain *h, const u32 *intspec, u32 intsize,
- u32 *out_hwirq, u32 *out_type);
- void *priv;
- struct device_node *controller;
- struct list_head l;
-};
-
-#endif
diff --git a/arch/x86/include/asm/prom.h b/arch/x86/include/asm/prom.h
index 971e0b4..2c8101e 100644
--- a/arch/x86/include/asm/prom.h
+++ b/arch/x86/include/asm/prom.h
@@ -21,7 +21,6 @@
#include <asm/irq.h>
#include <asm/atomic.h>
#include <asm/setup.h>
-#include <asm/irq_controller.h>
#ifdef CONFIG_OF
extern int of_ioapic;
@@ -54,15 +53,6 @@ extern char cmd_line[COMMAND_LINE_SIZE];
#define pci_address_to_pio pci_address_to_pio
unsigned long pci_address_to_pio(phys_addr_t addr);
-/**
- * irq_dispose_mapping - Unmap an interrupt
- * @virq: linux virq number of the interrupt to unmap
- *
- * FIXME: We really should implement proper virq handling like power,
- * but that's going to be major surgery.
- */
-static inline void irq_dispose_mapping(unsigned int virq) { }
-
#define HAVE_ARCH_DEVTREE_FIXUPS
#endif /* __ASSEMBLY__ */
diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c
index 9aeb78a..250c2d5 100644
--- a/arch/x86/kernel/devicetree.c
+++ b/arch/x86/kernel/devicetree.c
@@ -16,64 +16,14 @@
#include <linux/initrd.h>
#include <asm/hpet.h>
-#include <asm/irq_controller.h>
#include <asm/apic.h>
#include <asm/pci_x86.h>
__initdata u64 initial_dtb;
char __initdata cmd_line[COMMAND_LINE_SIZE];
-static LIST_HEAD(irq_domains);
-static DEFINE_RAW_SPINLOCK(big_irq_lock);
int __initdata of_ioapic;
-#ifdef CONFIG_X86_IO_APIC
-static void add_interrupt_host(struct irq_domain *ih)
-{
- unsigned long flags;
-
- raw_spin_lock_irqsave(&big_irq_lock, flags);
- list_add(&ih->l, &irq_domains);
- raw_spin_unlock_irqrestore(&big_irq_lock, flags);
-}
-#endif
-
-static struct irq_domain *get_ih_from_node(struct device_node *controller)
-{
- struct irq_domain *ih, *found = NULL;
- unsigned long flags;
-
- raw_spin_lock_irqsave(&big_irq_lock, flags);
- list_for_each_entry(ih, &irq_domains, l) {
- if (ih->controller == controller) {
- found = ih;
- break;
- }
- }
- raw_spin_unlock_irqrestore(&big_irq_lock, flags);
- return found;
-}
-
-unsigned int irq_create_of_mapping(struct device_node *controller,
- const u32 *intspec, unsigned int intsize)
-{
- struct irq_domain *ih;
- u32 virq, type;
- int ret;
-
- ih = get_ih_from_node(controller);
- if (!ih)
- return 0;
- ret = ih->xlate(ih, intspec, intsize, &virq, &type);
- if (ret)
- return 0;
- if (type == IRQ_TYPE_NONE)
- return virq;
- irq_set_irq_type(virq, type);
- return virq;
-}
-EXPORT_SYMBOL_GPL(irq_create_of_mapping);
-
unsigned long pci_address_to_pio(phys_addr_t address)
{
/*
@@ -377,36 +327,49 @@ static struct of_ioapic_type of_ioapic_type[] =
},
};
-static int ioapic_xlate(struct irq_domain *id, const u32 *intspec, u32 intsize,
- u32 *out_hwirq, u32 *out_type)
+static int ioapic_dt_translate(struct irq_domain *domain,
+ struct device_node *controller,
+ const u32 *intspec, u32 intsize,
+ irq_hw_number_t *out_hwirq, u32 *out_type)
{
- struct mp_ioapic_gsi *gsi_cfg;
struct io_apic_irq_attr attr;
struct of_ioapic_type *it;
u32 line, idx, type;
+ int rc;
- if (intsize < 2)
+ if (controller != domain->of_node)
return -EINVAL;
- line = *intspec;
- idx = (u32) id->priv;
- gsi_cfg = mp_ioapic_gsi_routing(idx);
- *out_hwirq = line + gsi_cfg->gsi_base;
+ if (intsize < 2)
+ return -EINVAL;
- intspec++;
- type = *intspec;
+ line = intspec[0];
- if (type >= ARRAY_SIZE(of_ioapic_type))
+ if (intspec[1] >= ARRAY_SIZE(of_ioapic_type))
return -EINVAL;
- it = of_ioapic_type + type;
- *out_type = it->out_type;
+ it = of_ioapic_type + intspec[1];
+ type = it->out_type;
+ idx = (u32) domain->priv;
set_io_apic_irq_attr(&attr, idx, line, it->trigger, it->polarity);
- return io_apic_setup_irq_pin_once(*out_hwirq, cpu_to_node(0), &attr);
+ rc = io_apic_setup_irq_pin_once(irq_domain_to_irq(domain, line),
+ cpu_to_node(0), &attr);
+ if (rc)
+ return rc;
+
+ if (out_hwirq)
+ *out_hwirq = line;
+ if (out_type)
+ *out_type = type;
+ return 0;
}
+const struct irq_domain_ops ioapic_irq_domain_ops = {
+ .dt_translate = ioapic_dt_translate,
+};
+
static void __init ioapic_add_ofnode(struct device_node *np)
{
struct resource r;
@@ -422,13 +385,17 @@ static void __init ioapic_add_ofnode(struct device_node *np)
for (i = 0; i < nr_ioapics; i++) {
if (r.start == mpc_ioapic_addr(i)) {
struct irq_domain *id;
+ struct mp_ioapic_gsi *gsi_cfg;
+
+ gsi_cfg = mp_ioapic_gsi_routing(i);
id = kzalloc(sizeof(*id), GFP_KERNEL);
BUG_ON(!id);
- id->controller = np;
- id->xlate = ioapic_xlate;
+ id->ops = &ioapic_irq_domain_ops;
+ id->irq_base = gsi_cfg->gsi_base;
+ id->of_node = np;
id->priv = (void *)i;
- add_interrupt_host(id);
+ irq_domain_add(id);
return;
}
}
diff --git a/include/linux/irq.h b/include/linux/irq.h
index 8b45384..a103c01 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -35,6 +35,16 @@ typedef void (*irq_flow_handler_t)(unsigned int irq,
struct irq_desc *desc);
typedef void (*irq_preflow_handler_t)(struct irq_data *data);
+/* This type is the placeholder for a hardware interrupt number. It has to
+ * be big enough to enclose whatever representation is used by a given
+ * platform.
+ */
+#ifndef _IRQ_HW_NUMBER_T
+#define _IRQ_HW_NUMBER_T
+typedef unsigned long irq_hw_number_t;
+#endif
+
+
/*
* IRQ line status.
*
@@ -113,14 +123,18 @@ enum {
};
struct msi_desc;
+struct irq_domain;
/**
* struct irq_data - per irq and irq chip data passed down to chip functions
* @irq: interrupt number
+ * @hwirq: hardware interrupt number, local to the interrupt domain
* @node: node index useful for balancing
* @state_use_accessors: status information for irq chip functions.
* Use accessor functions to deal with it
* @chip: low level interrupt hardware access
+ * @domain: Interrupt translation domain; responsible for mapping
+ * between hwirq number and linux irq number.
* @handler_data: per-IRQ data for the irq_chip methods
* @chip_data: platform-specific per-chip private data for the chip
* methods, to allow shared chip implementations
@@ -133,9 +147,11 @@ struct msi_desc;
*/
struct irq_data {
unsigned int irq;
+ irq_hw_number_t hwirq;
unsigned int node;
unsigned int state_use_accessors;
struct irq_chip *chip;
+ struct irq_domain *domain;
void *handler_data;
void *chip_data;
struct msi_desc *msi_desc;
@@ -716,6 +732,71 @@ static inline void irq_gc_unlock(struct irq_chip_generic *gc) { }
#endif /* CONFIG_GENERIC_HARDIRQS */
+/*
+ * irq-domain - IRQ translation domains
+ *
+ * Translation infrastructure between hw and linux irq numbers.
+ */
+struct device_node;
+
+/**
+ * struct irq_domain_ops - Methods for irq_domain objects
+ * @to_irq: (optional) given a local hardware irq number, return the linux
+ * irq number. If to_irq is not implemented, then the irq_domain
+ * will use this translation: irq = (domain->irq_base + hwirq)
+ * @dt_translate: Given a device tree node and interrupt specifier, decode
+ * the hardware irq number and linux irq type value.
+ */
+struct irq_domain_ops {
+ unsigned int (*to_irq)(struct irq_domain *d, irq_hw_number_t hwirq);
+
+#ifdef CONFIG_OF
+ int (*dt_translate)(struct irq_domain *d, struct device_node *node,
+ const u32 *intspec, unsigned int intsize,
+ irq_hw_number_t *out_hwirq, unsigned int *out_type);
+#endif
+};
+
+/**
+ * struct irq_domain - Hardware interrupt number translation object
+ * @list: Element in global irq_domain list.
+ * @irq_base: Start of irq_desc range assigned to the irq_domain. The creator
+ * of the irq_domain is responsible for allocating the array of
+ * irq_desc structures.
+ * @ops: pointer to irq_domain methods
+ * @priv: private data pointer for use by owner. Not touched by irq_domain
+ * core code.
+ * @of_node: (optional) Pointer to device tree nodes associated with the
+ * irq_domain. Used when decoding device tree interrupt specifiers.
+ */
+struct irq_domain {
+ struct list_head list;
+ unsigned int irq_base;
+ const struct irq_domain_ops *ops;
+ void *priv;
+
+ struct device_node *of_node;
+};
+
+/**
+ * irq_domain_to_irq() - Translate from a hardware irq to a linux irq number
+ *
+ * Returns the linux irq number associated with a hardware irq. By default,
+ * the mapping is irq == domain->irq_base + hwirq, but this mapping can
+ * be overridden if the irq_domain implements a .to_irq() hook.
+ */
+static inline unsigned int irq_domain_to_irq(struct irq_domain *d,
+ irq_hw_number_t hwirq)
+{
+ return d->ops->to_irq ? d->ops->to_irq(d, hwirq) : d->irq_base + hwirq;
+}
+
+extern void irq_domain_add(struct irq_domain *domain);
+extern void irq_domain_del(struct irq_domain *domain);
+extern unsigned int irq_domain_map(struct irq_domain *domain,
+ irq_hw_number_t hwirq);
+extern void irq_domain_unmap(struct irq_domain *domain, irq_hw_number_t hw);
+
#endif /* !CONFIG_S390 */
#endif /* _LINUX_IRQ_H */
diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h
index e6955f5..67a2a57 100644
--- a/include/linux/of_irq.h
+++ b/include/linux/of_irq.h
@@ -63,6 +63,7 @@ extern int of_irq_map_one(struct device_node *device, int index,
extern unsigned int irq_create_of_mapping(struct device_node *controller,
const u32 *intspec,
unsigned int intsize);
+extern void irq_dispose_mapping(unsigned int irq);
extern int of_irq_to_resource(struct device_node *dev, int index,
struct resource *r);
extern int of_irq_count(struct device_node *dev);
@@ -70,6 +71,7 @@ extern int of_irq_to_resource_table(struct device_node *dev,
struct resource *res, int nr_irqs);
extern struct device_node *of_irq_find_parent(struct device_node *child);
+
#endif /* CONFIG_OF_IRQ */
#endif /* CONFIG_OF */
#endif /* __OF_IRQ_H */
diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile
index 7329005..aa8feed 100644
--- a/kernel/irq/Makefile
+++ b/kernel/irq/Makefile
@@ -1,5 +1,5 @@
-obj-y := irqdesc.o handle.o manage.o spurious.o resend.o chip.o dummychip.o devres.o
+obj-y := irqdesc.o handle.o manage.o spurious.o resend.o chip.o dummychip.o devres.o irqdomain.o
obj-$(CONFIG_GENERIC_IRQ_CHIP) += generic-chip.o
obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o
obj-$(CONFIG_PROC_FS) += proc.o
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
new file mode 100644
index 0000000..8310d6e
--- /dev/null
+++ b/kernel/irq/irqdomain.c
@@ -0,0 +1,137 @@
+
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+
+static LIST_HEAD(irq_domain_list);
+static DEFINE_MUTEX(irq_domain_mutex);
+
+/**
+ * irq_domain_add() - Register an irq_domain
+ * @domain: ptr to initialized irq_domain structure
+ *
+ * Registers an irq_domain structure. The irq_domain must at a minimum be
+ * initialized with an ops structure pointer, and either a ->to_irq hook or
+ * a valid irq_base value. Everything else is optional.
+ */
+void irq_domain_add(struct irq_domain *domain)
+{
+ mutex_lock(&irq_domain_mutex);
+ list_add(&domain->list, &irq_domain_list);
+ mutex_unlock(&irq_domain_mutex);
+}
+
+/**
+ * irq_domain_del() - Unregister an irq_domain
+ * @domain: ptr to registered irq_domain.
+ */
+void irq_domain_del(struct irq_domain *domain)
+{
+ mutex_lock(&irq_domain_mutex);
+ list_del(&domain->list);
+ mutex_unlock(&irq_domain_mutex);
+}
+
+/**
+ * irq_domain_map() - Allocate and/or increment a reference to a hwirq
+ *
+ * TODO: Establish a linux irq number mapping for a hardware irq. If the
+ * mapping already exists, then increment the reference count and return the
+ * linux irq number.
+ *
+ * At the moment this function is an empty stub since irq_domain initially
+ * only supports the common case of mapping hw irq numbers into a contiguous
+ * range of pre-allocated linux irq_descs based at irq_domain->irq_base. When
+ * irq_domains are extended either to support non-contiguous mappings (ie. to
+ * support MSI interrupts) or to remove preallocation of all irq_descs (as
+ * powerpc does so that irq_descs are only allocated for in-use irq inputs),
+ * then this function will be extended to implement the irq_desc allocation
+ * and reference counting.
+ *
+ * Any caller to this function must arrange to also call irq_domain_unmap()
+ * if the irq ever becomes unused again.
+ */
+unsigned int irq_domain_map(struct irq_domain *domain, irq_hw_number_t hwirq)
+{
+ int irq = irq_domain_to_irq(domain, hwirq);
+ struct irq_data *d = irq_get_irq_data(irq);
+
+ d->domain = domain;
+ d->hwirq = hwirq;
+
+ return irq;
+}
+
+/**
+ * irq_domain_unmap() - Release a reference to a hwirq
+ *
+ * TODO: decrement the reference count on a hardware irq number. If the ref
+ * count reaches zero, then the irq_desc can be freed.
+ *
+ * At the moment this function is an empty stub. See the comment on
+ * irq_domain_map() for details.
+ */
+void irq_domain_unmap(struct irq_domain *domain, irq_hw_number_t hwirq)
+{
+ int irq = irq_domain_to_irq(domain, hwirq);
+ struct irq_data *d = irq_get_irq_data(irq);
+
+ d->domain = NULL;
+}
+
+#if defined(CONFIG_OF_IRQ) && !defined(CONFIG_PPC)
+/**
+ * irq_create_of_mapping() - Map a linux irq number from a DT interrupt spec
+ *
+ * Used by the device tree interrupt mapping code to translate a device tree
+ * interrupt specifier to a valid linux irq number. Returns either a valid
+ * linux IRQ number or 0.
+ *
+ * When the caller no longer need the irq number returned by this function it
+ * should arrange to call irq_dispose_mapping().
+ */
+unsigned int irq_create_of_mapping(struct device_node *controller,
+ const u32 *intspec, unsigned int intsize)
+{
+ struct irq_domain *domain;
+ irq_hw_number_t hwirq;
+ unsigned int irq, type;
+ int rc = -ENODEV;
+
+ /* Find a domain which can translate the irq spec */
+ mutex_lock(&irq_domain_mutex);
+ list_for_each_entry(domain, &irq_domain_list, list) {
+ if (!domain->ops->dt_translate)
+ continue;
+ rc = domain->ops->dt_translate(domain, controller,
+ intspec, intsize, &hwirq, &type);
+ if (rc == 0)
+ break;
+ }
+ mutex_unlock(&irq_domain_mutex);
+
+ if (rc != 0)
+ return 0;
+
+ irq = irq_domain_map(domain, hwirq);
+ if (type != IRQ_TYPE_NONE)
+ irq_set_irq_type(irq, type);
+ return irq;
+}
+EXPORT_SYMBOL_GPL(irq_create_of_mapping);
+
+/**
+ * irq_dispose_mapping() - Discard a mapping created by irq_create_of_mapping()
+ * @irq: linux irq number to be discarded
+ *
+ * Calling this function indicates the caller no longer needs a reference to
+ * the linux irq number returned by a prior call to irq_create_of_mapping().
+ */
+void irq_dispose_mapping(unsigned int irq)
+{
+ struct irq_data *d = irq_get_irq_data(irq);
+ irq_domain_unmap(d->domain, d->hwirq);
+}
+EXPORT_SYMBOL_GPL(irq_dispose_mapping);
+
+#endif
of_find_matching_node_by_address() can be used to find a device tree
node for a device at a specific address.
Signed-off-by: Grant Likely <[email protected]>
---
drivers/of/address.c | 18 ++++++++++++++++++
include/linux/of_address.h | 4 ++++
2 files changed, 22 insertions(+), 0 deletions(-)
diff --git a/drivers/of/address.c b/drivers/of/address.c
index b4559c5..da1f4b9 100644
--- a/drivers/of/address.c
+++ b/drivers/of/address.c
@@ -577,6 +577,24 @@ int of_address_to_resource(struct device_node *dev, int index,
}
EXPORT_SYMBOL_GPL(of_address_to_resource);
+struct device_node *of_find_matching_node_by_address(struct device_node *from,
+ const struct of_device_id *matches,
+ u64 base_address)
+{
+ struct device_node *dn = of_find_matching_node(from, matches);
+ struct resource res;
+
+ while (dn) {
+ if (of_address_to_resource(dn, 0, &res))
+ continue;
+ if (res.start == base_address)
+ return dn;
+ dn = of_find_matching_node(dn, matches);
+ }
+
+ return NULL;
+}
+
/**
* of_iomap - Maps the memory mapped IO for a given device_node
diff --git a/include/linux/of_address.h b/include/linux/of_address.h
index 2feda6e..bdfc20d 100644
--- a/include/linux/of_address.h
+++ b/include/linux/of_address.h
@@ -6,6 +6,10 @@
extern u64 of_translate_address(struct device_node *np, const __be32 *addr);
extern int of_address_to_resource(struct device_node *dev, int index,
struct resource *r);
+extern struct device_node *of_find_matching_node_by_address(
+ struct device_node *from,
+ const struct of_device_id *matches,
+ u64 base_address);
extern void __iomem *of_iomap(struct device_node *device, int index);
/* Extract an address from a device, returns the region size and
irq_domain_add_simple() is an easy way to generate an irq translation
domain for simple irq controllers. It assumes a flat 1:1 mapping from
hardware irq number to an offset of the first linux irq number assigned
to the controller
Signed-off-by: Grant Likely <[email protected]>
---
include/linux/irq.h | 11 +++++++++
kernel/irq/irqdomain.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++-
2 files changed, 68 insertions(+), 1 deletions(-)
diff --git a/include/linux/irq.h b/include/linux/irq.h
index a103c01..50830fc 100644
--- a/include/linux/irq.h
+++ b/include/linux/irq.h
@@ -797,6 +797,17 @@ extern unsigned int irq_domain_map(struct irq_domain *domain,
irq_hw_number_t hwirq);
extern void irq_domain_unmap(struct irq_domain *domain, irq_hw_number_t hw);
+struct of_device_id;
+#ifdef CONFIG_OF
+extern void irq_domain_add_simple(struct device_node *controller, int irq_base);
+extern void irq_domain_generate_simple(struct of_device_id *match,
+ u64 phys_base, unsigned int irq_start);
+#else
+static inline void irq_domain_generate_simple(struct of_device_id *match,
+ u64 phys_base, unsigned int irq_start) { }
+#endif
+
+
#endif /* !CONFIG_S390 */
#endif /* _LINUX_IRQ_H */
diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
index 8310d6e..2d03c2d 100644
--- a/kernel/irq/irqdomain.c
+++ b/kernel/irq/irqdomain.c
@@ -1,7 +1,9 @@
-
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
static LIST_HEAD(irq_domain_list);
static DEFINE_MUTEX(irq_domain_mutex);
@@ -116,6 +118,8 @@ unsigned int irq_create_of_mapping(struct device_node *controller,
irq = irq_domain_map(domain, hwirq);
if (type != IRQ_TYPE_NONE)
irq_set_irq_type(irq, type);
+ pr_debug("%s: mapped hwirq=%i to irq=%i, flags=%x\n",
+ controller->full_name, (int)hwirq, irq, type);
return irq;
}
EXPORT_SYMBOL_GPL(irq_create_of_mapping);
@@ -134,4 +138,56 @@ void irq_dispose_mapping(unsigned int irq)
}
EXPORT_SYMBOL_GPL(irq_dispose_mapping);
+int irq_domain_simple_dt_translate(struct irq_domain *d,
+ struct device_node *controller,
+ const u32 *intspec, unsigned int intsize,
+ irq_hw_number_t *out_hwirq, unsigned int *out_type)
+{
+ if (d->of_node != controller)
+ return -EINVAL;
+ if (intsize != 1)
+ return -EINVAL;
+
+ *out_hwirq = intspec[0];
+ *out_type = IRQ_TYPE_NONE;
+ return 0;
+}
+
+struct irq_domain_ops irq_domain_simple_ops = {
+ .dt_translate = irq_domain_simple_dt_translate,
+};
+
+/**
+ * irq_domain_create_simple() - Set up a 'simple' translation range
+ */
+void irq_domain_add_simple(struct device_node *controller, int irq_base)
+{
+ struct irq_domain *domain;
+
+ domain = kzalloc(sizeof(*domain), GFP_KERNEL);
+ if (!domain) {
+ WARN_ON(1);
+ return;
+ }
+
+ domain->irq_base = irq_base;
+ domain->of_node = of_node_get(controller);
+ domain->ops = &irq_domain_simple_ops;
+ irq_domain_add(domain);
+}
+EXPORT_SYMBOL_GPL(irq_domain_add_simple);
+
+void irq_domain_generate_simple(struct of_device_id *match,
+ u64 phys_base, unsigned int irq_start)
+{
+ struct device_node *node;
+ pr_info("looking for phys_base=%llx, irq_start=%i\n",
+ (unsigned long long) phys_base, (int) irq_start);
+ node = of_find_matching_node_by_address(NULL, match, phys_base);
+ if (node)
+ irq_domain_add_simple(node, irq_start);
+ else
+ pr_info("no node found\n");
+}
+EXPORT_SYMBOL_GPL(irq_domain_generate_simple);
#endif
of_platform_populate() is similar to of_platform_bus_probe() except
that it strictly enforces that all device nodes must have a compatible
property, and it can be used to register devices (not buses) which are
children of the root node.
Signed-off-by: Grant Likely <[email protected]>
---
drivers/of/platform.c | 54 +++++++++++++++++++++++++++++++++++++++---
include/linux/of_platform.h | 3 ++
2 files changed, 53 insertions(+), 4 deletions(-)
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 63d3cb7..9b785be 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -221,19 +221,26 @@ EXPORT_SYMBOL(of_platform_device_create);
*/
static int of_platform_bus_create(struct device_node *bus,
const struct of_device_id *matches,
- struct device *parent)
+ struct device *parent, bool strict)
{
struct device_node *child;
struct platform_device *dev;
int rc = 0;
+ /* Make sure it has a compatible property */
+ if (strict && (!of_get_property(bus, "compatible", NULL))) {
+ pr_debug("%s() - skipping %s, no compatible prop\n",
+ __func__, bus->full_name);
+ return 0;
+ }
+
dev = of_platform_device_create(bus, NULL, parent);
if (!dev || !of_match_node(matches, bus))
return 0;
for_each_child_of_node(bus, child) {
pr_debug(" create child: %s\n", child->full_name);
- rc = of_platform_bus_create(child, matches, &dev->dev);
+ rc = of_platform_bus_create(child, matches, &dev->dev, strict);
if (rc) {
of_node_put(child);
break;
@@ -267,11 +274,11 @@ int of_platform_bus_probe(struct device_node *root,
/* Do a self check of bus type, if there's a match, create children */
if (of_match_node(matches, root)) {
- rc = of_platform_bus_create(root, matches, parent);
+ rc = of_platform_bus_create(root, matches, parent, false);
} else for_each_child_of_node(root, child) {
if (!of_match_node(matches, child))
continue;
- rc = of_platform_bus_create(child, matches, parent);
+ rc = of_platform_bus_create(child, matches, parent, false);
if (rc)
break;
}
@@ -280,4 +287,43 @@ int of_platform_bus_probe(struct device_node *root,
return rc;
}
EXPORT_SYMBOL(of_platform_bus_probe);
+
+/**
+ * of_platform_populate() - Populate platform_devices from device tree data
+ * @root: parent of the first level to probe or NULL for the root of the tree
+ * @matches: match table, NULL to use the default
+ * @parent: parent to hook devices from, NULL for toplevel
+ *
+ * Similar to of_platform_bus_probe(), this function walks the device tree
+ * and creates devices from nodes. It differs in that it follows the modern
+ * convention of requiring all device nodes to have a 'compatible' property,
+ * and it is suitable for creating devices which are children of the root
+ * node (of_platform_bus_probe will only create children of the root which
+ * are selected by the @matches argument).
+ *
+ * New board support should be using this function instead of
+ * of_platform_bus_probe().
+ *
+ * Returns 0 on success, < 0 on failure.
+ */
+int of_platform_populate(struct device_node *root,
+ const struct of_device_id *matches,
+ struct device *parent)
+{
+ struct device_node *child;
+ int rc = 0;
+
+ root = root ? of_node_get(root) : of_find_node_by_path("/");
+ if (!root)
+ return -EINVAL;
+
+ for_each_child_of_node(root, child) {
+ rc = of_platform_bus_create(child, matches, parent, true);
+ if (rc)
+ break;
+ }
+
+ of_node_put(root);
+ return rc;
+}
#endif /* !CONFIG_SPARC */
diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h
index fb51ae3..43c723d 100644
--- a/include/linux/of_platform.h
+++ b/include/linux/of_platform.h
@@ -55,6 +55,9 @@ extern struct platform_device *of_platform_device_create(struct device_node *np,
extern int of_platform_bus_probe(struct device_node *root,
const struct of_device_id *matches,
struct device *parent);
+extern int of_platform_populate(struct device_node *root,
+ const struct of_device_id *matches,
+ struct device *parent);
#endif /* !CONFIG_SPARC */
#endif /* CONFIG_OF_DEVICE */
No need for most platforms to define their own bus table when calling
of_platform_populate(). Supply a stock one.
Signed-off-by: Grant Likely <[email protected]>
---
drivers/of/platform.c | 8 ++++++++
include/linux/of_platform.h | 2 ++
2 files changed, 10 insertions(+), 0 deletions(-)
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 9b785be..1f4a5d3 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -22,6 +22,14 @@
#include <linux/of_platform.h>
#include <linux/platform_device.h>
+const struct of_device_id of_default_bus_match_table[] = {
+ { .compatible = "simple-bus", },
+#ifdef CONFIG_ARM_AMBA
+ { .compatible = "arm,amba-bus", },
+#endif /* CONFIG_ARM_AMBA */
+ {} /* Empty terminated list */
+};
+
static int of_dev_node_match(struct device *dev, void *data)
{
return dev->of_node == data;
diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h
index 43c723d..8390233 100644
--- a/include/linux/of_platform.h
+++ b/include/linux/of_platform.h
@@ -40,6 +40,8 @@ struct of_platform_driver
#define to_of_platform_driver(drv) \
container_of(drv,struct of_platform_driver, driver)
+extern const struct of_device_id of_default_bus_match_table[];
+
/* Platform drivers register/unregister */
extern struct platform_device *of_device_alloc(struct device_node *np,
const char *bus_id,
Add a function to create amba_devices (i.e. primecell peripherals)
from device tree nodes. The device tree scanning is done by the
of_platform_populate() function which can call of_amba_device_create
based on a match table entry.
Nodes with a "arm,primecell-periphid" property can override the h/w
peripheral id value.
Based on the original work by Jeremy Kerr.
Signed-off-by: Jeremy Kerr <[email protected]>
Acked-by: Linus Walleij <[email protected]>
Signed-off-by: Rob Herring <[email protected]>
Reviewed-by: Arnd Bergmann <[email protected]>
[grant.likely: add Jeremy's original s-o-b line, changes from review
comments, and moved all code to drivers/of/platform.c]
Signed-off-by: Grant Likely <[email protected]>
---
.../devicetree/bindings/arm/primecell.txt | 21 ++++++
drivers/of/platform.c | 71 ++++++++++++++++++++
2 files changed, 92 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/arm/primecell.txt
diff --git a/Documentation/devicetree/bindings/arm/primecell.txt b/Documentation/devicetree/bindings/arm/primecell.txt
new file mode 100644
index 0000000..1d5d7a8
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/primecell.txt
@@ -0,0 +1,21 @@
+* ARM Primecell Peripherals
+
+ARM, Ltd. Primecell peripherals have a standard id register that can be used to
+identify the peripheral type, vendor, and revision. This value can be used for
+driver matching.
+
+Required properties:
+
+- compatible : should be a specific value for peripheral and "arm,primecell"
+
+Optional properties:
+
+- arm,primecell-periphid : Value to override the h/w value with
+
+Example:
+
+serial@fff36000 {
+ compatible = "arm,pl011", "arm,primecell";
+ arm,primecell-periphid = <0x00341011>;
+};
+
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 1f4a5d3..4192ddc 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -13,6 +13,7 @@
*/
#include <linux/errno.h>
#include <linux/module.h>
+#include <linux/amba/bus.h>
#include <linux/device.h>
#include <linux/dma-mapping.h>
#include <linux/slab.h>
@@ -217,6 +218,71 @@ struct platform_device *of_platform_device_create(struct device_node *np,
}
EXPORT_SYMBOL(of_platform_device_create);
+#ifdef CONFIG_ARM_AMBA
+static struct amba_device *of_amba_device_create(struct device_node *node,
+ const char *bus_id,
+ void *platform_data,
+ struct device *parent)
+{
+ struct amba_device *dev;
+ const void *prop;
+ int i, ret;
+
+ pr_debug("Creating amba device %s\n", node->full_name);
+
+ if (!of_device_is_available(node))
+ return NULL;
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return NULL;
+
+ /* setup generic device info */
+ dev->dev.coherent_dma_mask = ~0;
+ dev->dev.of_node = of_node_get(node);
+ dev->dev.parent = parent;
+ dev->dev.platform_data = platform_data;
+ if (bus_id)
+ dev_set_name(&dev->dev, "%s", bus_id);
+ else
+ of_device_make_bus_id(&dev->dev);
+
+ /* setup amba-specific device info */
+ dev->dma_mask = ~0;
+
+ /* Allow the HW Peripheral ID to be overridden */
+ prop = of_get_property(node, "arm,primecell-periphid", NULL);
+ if (prop)
+ dev->periphid = of_read_ulong(prop, 1);
+
+ /* Decode the IRQs and address ranges */
+ for (i = 0; i < AMBA_NR_IRQS; i++)
+ dev->irq[i] = irq_of_parse_and_map(node, i);
+
+ ret = of_address_to_resource(node, 0, &dev->res);
+ if (ret)
+ goto err_free;
+
+ ret = amba_device_register(dev, &iomem_resource);
+ if (ret)
+ goto err_free;
+
+ return dev;
+
+err_free:
+ kfree(dev);
+ return NULL;
+}
+#else /* CONFIG_ARM_AMBA */
+static struct amba_device *of_amba_device_create(struct device_node *node,
+ const char *bus_id,
+ void *platform_data,
+ struct device *parent)
+{
+ return NULL;
+}
+#endif /* CONFIG_ARM_AMBA */
+
/**
* of_platform_bus_create() - Create a device for a node and its children.
* @bus: device node of the bus to instantiate
@@ -242,6 +308,11 @@ static int of_platform_bus_create(struct device_node *bus,
return 0;
}
+ if (of_device_is_compatible(bus, "arm,primecell")) {
+ of_amba_device_create(bus, NULL, NULL, parent);
+ return 0;
+ }
+
dev = of_platform_device_create(bus, NULL, parent);
if (!dev || !of_match_node(matches, bus))
return 0;
Some platform code has specific requirements on the naming of devices.
This patch allows callers of of_platform_populate() to provide a
device name lookup table.
Signed-off-by: Grant Likely <[email protected]>
---
drivers/of/platform.c | 73 +++++++++++++++++++++++++++++++++++++------
include/linux/of_platform.h | 35 ++++++++++++++++++++
2 files changed, 98 insertions(+), 10 deletions(-)
diff --git a/drivers/of/platform.c b/drivers/of/platform.c
index 4192ddc..e75af39 100644
--- a/drivers/of/platform.c
+++ b/drivers/of/platform.c
@@ -177,17 +177,20 @@ struct platform_device *of_device_alloc(struct device_node *np,
EXPORT_SYMBOL(of_device_alloc);
/**
- * of_platform_device_create - Alloc, initialize and register an of_device
+ * of_platform_device_create_pdata - Alloc, initialize and register an of_device
* @np: pointer to node to create device for
* @bus_id: name to assign device
+ * @platform_data: pointer to populate platform_data pointer with
* @parent: Linux device model parent device.
*
* Returns pointer to created platform device, or NULL if a device was not
* registered. Unavailable devices will not get registered.
*/
-struct platform_device *of_platform_device_create(struct device_node *np,
- const char *bus_id,
- struct device *parent)
+struct platform_device *of_platform_device_create_pdata(
+ struct device_node *np,
+ const char *bus_id,
+ void *platform_data,
+ struct device *parent)
{
struct platform_device *dev;
@@ -203,6 +206,7 @@ struct platform_device *of_platform_device_create(struct device_node *np,
#endif
dev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
dev->dev.bus = &platform_bus_type;
+ dev->dev.platform_data = platform_data;
/* We do not fill the DMA ops for platform devices by default.
* This is currently the responsibility of the platform code
@@ -216,6 +220,22 @@ struct platform_device *of_platform_device_create(struct device_node *np,
return dev;
}
+
+/**
+ * of_platform_device_create - Alloc, initialize and register an of_device
+ * @np: pointer to node to create device for
+ * @bus_id: name to assign device
+ * @parent: Linux device model parent device.
+ *
+ * Returns pointer to created platform device, or NULL if a device was not
+ * registered. Unavailable devices will not get registered.
+ */
+struct platform_device *of_platform_device_create(struct device_node *np,
+ const char *bus_id,
+ struct device *parent)
+{
+ return of_platform_device_create_pdata(np, bus_id, NULL, parent);
+}
EXPORT_SYMBOL(of_platform_device_create);
#ifdef CONFIG_ARM_AMBA
@@ -284,6 +304,28 @@ static struct amba_device *of_amba_device_create(struct device_node *node,
#endif /* CONFIG_ARM_AMBA */
/**
+ * of_devname_lookup() - Given a device node, lookup the preferred Linux name
+ */
+static const struct of_dev_auxdata *of_dev_lookup(const struct of_dev_auxdata *lookup,
+ struct device_node *np)
+{
+ struct resource res;
+ if (lookup) {
+ for(; lookup->name != NULL; lookup++) {
+ if (!of_device_is_compatible(np, lookup->compatible))
+ continue;
+ if (of_address_to_resource(np, 0, &res))
+ continue;
+ if (res.start != lookup->phys_addr)
+ continue;
+ pr_debug("%s: devname=%s\n", np->full_name, lookup->name);
+ return lookup;
+ }
+ }
+ return NULL;
+}
+
+/**
* of_platform_bus_create() - Create a device for a node and its children.
* @bus: device node of the bus to instantiate
* @matches: match table for bus nodes
@@ -295,10 +337,14 @@ static struct amba_device *of_amba_device_create(struct device_node *node,
*/
static int of_platform_bus_create(struct device_node *bus,
const struct of_device_id *matches,
+ const struct of_dev_auxdata *lookup,
struct device *parent, bool strict)
{
+ const struct of_dev_auxdata *auxdata;
struct device_node *child;
struct platform_device *dev;
+ const char *bus_id = NULL;
+ void *platform_data = NULL;
int rc = 0;
/* Make sure it has a compatible property */
@@ -308,18 +354,24 @@ static int of_platform_bus_create(struct device_node *bus,
return 0;
}
+ auxdata = of_dev_lookup(lookup, bus);
+ if (auxdata) {
+ bus_id = auxdata->name;
+ platform_data = auxdata->platform_data;
+ }
+
if (of_device_is_compatible(bus, "arm,primecell")) {
- of_amba_device_create(bus, NULL, NULL, parent);
+ of_amba_device_create(bus, bus_id, platform_data, parent);
return 0;
}
- dev = of_platform_device_create(bus, NULL, parent);
+ dev = of_platform_device_create_pdata(bus, bus_id, platform_data, parent);
if (!dev || !of_match_node(matches, bus))
return 0;
for_each_child_of_node(bus, child) {
pr_debug(" create child: %s\n", child->full_name);
- rc = of_platform_bus_create(child, matches, &dev->dev, strict);
+ rc = of_platform_bus_create(child, matches, lookup, &dev->dev, strict);
if (rc) {
of_node_put(child);
break;
@@ -353,11 +405,11 @@ int of_platform_bus_probe(struct device_node *root,
/* Do a self check of bus type, if there's a match, create children */
if (of_match_node(matches, root)) {
- rc = of_platform_bus_create(root, matches, parent, false);
+ rc = of_platform_bus_create(root, matches, NULL, parent, false);
} else for_each_child_of_node(root, child) {
if (!of_match_node(matches, child))
continue;
- rc = of_platform_bus_create(child, matches, parent, false);
+ rc = of_platform_bus_create(child, matches, NULL, parent, false);
if (rc)
break;
}
@@ -387,6 +439,7 @@ EXPORT_SYMBOL(of_platform_bus_probe);
*/
int of_platform_populate(struct device_node *root,
const struct of_device_id *matches,
+ const struct of_dev_auxdata *lookup,
struct device *parent)
{
struct device_node *child;
@@ -397,7 +450,7 @@ int of_platform_populate(struct device_node *root,
return -EINVAL;
for_each_child_of_node(root, child) {
- rc = of_platform_bus_create(child, matches, parent, true);
+ rc = of_platform_bus_create(child, matches, lookup, parent, true);
if (rc)
break;
}
diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h
index 8390233..5a6f458 100644
--- a/include/linux/of_platform.h
+++ b/include/linux/of_platform.h
@@ -20,6 +20,40 @@
#include <linux/platform_device.h>
/**
+ * struct of_dev_auxdata - lookup table entry for device names & platform_data
+ * @compatible: compatible value of node to match against node
+ * @phys_addr: Start address of registers to match against node
+ * @name: Name to assign for matching nodes
+ * @platform_data: platform_data to assign for matching nodes
+ *
+ * This lookup table allows the caller of of_platform_populate() to override
+ * the names of devices when creating devices from the device tree. The table
+ * should be terminated with an empty entry. It also allows the platform_data
+ * pointer to be set.
+ *
+ * The reason for this functionality is that some Linux infrastructure uses
+ * the device name to look up a specific device, but the Linux-specific names
+ * are not encoded into the device tree, so the kernel needs to provide specific
+ * values.
+ *
+ * Note: Using an auxdata lookup table should be considered a last resort when
+ * converting a platform to use the DT. Normally the automatically generated
+ * device name will not matter, and drivers should obtain data from the device
+ * node instead of from an anonymouns platform_data pointer.
+ */
+struct of_dev_auxdata {
+ char *compatible;
+ resource_size_t phys_addr;
+ char *name;
+ void *platform_data;
+};
+
+/* Macro to simplify populating a lookup table */
+#define OF_DEV_AUXDATA(_compat,_phys,_name,_pdata) \
+ { .compatible = _compat, .phys_addr = _phys, .name = _name, \
+ .platform_data = _pdata }
+
+/**
* of_platform_driver - Legacy of-aware driver for platform devices.
*
* An of_platform_driver driver is attached to a basic platform_device on
@@ -59,6 +93,7 @@ extern int of_platform_bus_probe(struct device_node *root,
struct device *parent);
extern int of_platform_populate(struct device_node *root,
const struct of_device_id *matches,
+ const struct of_dev_auxdata *lookup,
struct device *parent);
#endif /* !CONFIG_SPARC */
of_platform_populate() also handles nodes at the root of the tree,
which is wanted for things like describing the sound complex. This
patch converts mpc5200 support to use of_platform_populate() instead
of of_platform_bus_probe().
Signed-off-by: Grant Likely <[email protected]>
---
arch/powerpc/platforms/52xx/mpc52xx_common.c | 10 ++++------
1 files changed, 4 insertions(+), 6 deletions(-)
diff --git a/arch/powerpc/platforms/52xx/mpc52xx_common.c b/arch/powerpc/platforms/52xx/mpc52xx_common.c
index 41f3a7e..6a2fde2 100644
--- a/arch/powerpc/platforms/52xx/mpc52xx_common.c
+++ b/arch/powerpc/platforms/52xx/mpc52xx_common.c
@@ -97,13 +97,11 @@ struct mpc52xx_gpio_wkup __iomem *wkup_gpio;
* of the localplus bus to the of_platform
* bus.
*/
-void __init
-mpc52xx_declare_of_platform_devices(void)
+void __init mpc52xx_declare_of_platform_devices(void)
{
- /* Find every child of the SOC node and add it to of_platform */
- if (of_platform_bus_probe(NULL, mpc52xx_bus_ids, NULL))
- printk(KERN_ERR __FILE__ ": "
- "Error while probing of_platform bus\n");
+ /* Find all the 'platform' devices and register them. */
+ if (of_platform_populate(NULL, mpc52xx_bus_ids, NULL, NULL))
+ pr_err(__FILE__ ": Error while populating devices from DT\n");
}
/*
This patch adds a DT_MACHINE_START macro to use instead of
MACHINE_START when creating a machine_desc that supports using the
device tree.
Signed-off-by: Grant Likely <[email protected]>
---
arch/arm/include/asm/mach/arch.h | 9 +++++++++
1 files changed, 9 insertions(+), 0 deletions(-)
diff --git a/arch/arm/include/asm/mach/arch.h b/arch/arm/include/asm/mach/arch.h
index 946f4d7..858d269 100644
--- a/arch/arm/include/asm/mach/arch.h
+++ b/arch/arm/include/asm/mach/arch.h
@@ -70,4 +70,13 @@ static const struct machine_desc __mach_desc_##_type \
#define MACHINE_END \
};
+#define MACH_TYPE_DT 0xffffffff
+
+#define DT_MACHINE_START(_name, _namestr) \
+static const struct machine_desc __mach_desc_##_name \
+ __used \
+ __attribute__((__section__(".arch.info.init"))) = { \
+ .nr = MACH_TYPE_DT, \
+ .name = _namestr,
+
#endif
Contains the bare minimum template required to boot with the device tree.
Signed-off-by: Grant Likely <[email protected]>
---
arch/arm/boot/dts/skeleton.dtsi | 13 +++++++++++++
1 files changed, 13 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/boot/dts/skeleton.dtsi
diff --git a/arch/arm/boot/dts/skeleton.dtsi b/arch/arm/boot/dts/skeleton.dtsi
new file mode 100644
index 0000000..b41d241
--- /dev/null
+++ b/arch/arm/boot/dts/skeleton.dtsi
@@ -0,0 +1,13 @@
+/*
+ * Skeleton device tree; the bare minimum needed to boot; just include and
+ * add a compatible value. The bootloader will typically populate the memory
+ * node.
+ */
+
+/ {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ chosen { };
+ aliases { };
+ memory { device_type = "memory"; reg = <0 0>; };
+};
For testing the dt work, define a dt-enabled versatile platform.
This patch adds a new versatile platform for when using the device
tree. Add platform and amba devices are discovered and registered by
parsing the device tree. Clocks and initial io mappings are still
configured statically.
This patch is definitely not complete since a few of the device
drivers depend on static platform_data in order to initialize
correctly. The next step will be to fix up the breakage one driver at
a time. However, this patch leaves existing board support files
alone, so nothing should be broken.
Signed-off-by: Grant Likely <[email protected]>
---
arch/arm/boot/dts/versatile-ab.dts | 185 ++++++++++++++++++++++++++++++++
arch/arm/boot/dts/versatile-pb.dts | 48 ++++++++
arch/arm/mach-versatile/Kconfig | 8 +
arch/arm/mach-versatile/Makefile | 1
arch/arm/mach-versatile/core.c | 61 +++++++++++
arch/arm/mach-versatile/core.h | 5 +
arch/arm/mach-versatile/versatile_dt.c | 66 +++++++++++
7 files changed, 374 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/boot/dts/versatile-ab.dts
create mode 100644 arch/arm/boot/dts/versatile-pb.dts
create mode 100644 arch/arm/mach-versatile/versatile_dt.c
diff --git a/arch/arm/boot/dts/versatile-ab.dts b/arch/arm/boot/dts/versatile-ab.dts
new file mode 100644
index 0000000..0e31dc9
--- /dev/null
+++ b/arch/arm/boot/dts/versatile-ab.dts
@@ -0,0 +1,185 @@
+/dts-v1/;
+/include/ "skeleton.dtsi"
+
+/ {
+ model = "ARM Versatile AB";
+ compatible = "arm,versatile-ab";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ interrupt-parent = <&vic>;
+
+ memory {
+ reg = <0x0 0x08000000>;
+ };
+
+ flash@34000000 {
+ compatible = "arm,versatile-flash";
+ reg = <0x34000000 0x4000000>;
+ bank-width = <4>;
+ };
+
+ i2c@10002000 {
+ #address-cells = <1>;
+ #size-cells = <0>;
+ compatible = "arm,versatile-i2c";
+ reg = <0x10002000 0x1000>;
+
+ rtc@68 {
+ compatible = "dallas,ds1338";
+ reg = <0x68>;
+ };
+ };
+
+ net@10010000 {
+ compatible = "smsc,lan91c111";
+ reg = <0x10010000 0x10000>;
+ interrupts = <25>;
+ };
+
+ lcd@10008000 {
+ compatible = "arm,versatile-lcd";
+ reg = <0x10008000 0x1000>;
+ };
+
+ amba {
+ compatible = "arm,amba-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ vic: intc@10140000 {
+ compatible = "arm,versatile-vic", "arm,vic";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ reg = <0x10140000 0x1000>;
+ };
+
+ sic: intc@10003000 {
+ compatible = "arm,versatile-sic", "arm,sic";
+ interrupt-controller;
+ #interrupt-cells = <1>;
+ reg = <0x10003000 0x1000>;
+ interrupt-parent = <&vic>;
+ interrupts = <31>; /* Cascaded to vic */
+ };
+
+ dma@10130000 {
+ compatible = "arm,primecell";
+ reg = <0x10130000 0x1000>;
+ interrupts = <17>;
+ };
+
+ uart0: uart@101f1000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0x101f1000 0x1000>;
+ interrupts = <12>;
+ };
+
+ uart1: uart@101f2000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0x101f2000 0x1000>;
+ interrupts = <13>;
+ };
+
+ uart2: uart@101f3000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0x101f3000 0x1000>;
+ interrupts = <14>;
+ };
+
+ smc@10100000 {
+ compatible = "arm,primecell";
+ reg = <0x10100000 0x1000>;
+ };
+
+ mpmc@10110000 {
+ compatible = "arm,primecell";
+ reg = <0x10110000 0x1000>;
+ };
+
+ display@10120000 {
+ compatible = "arm,pl110", "arm,primecell";
+ reg = <0x10120000 0x1000>;
+ interrupts = <16>;
+ };
+
+ sctl@101e0000 {
+ compatible = "arm,primecell";
+ reg = <0x101e0000 0x1000>;
+ };
+
+ watchdog@101e1000 {
+ compatible = "arm,primecell";
+ reg = <0x101e1000 0x1000>;
+ interrupts = <0>;
+ };
+
+ gpio0: gpio@101e4000 {
+ compatible = "arm,primecell";
+ reg = <0x101e4000 0x1000>;
+ gpio-controller;
+ interrupts = <6>;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio1: gpio@101e5000 {
+ compatible = "arm,primecell";
+ reg = <0x101e5000 0x1000>;
+ interrupts = <7>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ rtc@101e8000 {
+ compatible = "arm,primecell";
+ reg = <0x101e8000 0x1000>;
+ interrupts = <10>;
+ };
+
+ sci@101f0000 {
+ compatible = "arm,primecell";
+ reg = <0x101f0000 0x1000>;
+ interrupts = <15>;
+ };
+
+ ssp@101f4000 {
+ compatible = "arm,primecell";
+ reg = <0x101f4000 0x1000>;
+ interrupts = <11>;
+ };
+
+ fpga {
+ compatible = "arm,versatile-fpga", "simple-bus";
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges = <0 0x10000000 0x10000>;
+
+ aaci@4000 {
+ compatible = "arm,primecell";
+ reg = <0x4000 0x1000>;
+ interrupts = <24>;
+ };
+ mmc@5000 {
+ compatible = "arm,primecell";
+ reg = < 0x5000 0x1000>;
+ interrupts = <22>;
+ };
+ kmi@6000 {
+ compatible = "arm,pl050", "arm,primecell";
+ reg = <0x6000 0x1000>;
+ interrupt-parent = <&sic>;
+ interrupts = <3>;
+ };
+ kmi@7000 {
+ compatible = "arm,pl050", "arm,primecell";
+ reg = <0x7000 0x1000>;
+ interrupt-parent = <&sic>;
+ interrupts = <4>;
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/versatile-pb.dts b/arch/arm/boot/dts/versatile-pb.dts
new file mode 100644
index 0000000..d1934af
--- /dev/null
+++ b/arch/arm/boot/dts/versatile-pb.dts
@@ -0,0 +1,48 @@
+/include/ "versatile-ab.dts"
+
+/ {
+ model = "ARM Versatile PB";
+ compatible = "arm,versatile-pb";
+
+ amba {
+ gpio2: gpio@101e6000 {
+ compatible = "arm,primecell";
+ reg = <0x101e6000 0x1000>;
+ interrupts = <8>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ gpio3: gpio@101e7000 {
+ compatible = "arm,primecell";
+ reg = <0x101e7000 0x1000>;
+ interrupts = <9>;
+ gpio-controller;
+ #gpio-cells = <2>;
+ interrupt-controller;
+ #interrupt-cells = <2>;
+ };
+
+ fpga {
+ uart@9000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0x9000 0x1000>;
+ interrupt-parent = <&sic>;
+ interrupts = <6>;
+ };
+ sci@a000 {
+ compatible = "arm,primecell";
+ reg = <0xa000 0x1000>;
+ interrupt-parent = <&sic>;
+ interrupts = <5>;
+ };
+ mmc@b000 {
+ compatible = "arm,primecell";
+ reg = <0xb000 0x1000>;
+ interrupts = <23>;
+ };
+ };
+ };
+};
diff --git a/arch/arm/mach-versatile/Kconfig b/arch/arm/mach-versatile/Kconfig
index 9cdec5a..c1f38f6 100644
--- a/arch/arm/mach-versatile/Kconfig
+++ b/arch/arm/mach-versatile/Kconfig
@@ -17,4 +17,12 @@ config MACH_VERSATILE_AB
Include support for the ARM(R) Versatile Application Baseboard
for the ARM926EJ-S.
+config MACH_VERSATILE_DT
+ bool "Support Versatile platform from device tree"
+ select USE_OF
+ select CPU_ARM926T
+ help
+ Include support for the ARM(R) Versatile/PB platform,
+ using the device tree for discovery
+
endmenu
diff --git a/arch/arm/mach-versatile/Makefile b/arch/arm/mach-versatile/Makefile
index 97cf4d8..81fa3fe 100644
--- a/arch/arm/mach-versatile/Makefile
+++ b/arch/arm/mach-versatile/Makefile
@@ -5,4 +5,5 @@
obj-y := core.o
obj-$(CONFIG_ARCH_VERSATILE_PB) += versatile_pb.o
obj-$(CONFIG_MACH_VERSATILE_AB) += versatile_ab.o
+obj-$(CONFIG_MACH_VERSATILE_DT) += versatile_dt.o
obj-$(CONFIG_PCI) += pci.o
diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c
index 0c99cf0..80e010b 100644
--- a/arch/arm/mach-versatile/core.c
+++ b/arch/arm/mach-versatile/core.c
@@ -24,6 +24,8 @@
#include <linux/platform_device.h>
#include <linux/sysdev.h>
#include <linux/interrupt.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
#include <linux/amba/bus.h>
#include <linux/amba/clcd.h>
#include <linux/amba/pl061.h>
@@ -83,13 +85,26 @@ static struct fpga_irq_data sic_irq = {
#define PIC_MASK 0
#endif
+/* Lookup table for finding a DT node that represents the vic instance */
+static struct of_device_id vic_of_match[] __initdata = {
+ { .compatible = "arm,vic", },
+ {}
+};
+
+static struct of_device_id sic_of_match[] __initdata = {
+ { .compatible = "arm,sic", },
+ {}
+};
+
void __init versatile_init_irq(void)
{
vic_init(VA_VIC_BASE, IRQ_VIC_START, ~0, 0);
+ irq_domain_generate_simple(vic_of_match, VERSATILE_VIC_BASE, IRQ_VIC_START);
writel(~0, VA_SIC_BASE + SIC_IRQ_ENABLE_CLEAR);
fpga_irq_init(IRQ_VICSOURCE31, ~PIC_MASK, &sic_irq);
+ irq_domain_generate_simple(sic_of_match, VERSATILE_SIC_BASE, IRQ_SIC_START);
/*
* Interrupts on secondary controller from 0 to 8 are routed to
@@ -646,6 +661,52 @@ static struct amba_device *amba_devs[] __initdata = {
&kmi1_device,
};
+#ifdef CONFIG_OF
+/*
+ * Lookup table for attaching a specific name and platform_data pointer to
+ * devices as they get created by of_platform_populate(). Ideally this table
+ * would not exist, but the current clock implementation depends on some devices
+ * having a specific name.
+ */
+struct of_dev_auxdata versatile_auxdata_lookup[] __initdata = {
+ OF_DEV_AUXDATA("arm,primecell", VERSATILE_MMCI0_BASE, "fpga:05", NULL),
+ OF_DEV_AUXDATA("arm,primecell", VERSATILE_KMI0_BASE, "fpga:06", NULL),
+ OF_DEV_AUXDATA("arm,primecell", VERSATILE_KMI1_BASE, "fpga:07", NULL),
+ OF_DEV_AUXDATA("arm,primecell", VERSATILE_UART3_BASE, "fpga:09", NULL),
+ OF_DEV_AUXDATA("arm,primecell", VERSATILE_MMCI1_BASE, "fpga:0b", NULL),
+
+ OF_DEV_AUXDATA("arm,primecell", VERSATILE_CLCD_BASE, "dev:20", &clcd_plat_data),
+ OF_DEV_AUXDATA("arm,primecell", VERSATILE_UART0_BASE, "dev:f1", NULL),
+ OF_DEV_AUXDATA("arm,primecell", VERSATILE_UART1_BASE, "dev:f2", NULL),
+ OF_DEV_AUXDATA("arm,primecell", VERSATILE_UART2_BASE, "dev:f3", NULL),
+ OF_DEV_AUXDATA("arm,primecell", VERSATILE_SSP_BASE, "dev:f4", NULL),
+
+#if 0
+ /*
+ * These entries are unnecessary because no clocks referencing
+ * them. I've left them in for now as place holders in case
+ * any of them need to be added back, but they should be
+ * removed before actually committing this patch. --gcl
+ */
+ OF_DEV_AUXDATA("arm,primecell", VERSATILE_AACI_BASE, "fpga:04", NULL),
+ OF_DEV_AUXDATA("arm,primecell", VERSATILE_SCI1_BASE, "fpga:0a", NULL),
+ OF_DEV_AUXDATA("arm,primecell", VERSATILE_SMC_BASE, "dev:00", NULL),
+ OF_DEV_AUXDATA("arm,primecell", VERSATILE_MPMC_BASE, "dev:10", NULL),
+ OF_DEV_AUXDATA("arm,primecell", VERSATILE_DMAC_BASE, "dev:30", NULL),
+
+ OF_DEV_AUXDATA("arm,primecell", VERSATILE_SCTL_BASE, "dev:e0", NULL),
+ OF_DEV_AUXDATA("arm,primecell", VERSATILE_WATCHDOG_BASE, "dev:e1", NULL),
+ OF_DEV_AUXDATA("arm,primecell", VERSATILE_GPIO0_BASE, "dev:e4", NULL),
+ OF_DEV_AUXDATA("arm,primecell", VERSATILE_GPIO1_BASE, "dev:e5", NULL),
+ OF_DEV_AUXDATA("arm,primecell", VERSATILE_GPIO2_BASE, "dev:e6", NULL),
+ OF_DEV_AUXDATA("arm,primecell", VERSATILE_GPIO3_BASE, "dev:e7", NULL),
+ OF_DEV_AUXDATA("arm,primecell", VERSATILE_RTC_BASE, "dev:e8", NULL),
+ OF_DEV_AUXDATA("arm,primecell", VERSATILE_SCI_BASE, "dev:f0", NULL),
+#endif
+ {}
+};
+#endif
+
#ifdef CONFIG_LEDS
#define VA_LEDS_BASE (__io_address(VERSATILE_SYS_BASE) + VERSATILE_SYS_LED_OFFSET)
diff --git a/arch/arm/mach-versatile/core.h b/arch/arm/mach-versatile/core.h
index fd6404e..c97b070 100644
--- a/arch/arm/mach-versatile/core.h
+++ b/arch/arm/mach-versatile/core.h
@@ -23,6 +23,7 @@
#define __ASM_ARCH_VERSATILE_H
#include <linux/amba/bus.h>
+#include <linux/of_platform.h>
extern void __init versatile_init(void);
extern void __init versatile_init_early(void);
@@ -31,6 +32,10 @@ extern void __init versatile_map_io(void);
extern struct sys_timer versatile_timer;
extern unsigned int mmc_status(struct device *dev);
+#ifdef CONFIG_OF
+extern struct of_dev_auxdata versatile_auxdata_lookup[];
+#endif
+
#define AMBA_DEVICE(name,busid,base,plat) \
static struct amba_device name##_device = { \
.dev = { \
diff --git a/arch/arm/mach-versatile/versatile_dt.c b/arch/arm/mach-versatile/versatile_dt.c
new file mode 100644
index 0000000..f476a11
--- /dev/null
+++ b/arch/arm/mach-versatile/versatile_dt.c
@@ -0,0 +1,66 @@
+/*
+ * Versatile board support using the device tree
+ *
+ * Copyright (C) 2010 Secret Lab Technologies Ltd.
+ * Copyright (C) 2009 Jeremy Kerr <[email protected]>
+ * Copyright (C) 2004 ARM Limited
+ * Copyright (C) 2000 Deep Blue Solutions Ltd
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/sysdev.h>
+#include <linux/clk.h>
+#include <linux/clkdev.h>
+#include <linux/amba/bus.h>
+#include <linux/amba/pl061.h>
+#include <linux/amba/mmci.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_fdt.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+
+#include <mach/hardware.h>
+#include <asm/irq.h>
+#include <asm/mach-types.h>
+
+#include <asm/mach/arch.h>
+
+#include "core.h"
+
+static void __init versatile_dt_init(void)
+{
+ of_platform_populate(NULL, of_default_bus_match_table,
+ versatile_auxdata_lookup, NULL);
+}
+
+static const char *versatile_dt_match[] __initdata = {
+ "arm,versatile-ab",
+ "arm,versatile-pb",
+ NULL,
+};
+
+DT_MACHINE_START(VERSATILE_PB, "ARM-Versatile (Device Tree Support)")
+ .map_io = versatile_map_io,
+ .init_early = versatile_init_early,
+ .init_irq = versatile_init_irq,
+ .timer = &versatile_timer,
+ .init_machine = versatile_dt_init,
+ .dt_compat = versatile_dt_match,
+MACHINE_END
Grant,
On 06/15/2011 11:41 PM, Grant Likely wrote:
> irq_domain_add_simple() is an easy way to generate an irq translation
> domain for simple irq controllers. It assumes a flat 1:1 mapping from
> hardware irq number to an offset of the first linux irq number assigned
> to the controller
>
> Signed-off-by: Grant Likely <[email protected]>
> ---
> include/linux/irq.h | 11 +++++++++
> kernel/irq/irqdomain.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++-
> 2 files changed, 68 insertions(+), 1 deletions(-)
>
> diff --git a/include/linux/irq.h b/include/linux/irq.h
> index a103c01..50830fc 100644
> --- a/include/linux/irq.h
> +++ b/include/linux/irq.h
> @@ -797,6 +797,17 @@ extern unsigned int irq_domain_map(struct irq_domain *domain,
> irq_hw_number_t hwirq);
> extern void irq_domain_unmap(struct irq_domain *domain, irq_hw_number_t hw);
>
> +struct of_device_id;
> +#ifdef CONFIG_OF
> +extern void irq_domain_add_simple(struct device_node *controller, int irq_base);
> +extern void irq_domain_generate_simple(struct of_device_id *match,
match should be const
> + u64 phys_base, unsigned int irq_start);
> +#else
Empty function for irq_domain_add_simple?
> +static inline void irq_domain_generate_simple(struct of_device_id *match,
> + u64 phys_base, unsigned int irq_start) { }
> +#endif
> +
> +
Rob
Grant,
On 06/15/2011 11:43 PM, Grant Likely wrote:
> For testing the dt work, define a dt-enabled versatile platform.
>
> This patch adds a new versatile platform for when using the device
> tree. Add platform and amba devices are discovered and registered by
> parsing the device tree. Clocks and initial io mappings are still
> configured statically.
>
> This patch is definitely not complete since a few of the device
> drivers depend on static platform_data in order to initialize
> correctly. The next step will be to fix up the breakage one driver at
> a time. However, this patch leaves existing board support files
> alone, so nothing should be broken.
>
> Signed-off-by: Grant Likely <[email protected]>
> ---
> arch/arm/boot/dts/versatile-ab.dts | 185 ++++++++++++++++++++++++++++++++
> arch/arm/boot/dts/versatile-pb.dts | 48 ++++++++
> arch/arm/mach-versatile/Kconfig | 8 +
> arch/arm/mach-versatile/Makefile | 1
> arch/arm/mach-versatile/core.c | 61 +++++++++++
> arch/arm/mach-versatile/core.h | 5 +
> arch/arm/mach-versatile/versatile_dt.c | 66 +++++++++++
> 7 files changed, 374 insertions(+), 0 deletions(-)
> create mode 100644 arch/arm/boot/dts/versatile-ab.dts
> create mode 100644 arch/arm/boot/dts/versatile-pb.dts
> create mode 100644 arch/arm/mach-versatile/versatile_dt.c
>
> diff --git a/arch/arm/boot/dts/versatile-ab.dts b/arch/arm/boot/dts/versatile-ab.dts
> new file mode 100644
> index 0000000..0e31dc9
> --- /dev/null
> +++ b/arch/arm/boot/dts/versatile-ab.dts
> @@ -0,0 +1,185 @@
> +/dts-v1/;
> +/include/ "skeleton.dtsi"
> +
> +/ {
> + model = "ARM Versatile AB";
> + compatible = "arm,versatile-ab";
> + #address-cells = <1>;
> + #size-cells = <1>;
> + interrupt-parent = <&vic>;
> +
> + memory {
> + reg = <0x0 0x08000000>;
> + };
> +
> + flash@34000000 {
> + compatible = "arm,versatile-flash";
Seems like this should be a flash part number.
> + reg = <0x34000000 0x4000000>;
> + bank-width = <4>;
> + };
> +
> + i2c@10002000 {
> + #address-cells = <1>;
> + #size-cells = <0>;
> + compatible = "arm,versatile-i2c";
> + reg = <0x10002000 0x1000>;
> +
> + rtc@68 {
> + compatible = "dallas,ds1338";
> + reg = <0x68>;
> + };
> + };
> +
> + net@10010000 {
> + compatible = "smsc,lan91c111";
> + reg = <0x10010000 0x10000>;
> + interrupts = <25>;
> + };
> +
> + lcd@10008000 {
> + compatible = "arm,versatile-lcd";
> + reg = <0x10008000 0x1000>;
> + };
> +
> + amba {
> + compatible = "arm,amba-bus";
> + #address-cells = <1>;
> + #size-cells = <1>;
> + ranges;
> +
> + vic: intc@10140000 {
> + compatible = "arm,versatile-vic", "arm,vic";
> + interrupt-controller;
> + #interrupt-cells = <1>;
> + reg = <0x10140000 0x1000>;
> + };
> +
> + sic: intc@10003000 {
> + compatible = "arm,versatile-sic", "arm,sic";
> + interrupt-controller;
> + #interrupt-cells = <1>;
> + reg = <0x10003000 0x1000>;
> + interrupt-parent = <&vic>;
> + interrupts = <31>; /* Cascaded to vic */
> + };
> +
> + dma@10130000 {
> + compatible = "arm,primecell";
Should have a more specific compatible string. arm,pl081 IIRC?
> + reg = <0x10130000 0x1000>;
> + interrupts = <17>;
> + };
> +
> + uart0: uart@101f1000 {
> + compatible = "arm,pl011", "arm,primecell";
> + reg = <0x101f1000 0x1000>;
> + interrupts = <12>;
> + };
> +
> + uart1: uart@101f2000 {
> + compatible = "arm,pl011", "arm,primecell";
> + reg = <0x101f2000 0x1000>;
> + interrupts = <13>;
> + };
> +
> + uart2: uart@101f3000 {
> + compatible = "arm,pl011", "arm,primecell";
> + reg = <0x101f3000 0x1000>;
> + interrupts = <14>;
> + };
> +
> + smc@10100000 {
> + compatible = "arm,primecell";
ditto
> + reg = <0x10100000 0x1000>;
> + };
> +
> + mpmc@10110000 {
> + compatible = "arm,primecell";
> + reg = <0x10110000 0x1000>;
> + };
> +
> + display@10120000 {
> + compatible = "arm,pl110", "arm,primecell";
> + reg = <0x10120000 0x1000>;
> + interrupts = <16>;
> + };
> +
> + sctl@101e0000 {
> + compatible = "arm,primecell";
> + reg = <0x101e0000 0x1000>;
> + };
> +
> + watchdog@101e1000 {
> + compatible = "arm,primecell";
> + reg = <0x101e1000 0x1000>;
> + interrupts = <0>;
> + };
> +
> + gpio0: gpio@101e4000 {
> + compatible = "arm,primecell";
arm,pl061
> + reg = <0x101e4000 0x1000>;
> + gpio-controller;
> + interrupts = <6>;
> + #gpio-cells = <2>;
> + interrupt-controller;
> + #interrupt-cells = <2>;
> + };
> +
> + gpio1: gpio@101e5000 {
> + compatible = "arm,primecell";
> + reg = <0x101e5000 0x1000>;
> + interrupts = <7>;
> + gpio-controller;
> + #gpio-cells = <2>;
> + interrupt-controller;
> + #interrupt-cells = <2>;
> + };
> +
> + rtc@101e8000 {
> + compatible = "arm,primecell";
arm,pl031 (or pl030?)
> + reg = <0x101e8000 0x1000>;
> + interrupts = <10>;
> + };
> +
> + sci@101f0000 {
> + compatible = "arm,primecell";
> + reg = <0x101f0000 0x1000>;
> + interrupts = <15>;
> + };
> +
> + ssp@101f4000 {
> + compatible = "arm,primecell";
arm,pl022
> + reg = <0x101f4000 0x1000>;
> + interrupts = <11>;
> + };
> +
> + fpga {
> + compatible = "arm,versatile-fpga", "simple-bus";
> + #address-cells = <1>;
> + #size-cells = <1>;
> + ranges = <0 0x10000000 0x10000>;
> +
> + aaci@4000 {
> + compatible = "arm,primecell";
> + reg = <0x4000 0x1000>;
> + interrupts = <24>;
> + };
> + mmc@5000 {
> + compatible = "arm,primecell";
> + reg = < 0x5000 0x1000>;
> + interrupts = <22>;
> + };
> + kmi@6000 {
> + compatible = "arm,pl050", "arm,primecell";
> + reg = <0x6000 0x1000>;
> + interrupt-parent = <&sic>;
> + interrupts = <3>;
> + };
> + kmi@7000 {
> + compatible = "arm,pl050", "arm,primecell";
> + reg = <0x7000 0x1000>;
> + interrupt-parent = <&sic>;
> + interrupts = <4>;
> + };
> + };
> + };
> +};
> diff --git a/arch/arm/boot/dts/versatile-pb.dts b/arch/arm/boot/dts/versatile-pb.dts
> new file mode 100644
> index 0000000..d1934af
> --- /dev/null
> +++ b/arch/arm/boot/dts/versatile-pb.dts
> @@ -0,0 +1,48 @@
> +/include/ "versatile-ab.dts"
> +
> +/ {
> + model = "ARM Versatile PB";
> + compatible = "arm,versatile-pb";
> +
> + amba {
> + gpio2: gpio@101e6000 {
> + compatible = "arm,primecell";
> + reg = <0x101e6000 0x1000>;
> + interrupts = <8>;
> + gpio-controller;
> + #gpio-cells = <2>;
> + interrupt-controller;
> + #interrupt-cells = <2>;
> + };
> +
> + gpio3: gpio@101e7000 {
> + compatible = "arm,primecell";
> + reg = <0x101e7000 0x1000>;
> + interrupts = <9>;
> + gpio-controller;
> + #gpio-cells = <2>;
> + interrupt-controller;
> + #interrupt-cells = <2>;
> + };
> +
> + fpga {
> + uart@9000 {
> + compatible = "arm,pl011", "arm,primecell";
> + reg = <0x9000 0x1000>;
> + interrupt-parent = <&sic>;
> + interrupts = <6>;
> + };
> + sci@a000 {
> + compatible = "arm,primecell";
> + reg = <0xa000 0x1000>;
> + interrupt-parent = <&sic>;
> + interrupts = <5>;
> + };
> + mmc@b000 {
> + compatible = "arm,primecell";
> + reg = <0xb000 0x1000>;
> + interrupts = <23>;
> + };
> + };
> + };
> +};
> diff --git a/arch/arm/mach-versatile/Kconfig b/arch/arm/mach-versatile/Kconfig
> index 9cdec5a..c1f38f6 100644
> --- a/arch/arm/mach-versatile/Kconfig
> +++ b/arch/arm/mach-versatile/Kconfig
> @@ -17,4 +17,12 @@ config MACH_VERSATILE_AB
> Include support for the ARM(R) Versatile Application Baseboard
> for the ARM926EJ-S.
>
> +config MACH_VERSATILE_DT
> + bool "Support Versatile platform from device tree"
> + select USE_OF
> + select CPU_ARM926T
> + help
> + Include support for the ARM(R) Versatile/PB platform,
> + using the device tree for discovery
> +
> endmenu
> diff --git a/arch/arm/mach-versatile/Makefile b/arch/arm/mach-versatile/Makefile
> index 97cf4d8..81fa3fe 100644
> --- a/arch/arm/mach-versatile/Makefile
> +++ b/arch/arm/mach-versatile/Makefile
> @@ -5,4 +5,5 @@
> obj-y := core.o
> obj-$(CONFIG_ARCH_VERSATILE_PB) += versatile_pb.o
> obj-$(CONFIG_MACH_VERSATILE_AB) += versatile_ab.o
> +obj-$(CONFIG_MACH_VERSATILE_DT) += versatile_dt.o
> obj-$(CONFIG_PCI) += pci.o
> diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c
> index 0c99cf0..80e010b 100644
> --- a/arch/arm/mach-versatile/core.c
> +++ b/arch/arm/mach-versatile/core.c
> @@ -24,6 +24,8 @@
> #include <linux/platform_device.h>
> #include <linux/sysdev.h>
> #include <linux/interrupt.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> #include <linux/amba/bus.h>
> #include <linux/amba/clcd.h>
> #include <linux/amba/pl061.h>
> @@ -83,13 +85,26 @@ static struct fpga_irq_data sic_irq = {
> #define PIC_MASK 0
> #endif
>
> +/* Lookup table for finding a DT node that represents the vic instance */
> +static struct of_device_id vic_of_match[] __initdata = {
__initconst
> + { .compatible = "arm,vic", },
> + {}
> +};
> +
> +static struct of_device_id sic_of_match[] __initdata = {
ditto
> + { .compatible = "arm,sic", },
> + {}
> +};
> +
> void __init versatile_init_irq(void)
> {
> vic_init(VA_VIC_BASE, IRQ_VIC_START, ~0, 0);
> + irq_domain_generate_simple(vic_of_match, VERSATILE_VIC_BASE, IRQ_VIC_START);
>
> writel(~0, VA_SIC_BASE + SIC_IRQ_ENABLE_CLEAR);
>
> fpga_irq_init(IRQ_VICSOURCE31, ~PIC_MASK, &sic_irq);
> + irq_domain_generate_simple(sic_of_match, VERSATILE_SIC_BASE, IRQ_SIC_START);
>
> /*
> * Interrupts on secondary controller from 0 to 8 are routed to
> @@ -646,6 +661,52 @@ static struct amba_device *amba_devs[] __initdata = {
> &kmi1_device,
> };
>
> +#ifdef CONFIG_OF
> +/*
> + * Lookup table for attaching a specific name and platform_data pointer to
> + * devices as they get created by of_platform_populate(). Ideally this table
> + * would not exist, but the current clock implementation depends on some devices
> + * having a specific name.
> + */
> +struct of_dev_auxdata versatile_auxdata_lookup[] __initdata = {
> + OF_DEV_AUXDATA("arm,primecell", VERSATILE_MMCI0_BASE, "fpga:05", NULL),
> + OF_DEV_AUXDATA("arm,primecell", VERSATILE_KMI0_BASE, "fpga:06", NULL),
> + OF_DEV_AUXDATA("arm,primecell", VERSATILE_KMI1_BASE, "fpga:07", NULL),
> + OF_DEV_AUXDATA("arm,primecell", VERSATILE_UART3_BASE, "fpga:09", NULL),
> + OF_DEV_AUXDATA("arm,primecell", VERSATILE_MMCI1_BASE, "fpga:0b", NULL),
> +
> + OF_DEV_AUXDATA("arm,primecell", VERSATILE_CLCD_BASE, "dev:20", &clcd_plat_data),
> + OF_DEV_AUXDATA("arm,primecell", VERSATILE_UART0_BASE, "dev:f1", NULL),
> + OF_DEV_AUXDATA("arm,primecell", VERSATILE_UART1_BASE, "dev:f2", NULL),
> + OF_DEV_AUXDATA("arm,primecell", VERSATILE_UART2_BASE, "dev:f3", NULL),
> + OF_DEV_AUXDATA("arm,primecell", VERSATILE_SSP_BASE, "dev:f4", NULL),
> +
> +#if 0
> + /*
> + * These entries are unnecessary because no clocks referencing
> + * them. I've left them in for now as place holders in case
> + * any of them need to be added back, but they should be
> + * removed before actually committing this patch. --gcl
> + */
> + OF_DEV_AUXDATA("arm,primecell", VERSATILE_AACI_BASE, "fpga:04", NULL),
> + OF_DEV_AUXDATA("arm,primecell", VERSATILE_SCI1_BASE, "fpga:0a", NULL),
> + OF_DEV_AUXDATA("arm,primecell", VERSATILE_SMC_BASE, "dev:00", NULL),
> + OF_DEV_AUXDATA("arm,primecell", VERSATILE_MPMC_BASE, "dev:10", NULL),
> + OF_DEV_AUXDATA("arm,primecell", VERSATILE_DMAC_BASE, "dev:30", NULL),
> +
> + OF_DEV_AUXDATA("arm,primecell", VERSATILE_SCTL_BASE, "dev:e0", NULL),
> + OF_DEV_AUXDATA("arm,primecell", VERSATILE_WATCHDOG_BASE, "dev:e1", NULL),
> + OF_DEV_AUXDATA("arm,primecell", VERSATILE_GPIO0_BASE, "dev:e4", NULL),
> + OF_DEV_AUXDATA("arm,primecell", VERSATILE_GPIO1_BASE, "dev:e5", NULL),
> + OF_DEV_AUXDATA("arm,primecell", VERSATILE_GPIO2_BASE, "dev:e6", NULL),
> + OF_DEV_AUXDATA("arm,primecell", VERSATILE_GPIO3_BASE, "dev:e7", NULL),
> + OF_DEV_AUXDATA("arm,primecell", VERSATILE_RTC_BASE, "dev:e8", NULL),
> + OF_DEV_AUXDATA("arm,primecell", VERSATILE_SCI_BASE, "dev:f0", NULL),
> +#endif
> + {}
> +};
> +#endif
> +
> #ifdef CONFIG_LEDS
> #define VA_LEDS_BASE (__io_address(VERSATILE_SYS_BASE) + VERSATILE_SYS_LED_OFFSET)
>
> diff --git a/arch/arm/mach-versatile/core.h b/arch/arm/mach-versatile/core.h
> index fd6404e..c97b070 100644
> --- a/arch/arm/mach-versatile/core.h
> +++ b/arch/arm/mach-versatile/core.h
> @@ -23,6 +23,7 @@
> #define __ASM_ARCH_VERSATILE_H
>
> #include <linux/amba/bus.h>
> +#include <linux/of_platform.h>
>
> extern void __init versatile_init(void);
> extern void __init versatile_init_early(void);
> @@ -31,6 +32,10 @@ extern void __init versatile_map_io(void);
> extern struct sys_timer versatile_timer;
> extern unsigned int mmc_status(struct device *dev);
>
> +#ifdef CONFIG_OF
Don't really need the ifdef.
> +extern struct of_dev_auxdata versatile_auxdata_lookup[];
> +#endif
> +
> #define AMBA_DEVICE(name,busid,base,plat) \
> static struct amba_device name##_device = { \
> .dev = { \
> diff --git a/arch/arm/mach-versatile/versatile_dt.c b/arch/arm/mach-versatile/versatile_dt.c
> new file mode 100644
> index 0000000..f476a11
> --- /dev/null
> +++ b/arch/arm/mach-versatile/versatile_dt.c
> @@ -0,0 +1,66 @@
> +/*
> + * Versatile board support using the device tree
> + *
> + * Copyright (C) 2010 Secret Lab Technologies Ltd.
> + * Copyright (C) 2009 Jeremy Kerr <[email protected]>
> + * Copyright (C) 2004 ARM Limited
> + * Copyright (C) 2000 Deep Blue Solutions Ltd
> + *
> + * 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.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + */
> +
> +#include <linux/init.h>
> +#include <linux/device.h>
> +#include <linux/sysdev.h>
> +#include <linux/clk.h>
> +#include <linux/clkdev.h>
> +#include <linux/amba/bus.h>
> +#include <linux/amba/pl061.h>
> +#include <linux/amba/mmci.h>
A lot of these headers are not needed.
> +#include <linux/io.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_fdt.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +
> +#include <mach/hardware.h>
> +#include <asm/irq.h>
> +#include <asm/mach-types.h>
> +
> +#include <asm/mach/arch.h>
> +
> +#include "core.h"
> +
> +static void __init versatile_dt_init(void)
> +{
> + of_platform_populate(NULL, of_default_bus_match_table,
> + versatile_auxdata_lookup, NULL);
> +}
> +
> +static const char *versatile_dt_match[] __initdata = {
__initconst
> + "arm,versatile-ab",
> + "arm,versatile-pb",
> + NULL,
> +};
> +
> +DT_MACHINE_START(VERSATILE_PB, "ARM-Versatile (Device Tree Support)")
> + .map_io = versatile_map_io,
> + .init_early = versatile_init_early,
> + .init_irq = versatile_init_irq,
> + .timer = &versatile_timer,
> + .init_machine = versatile_dt_init,
> + .dt_compat = versatile_dt_match,
> +MACHINE_END
>
> _______________________________________________
> devicetree-discuss mailing list
> [email protected]
> https://lists.ozlabs.org/listinfo/devicetree-discuss
On Thursday 16 June 2011, Grant Likely wrote:
> This patch adds a new versatile platform for when using the device
> tree. Add platform and amba devices are discovered and registered by
> parsing the device tree. Clocks and initial io mappings are still
> configured statically.
Hi Grant,
Two small questions about the device tree contents:
> + i2c@10002000 {
> + #address-cells = <1>;
> + #size-cells = <0>;
> + compatible = "arm,versatile-i2c";
> + reg = <0x10002000 0x1000>;
> +
> + rtc@68 {
> + compatible = "dallas,ds1338";
> + reg = <0x68>;
> + };
> + };
> +
> + net@10010000 {
> + compatible = "smsc,lan91c111";
> + reg = <0x10010000 0x10000>;
> + interrupts = <25>;
> + };
> +
> + lcd@10008000 {
> + compatible = "arm,versatile-lcd";
> + reg = <0x10008000 0x1000>;
> + };
Why are these devices on the top level, rather than on the AMBA bus or
the FPGA? From the documentation, it seems that they are implemented
in the FPGA, which would also match the address layout.
> + amba {
> + compatible = "arm,amba-bus";
> + #address-cells = <1>;
> + #size-cells = <1>;
> + ranges;
> +
> + vic: intc@10140000 {
> + compatible = "arm,versatile-vic", "arm,vic";
> + interrupt-controller;
> + #interrupt-cells = <1>;
> + reg = <0x10140000 0x1000>;
> + };
> +
Why the empty ranges property? All device registers are based on 0x10000000,
so I'd expect this to be described like that here.
Arnd
Forgot one thing.
On 06/16/2011 09:04 AM, Rob Herring wrote:
> Grant,
>
> On 06/15/2011 11:41 PM, Grant Likely wrote:
>> irq_domain_add_simple() is an easy way to generate an irq translation
>> domain for simple irq controllers. It assumes a flat 1:1 mapping from
>> hardware irq number to an offset of the first linux irq number assigned
>> to the controller
>>
The description and headline are a bit out of date since adding
irq_domain_generate_simple.
Rob
On Thu, Jun 16, 2011 at 04:20:37PM +0200, Arnd Bergmann wrote:
> On Thursday 16 June 2011, Grant Likely wrote:
> > This patch adds a new versatile platform for when using the device
> > tree. Add platform and amba devices are discovered and registered by
> > parsing the device tree. Clocks and initial io mappings are still
> > configured statically.
>
> Hi Grant,
>
> Two small questions about the device tree contents:
>
> > + i2c@10002000 {
> > + #address-cells = <1>;
> > + #size-cells = <0>;
> > + compatible = "arm,versatile-i2c";
> > + reg = <0x10002000 0x1000>;
> > +
> > + rtc@68 {
> > + compatible = "dallas,ds1338";
> > + reg = <0x68>;
> > + };
> > + };
> > +
> > + net@10010000 {
> > + compatible = "smsc,lan91c111";
> > + reg = <0x10010000 0x10000>;
> > + interrupts = <25>;
> > + };
> > +
> > + lcd@10008000 {
> > + compatible = "arm,versatile-lcd";
> > + reg = <0x10008000 0x1000>;
> > + };
>
> Why are these devices on the top level, rather than on the AMBA bus or
> the FPGA? From the documentation, it seems that they are implemented
> in the FPGA, which would also match the address layout.
Mostly because I 'faked' this device tree based on what is currently
in the kernel for the Versatile platform. I need to look at the
documentation and make it reflect reality, including setting up ranges
correctly (as you commented on below).
>
> > + amba {
> > + compatible = "arm,amba-bus";
> > + #address-cells = <1>;
> > + #size-cells = <1>;
> > + ranges;
> > +
> > + vic: intc@10140000 {
> > + compatible = "arm,versatile-vic", "arm,vic";
> > + interrupt-controller;
> > + #interrupt-cells = <1>;
> > + reg = <0x10140000 0x1000>;
> > + };
> > +
>
> Why the empty ranges property? All device registers are based on 0x10000000,
> so I'd expect this to be described like that here.
>
> Arnd
On Thu, Jun 16, 2011 at 09:19:11AM -0500, Rob Herring wrote:
> On 06/15/2011 11:43 PM, Grant Likely wrote:
> > --- /dev/null
> > +++ b/arch/arm/boot/dts/versatile-ab.dts
> > @@ -0,0 +1,185 @@
> > +/dts-v1/;
> > +/include/ "skeleton.dtsi"
> > +
> > +/ {
> > + model = "ARM Versatile AB";
> > + compatible = "arm,versatile-ab";
> > + #address-cells = <1>;
> > + #size-cells = <1>;
> > + interrupt-parent = <&vic>;
> > +
> > + memory {
> > + reg = <0x0 0x08000000>;
> > + };
> > +
> > + flash@34000000 {
> > + compatible = "arm,versatile-flash";
>
> Seems like this should be a flash part number.
Indeed it should. I haven't looked up the details of the flash device
yet though. The DT physmap binding needs to be implemented here too
so that partitions can be described.
> > + dma@10130000 {
> > + compatible = "arm,primecell";
>
> Should have a more specific compatible string. arm,pl081 IIRC?
Probably. I've not looked up the details in the versatile
documentation yet though.
>
> > + reg = <0x10130000 0x1000>;
> > + interrupts = <17>;
> > + };
> > +
> > + uart0: uart@101f1000 {
> > + compatible = "arm,pl011", "arm,primecell";
> > + reg = <0x101f1000 0x1000>;
> > + interrupts = <12>;
> > + };
> > +
> > + uart1: uart@101f2000 {
> > + compatible = "arm,pl011", "arm,primecell";
> > + reg = <0x101f2000 0x1000>;
> > + interrupts = <13>;
> > + };
> > +
> > + uart2: uart@101f3000 {
> > + compatible = "arm,pl011", "arm,primecell";
> > + reg = <0x101f3000 0x1000>;
> > + interrupts = <14>;
> > + };
> > +
> > + smc@10100000 {
> > + compatible = "arm,primecell";
>
> ditto
>
> > + reg = <0x10100000 0x1000>;
> > + };
> > +
> > + mpmc@10110000 {
> > + compatible = "arm,primecell";
> > + reg = <0x10110000 0x1000>;
> > + };
> > +
> > + display@10120000 {
> > + compatible = "arm,pl110", "arm,primecell";
> > + reg = <0x10120000 0x1000>;
> > + interrupts = <16>;
> > + };
> > +
> > + sctl@101e0000 {
> > + compatible = "arm,primecell";
> > + reg = <0x101e0000 0x1000>;
> > + };
> > +
> > + watchdog@101e1000 {
> > + compatible = "arm,primecell";
> > + reg = <0x101e1000 0x1000>;
> > + interrupts = <0>;
> > + };
> > +
> > + gpio0: gpio@101e4000 {
> > + compatible = "arm,primecell";
>
> arm,pl061
>
> > + reg = <0x101e4000 0x1000>;
> > + gpio-controller;
> > + interrupts = <6>;
> > + #gpio-cells = <2>;
> > + interrupt-controller;
> > + #interrupt-cells = <2>;
> > + };
> > +
> > + gpio1: gpio@101e5000 {
> > + compatible = "arm,primecell";
> > + reg = <0x101e5000 0x1000>;
> > + interrupts = <7>;
> > + gpio-controller;
> > + #gpio-cells = <2>;
> > + interrupt-controller;
> > + #interrupt-cells = <2>;
> > + };
> > +
> > + rtc@101e8000 {
> > + compatible = "arm,primecell";
>
> arm,pl031 (or pl030?)
>
> > + reg = <0x101e8000 0x1000>;
> > + interrupts = <10>;
> > + };
> > +
> > + sci@101f0000 {
> > + compatible = "arm,primecell";
> > + reg = <0x101f0000 0x1000>;
> > + interrupts = <15>;
> > + };
> > +
> > + ssp@101f4000 {
> > + compatible = "arm,primecell";
>
> arm,pl022
>
> > + reg = <0x101f4000 0x1000>;
> > + interrupts = <11>;
> > + };
> > +
> > + fpga {
> > + compatible = "arm,versatile-fpga", "simple-bus";
> > + #address-cells = <1>;
> > + #size-cells = <1>;
> > + ranges = <0 0x10000000 0x10000>;
> > +
> > + aaci@4000 {
> > + compatible = "arm,primecell";
> > + reg = <0x4000 0x1000>;
> > + interrupts = <24>;
> > + };
> > + mmc@5000 {
> > + compatible = "arm,primecell";
> > + reg = < 0x5000 0x1000>;
> > + interrupts = <22>;
> > + };
> > + kmi@6000 {
> > + compatible = "arm,pl050", "arm,primecell";
> > + reg = <0x6000 0x1000>;
> > + interrupt-parent = <&sic>;
> > + interrupts = <3>;
> > + };
> > + kmi@7000 {
> > + compatible = "arm,pl050", "arm,primecell";
> > + reg = <0x7000 0x1000>;
> > + interrupt-parent = <&sic>;
> > + interrupts = <4>;
> > + };
> > + };
> > + };
> > +};
> > diff --git a/arch/arm/boot/dts/versatile-pb.dts b/arch/arm/boot/dts/versatile-pb.dts
> > new file mode 100644
> > index 0000000..d1934af
> > --- /dev/null
> > +++ b/arch/arm/boot/dts/versatile-pb.dts
> > @@ -0,0 +1,48 @@
> > +/include/ "versatile-ab.dts"
> > +
> > +/ {
> > + model = "ARM Versatile PB";
> > + compatible = "arm,versatile-pb";
> > +
> > + amba {
> > + gpio2: gpio@101e6000 {
> > + compatible = "arm,primecell";
> > + reg = <0x101e6000 0x1000>;
> > + interrupts = <8>;
> > + gpio-controller;
> > + #gpio-cells = <2>;
> > + interrupt-controller;
> > + #interrupt-cells = <2>;
> > + };
> > +
> > + gpio3: gpio@101e7000 {
> > + compatible = "arm,primecell";
> > + reg = <0x101e7000 0x1000>;
> > + interrupts = <9>;
> > + gpio-controller;
> > + #gpio-cells = <2>;
> > + interrupt-controller;
> > + #interrupt-cells = <2>;
> > + };
> > +
> > + fpga {
> > + uart@9000 {
> > + compatible = "arm,pl011", "arm,primecell";
> > + reg = <0x9000 0x1000>;
> > + interrupt-parent = <&sic>;
> > + interrupts = <6>;
> > + };
> > + sci@a000 {
> > + compatible = "arm,primecell";
> > + reg = <0xa000 0x1000>;
> > + interrupt-parent = <&sic>;
> > + interrupts = <5>;
> > + };
> > + mmc@b000 {
> > + compatible = "arm,primecell";
> > + reg = <0xb000 0x1000>;
> > + interrupts = <23>;
> > + };
> > + };
> > + };
> > +};
> > diff --git a/arch/arm/mach-versatile/Kconfig b/arch/arm/mach-versatile/Kconfig
> > index 9cdec5a..c1f38f6 100644
> > --- a/arch/arm/mach-versatile/Kconfig
> > +++ b/arch/arm/mach-versatile/Kconfig
> > @@ -17,4 +17,12 @@ config MACH_VERSATILE_AB
> > Include support for the ARM(R) Versatile Application Baseboard
> > for the ARM926EJ-S.
> >
> > +config MACH_VERSATILE_DT
> > + bool "Support Versatile platform from device tree"
> > + select USE_OF
> > + select CPU_ARM926T
> > + help
> > + Include support for the ARM(R) Versatile/PB platform,
> > + using the device tree for discovery
> > +
> > endmenu
> > diff --git a/arch/arm/mach-versatile/Makefile b/arch/arm/mach-versatile/Makefile
> > index 97cf4d8..81fa3fe 100644
> > --- a/arch/arm/mach-versatile/Makefile
> > +++ b/arch/arm/mach-versatile/Makefile
> > @@ -5,4 +5,5 @@
> > obj-y := core.o
> > obj-$(CONFIG_ARCH_VERSATILE_PB) += versatile_pb.o
> > obj-$(CONFIG_MACH_VERSATILE_AB) += versatile_ab.o
> > +obj-$(CONFIG_MACH_VERSATILE_DT) += versatile_dt.o
> > obj-$(CONFIG_PCI) += pci.o
> > diff --git a/arch/arm/mach-versatile/core.c b/arch/arm/mach-versatile/core.c
> > index 0c99cf0..80e010b 100644
> > --- a/arch/arm/mach-versatile/core.c
> > +++ b/arch/arm/mach-versatile/core.c
> > @@ -24,6 +24,8 @@
> > #include <linux/platform_device.h>
> > #include <linux/sysdev.h>
> > #include <linux/interrupt.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_platform.h>
> > #include <linux/amba/bus.h>
> > #include <linux/amba/clcd.h>
> > #include <linux/amba/pl061.h>
> > @@ -83,13 +85,26 @@ static struct fpga_irq_data sic_irq = {
> > #define PIC_MASK 0
> > #endif
> >
> > +/* Lookup table for finding a DT node that represents the vic instance */
> > +static struct of_device_id vic_of_match[] __initdata = {
>
> __initconst
fixed, thanks.
>
> > + { .compatible = "arm,vic", },
> > + {}
> > +};
> > +
> > +static struct of_device_id sic_of_match[] __initdata = {
>
> ditto
>
> > + { .compatible = "arm,sic", },
> > + {}
> > +};
> > +
> > void __init versatile_init_irq(void)
> > {
> > vic_init(VA_VIC_BASE, IRQ_VIC_START, ~0, 0);
> > + irq_domain_generate_simple(vic_of_match, VERSATILE_VIC_BASE, IRQ_VIC_START);
> >
> > writel(~0, VA_SIC_BASE + SIC_IRQ_ENABLE_CLEAR);
> >
> > fpga_irq_init(IRQ_VICSOURCE31, ~PIC_MASK, &sic_irq);
> > + irq_domain_generate_simple(sic_of_match, VERSATILE_SIC_BASE, IRQ_SIC_START);
> >
> > /*
> > * Interrupts on secondary controller from 0 to 8 are routed to
> > @@ -646,6 +661,52 @@ static struct amba_device *amba_devs[] __initdata = {
> > &kmi1_device,
> > };
> >
> > +#ifdef CONFIG_OF
> > +/*
> > + * Lookup table for attaching a specific name and platform_data pointer to
> > + * devices as they get created by of_platform_populate(). Ideally this table
> > + * would not exist, but the current clock implementation depends on some devices
> > + * having a specific name.
> > + */
> > +struct of_dev_auxdata versatile_auxdata_lookup[] __initdata = {
> > + OF_DEV_AUXDATA("arm,primecell", VERSATILE_MMCI0_BASE, "fpga:05", NULL),
> > + OF_DEV_AUXDATA("arm,primecell", VERSATILE_KMI0_BASE, "fpga:06", NULL),
> > + OF_DEV_AUXDATA("arm,primecell", VERSATILE_KMI1_BASE, "fpga:07", NULL),
> > + OF_DEV_AUXDATA("arm,primecell", VERSATILE_UART3_BASE, "fpga:09", NULL),
> > + OF_DEV_AUXDATA("arm,primecell", VERSATILE_MMCI1_BASE, "fpga:0b", NULL),
> > +
> > + OF_DEV_AUXDATA("arm,primecell", VERSATILE_CLCD_BASE, "dev:20", &clcd_plat_data),
> > + OF_DEV_AUXDATA("arm,primecell", VERSATILE_UART0_BASE, "dev:f1", NULL),
> > + OF_DEV_AUXDATA("arm,primecell", VERSATILE_UART1_BASE, "dev:f2", NULL),
> > + OF_DEV_AUXDATA("arm,primecell", VERSATILE_UART2_BASE, "dev:f3", NULL),
> > + OF_DEV_AUXDATA("arm,primecell", VERSATILE_SSP_BASE, "dev:f4", NULL),
> > +
> > +#if 0
> > + /*
> > + * These entries are unnecessary because no clocks referencing
> > + * them. I've left them in for now as place holders in case
> > + * any of them need to be added back, but they should be
> > + * removed before actually committing this patch. --gcl
> > + */
> > + OF_DEV_AUXDATA("arm,primecell", VERSATILE_AACI_BASE, "fpga:04", NULL),
> > + OF_DEV_AUXDATA("arm,primecell", VERSATILE_SCI1_BASE, "fpga:0a", NULL),
> > + OF_DEV_AUXDATA("arm,primecell", VERSATILE_SMC_BASE, "dev:00", NULL),
> > + OF_DEV_AUXDATA("arm,primecell", VERSATILE_MPMC_BASE, "dev:10", NULL),
> > + OF_DEV_AUXDATA("arm,primecell", VERSATILE_DMAC_BASE, "dev:30", NULL),
> > +
> > + OF_DEV_AUXDATA("arm,primecell", VERSATILE_SCTL_BASE, "dev:e0", NULL),
> > + OF_DEV_AUXDATA("arm,primecell", VERSATILE_WATCHDOG_BASE, "dev:e1", NULL),
> > + OF_DEV_AUXDATA("arm,primecell", VERSATILE_GPIO0_BASE, "dev:e4", NULL),
> > + OF_DEV_AUXDATA("arm,primecell", VERSATILE_GPIO1_BASE, "dev:e5", NULL),
> > + OF_DEV_AUXDATA("arm,primecell", VERSATILE_GPIO2_BASE, "dev:e6", NULL),
> > + OF_DEV_AUXDATA("arm,primecell", VERSATILE_GPIO3_BASE, "dev:e7", NULL),
> > + OF_DEV_AUXDATA("arm,primecell", VERSATILE_RTC_BASE, "dev:e8", NULL),
> > + OF_DEV_AUXDATA("arm,primecell", VERSATILE_SCI_BASE, "dev:f0", NULL),
> > +#endif
> > + {}
> > +};
> > +#endif
> > +
> > #ifdef CONFIG_LEDS
> > #define VA_LEDS_BASE (__io_address(VERSATILE_SYS_BASE) + VERSATILE_SYS_LED_OFFSET)
> >
> > diff --git a/arch/arm/mach-versatile/core.h b/arch/arm/mach-versatile/core.h
> > index fd6404e..c97b070 100644
> > --- a/arch/arm/mach-versatile/core.h
> > +++ b/arch/arm/mach-versatile/core.h
> > @@ -23,6 +23,7 @@
> > #define __ASM_ARCH_VERSATILE_H
> >
> > #include <linux/amba/bus.h>
> > +#include <linux/of_platform.h>
> >
> > extern void __init versatile_init(void);
> > extern void __init versatile_init_early(void);
> > @@ -31,6 +32,10 @@ extern void __init versatile_map_io(void);
> > extern struct sys_timer versatile_timer;
> > extern unsigned int mmc_status(struct device *dev);
> >
> > +#ifdef CONFIG_OF
>
> Don't really need the ifdef.
True, fixed.
>
> > +extern struct of_dev_auxdata versatile_auxdata_lookup[];
> > +#endif
> > +
> > #define AMBA_DEVICE(name,busid,base,plat) \
> > static struct amba_device name##_device = { \
> > .dev = { \
> > diff --git a/arch/arm/mach-versatile/versatile_dt.c b/arch/arm/mach-versatile/versatile_dt.c
> > new file mode 100644
> > index 0000000..f476a11
> > --- /dev/null
> > +++ b/arch/arm/mach-versatile/versatile_dt.c
> > @@ -0,0 +1,66 @@
> > +/*
> > + * Versatile board support using the device tree
> > + *
> > + * Copyright (C) 2010 Secret Lab Technologies Ltd.
> > + * Copyright (C) 2009 Jeremy Kerr <[email protected]>
> > + * Copyright (C) 2004 ARM Limited
> > + * Copyright (C) 2000 Deep Blue Solutions Ltd
> > + *
> > + * 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.
> > + *
> > + * This program is distributed in the hope that it will be useful,
> > + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> > + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> > + * GNU General Public License for more details.
> > + *
> > + * You should have received a copy of the GNU General Public License
> > + * along with this program; if not, write to the Free Software
> > + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> > + */
> > +
> > +#include <linux/init.h>
> > +#include <linux/device.h>
> > +#include <linux/sysdev.h>
> > +#include <linux/clk.h>
> > +#include <linux/clkdev.h>
> > +#include <linux/amba/bus.h>
> > +#include <linux/amba/pl061.h>
> > +#include <linux/amba/mmci.h>
>
> A lot of these headers are not needed.
Trimmed.
>
> > +#include <linux/io.h>
> > +#include <linux/of.h>
> > +#include <linux/of_address.h>
> > +#include <linux/of_fdt.h>
> > +#include <linux/of_irq.h>
> > +#include <linux/of_platform.h>
> > +
> > +#include <mach/hardware.h>
> > +#include <asm/irq.h>
> > +#include <asm/mach-types.h>
> > +
> > +#include <asm/mach/arch.h>
> > +
> > +#include "core.h"
> > +
> > +static void __init versatile_dt_init(void)
> > +{
> > + of_platform_populate(NULL, of_default_bus_match_table,
> > + versatile_auxdata_lookup, NULL);
> > +}
> > +
> > +static const char *versatile_dt_match[] __initdata = {
>
> __initconst
fixed.
Thanks,
g.
>
> > + "arm,versatile-ab",
> > + "arm,versatile-pb",
> > + NULL,
> > +};
> > +
> > +DT_MACHINE_START(VERSATILE_PB, "ARM-Versatile (Device Tree Support)")
> > + .map_io = versatile_map_io,
> > + .init_early = versatile_init_early,
> > + .init_irq = versatile_init_irq,
> > + .timer = &versatile_timer,
> > + .init_machine = versatile_dt_init,
> > + .dt_compat = versatile_dt_match,
> > +MACHINE_END
> >
> > _______________________________________________
> > devicetree-discuss mailing list
> > [email protected]
> > https://lists.ozlabs.org/listinfo/devicetree-discuss
>
On Thu, Jun 16, 2011 at 09:04:51AM -0500, Rob Herring wrote:
> Grant,
>
> On 06/15/2011 11:41 PM, Grant Likely wrote:
> > irq_domain_add_simple() is an easy way to generate an irq translation
> > domain for simple irq controllers. It assumes a flat 1:1 mapping from
> > hardware irq number to an offset of the first linux irq number assigned
> > to the controller
> >
> > Signed-off-by: Grant Likely <[email protected]>
> > ---
> > include/linux/irq.h | 11 +++++++++
> > kernel/irq/irqdomain.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++-
> > 2 files changed, 68 insertions(+), 1 deletions(-)
> >
> > diff --git a/include/linux/irq.h b/include/linux/irq.h
> > index a103c01..50830fc 100644
> > --- a/include/linux/irq.h
> > +++ b/include/linux/irq.h
> > @@ -797,6 +797,17 @@ extern unsigned int irq_domain_map(struct irq_domain *domain,
> > irq_hw_number_t hwirq);
> > extern void irq_domain_unmap(struct irq_domain *domain, irq_hw_number_t hw);
> >
> > +struct of_device_id;
> > +#ifdef CONFIG_OF
> > +extern void irq_domain_add_simple(struct device_node *controller, int irq_base);
> > +extern void irq_domain_generate_simple(struct of_device_id *match,
>
> match should be const
yup
>
> > + u64 phys_base, unsigned int irq_start);
> > +#else
>
> Empty function for irq_domain_add_simple?
Haven't needed it yet. I'll add it if it comes up. Honestly, I'm not
even sure if this patch will survive very long once the IRQ
controllers start using irq_domains directly. This is more of an easy
way to get an irq_domain registration so that DT translations work.
g.
On Thu, Jun 16, 2011 at 09:21:20AM -0500, Rob Herring wrote:
> Forgot one thing.
>
> On 06/16/2011 09:04 AM, Rob Herring wrote:
> > Grant,
> >
> > On 06/15/2011 11:41 PM, Grant Likely wrote:
> >> irq_domain_add_simple() is an easy way to generate an irq translation
> >> domain for simple irq controllers. It assumes a flat 1:1 mapping from
> >> hardware irq number to an offset of the first linux irq number assigned
> >> to the controller
> >>
>
> The description and headline are a bit out of date since adding
> irq_domain_generate_simple.
good catch. Thanks.
g.
On Wed, Jun 15, 2011 at 10:43:18PM -0600, Grant Likely wrote:
[...]
> diff --git a/arch/arm/boot/dts/versatile-ab.dts b/arch/arm/boot/dts/versatile-ab.dts
> new file mode 100644
> index 0000000..0e31dc9
> --- /dev/null
> +++ b/arch/arm/boot/dts/versatile-ab.dts
> @@ -0,0 +1,185 @@
> +/dts-v1/;
> +/include/ "skeleton.dtsi"
> +
> +/ {
> + model = "ARM Versatile AB";
> + compatible = "arm,versatile-ab";
> + #address-cells = <1>;
> + #size-cells = <1>;
We already have above two line in skeleton.dtsi.
> + interrupt-parent = <&vic>;
--
Regards,
Shawn
On 16/06/11 05:41, Grant Likely wrote:
> This patch adds irq_domain infrastructure for translating from
> hardware irq numbers to linux irqs. This is particularly important
> for architectures adding device tree support because the current
> implementation (excluding PowerPC and SPARC) cannot handle
> translation for more than a single interrupt controller. irq_domain
> supports device tree translation for any number of interrupt
> controllers.
>
> This patch converts x86, Microblaze, ARM and MIPS to use irq_domain
> for device tree irq translation. x86 is untested beyond compiling it,
> and the patch definitely breaks MIPS and Microblaze because I haven't
> added the code to either of them to actually register an irq_domain
> yet (easy to fix), but on ARM it works. I'm circulating this for comments
> to make sure I'm on the right path before I fix up the MIPS and
> Microblaze bits.
>
> PowerPC has /not/ been converted to use this new infrastructure. It
> is still missing some features before it can replace the virq
> infrastructure already in powerpc (see documentation on
> irq_domain_map/unmap for details). Followup patches will add the
> missing pieces and migrate PowerPC to use irq_domain.
>
> SPARC has its own method of managing interrupts from the device tree
> and is unaffected by this change.
>
> Signed-off-by: Grant Likely <[email protected]>
I've rebased my PPI + local timer patches on top of that patch in
the following branch:
git://git.kernel.org/pub/scm/linux/kernel/git/maz/arm-platforms.git ppi_irq_domain
This makes the code cleaner, though the way to link this into DT
is not completely clear for me yet.
Cheers,
M.
> ---
> arch/arm/include/asm/prom.h | 5 -
> arch/arm/kernel/devtree.c | 14 ---
> arch/microblaze/include/asm/irq.h | 10 --
> arch/microblaze/kernel/irq.c | 7 --
> arch/mips/include/asm/irq.h | 5 -
> arch/mips/kernel/prom.c | 14 ---
> arch/powerpc/include/asm/irq.h | 1
> arch/x86/include/asm/irq_controller.h | 12 ---
> arch/x86/include/asm/prom.h | 10 --
> arch/x86/kernel/devicetree.c | 101 ++++++++----------------
> include/linux/irq.h | 81 ++++++++++++++++++++
> include/linux/of_irq.h | 2
> kernel/irq/Makefile | 2
> kernel/irq/irqdomain.c | 137 +++++++++++++++++++++++++++++++++
> 14 files changed, 257 insertions(+), 144 deletions(-)
> delete mode 100644 arch/x86/include/asm/irq_controller.h
> create mode 100644 kernel/irq/irqdomain.c
>
> diff --git a/arch/arm/include/asm/prom.h b/arch/arm/include/asm/prom.h
> index 11b8708..6f65ca8 100644
> --- a/arch/arm/include/asm/prom.h
> +++ b/arch/arm/include/asm/prom.h
> @@ -16,11 +16,6 @@
> #include <asm/setup.h>
> #include <asm/irq.h>
>
> -static inline void irq_dispose_mapping(unsigned int virq)
> -{
> - return;
> -}
> -
> extern struct machine_desc *setup_machine_fdt(unsigned int dt_phys);
> extern void arm_dt_memblock_reserve(void);
>
> diff --git a/arch/arm/kernel/devtree.c b/arch/arm/kernel/devtree.c
> index a701e42..1e0b613 100644
> --- a/arch/arm/kernel/devtree.c
> +++ b/arch/arm/kernel/devtree.c
> @@ -129,17 +129,3 @@ struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys)
>
> return mdesc_best;
> }
> -
> -/**
> - * irq_create_of_mapping - Hook to resolve OF irq specifier into a Linux irq#
> - *
> - * Currently the mapping mechanism is trivial; simple flat hwirq numbers are
> - * mapped 1:1 onto Linux irq numbers. Cascaded irq controllers are not
> - * supported.
> - */
> -unsigned int irq_create_of_mapping(struct device_node *controller,
> - const u32 *intspec, unsigned int intsize)
> -{
> - return intspec[0];
> -}
> -EXPORT_SYMBOL_GPL(irq_create_of_mapping);
> diff --git a/arch/microblaze/include/asm/irq.h b/arch/microblaze/include/asm/irq.h
> index cc54187..6a6d83b 100644
> --- a/arch/microblaze/include/asm/irq.h
> +++ b/arch/microblaze/include/asm/irq.h
> @@ -16,6 +16,7 @@
> * be big enough to enclose whatever representation is used by a given
> * platform.
> */
> +#define _IRQ_HW_NUMBER_T
> typedef unsigned long irq_hw_number_t;
>
> extern unsigned int nr_irq;
> @@ -25,15 +26,6 @@ extern unsigned int nr_irq;
> struct pt_regs;
> extern void do_IRQ(struct pt_regs *regs);
>
> -/** FIXME - not implement
> - * irq_dispose_mapping - Unmap an interrupt
> - * @virq: linux virq number of the interrupt to unmap
> - */
> -static inline void irq_dispose_mapping(unsigned int virq)
> -{
> - return;
> -}
> -
> struct irq_host;
>
> /**
> diff --git a/arch/microblaze/kernel/irq.c b/arch/microblaze/kernel/irq.c
> index ce7ac84..59bb560 100644
> --- a/arch/microblaze/kernel/irq.c
> +++ b/arch/microblaze/kernel/irq.c
> @@ -54,10 +54,3 @@ unsigned int irq_create_mapping(struct irq_host *host, irq_hw_number_t hwirq)
> return hwirq;
> }
> EXPORT_SYMBOL_GPL(irq_create_mapping);
> -
> -unsigned int irq_create_of_mapping(struct device_node *controller,
> - const u32 *intspec, unsigned int intsize)
> -{
> - return intspec[0];
> -}
> -EXPORT_SYMBOL_GPL(irq_create_of_mapping);
> diff --git a/arch/mips/include/asm/irq.h b/arch/mips/include/asm/irq.h
> index 0ec0129..dc650ae 100644
> --- a/arch/mips/include/asm/irq.h
> +++ b/arch/mips/include/asm/irq.h
> @@ -16,11 +16,6 @@
>
> #include <irq.h>
>
> -static inline void irq_dispose_mapping(unsigned int virq)
> -{
> - return;
> -}
> -
> #ifdef CONFIG_I8259
> static inline int irq_canonicalize(int irq)
> {
> diff --git a/arch/mips/kernel/prom.c b/arch/mips/kernel/prom.c
> index 5b7eade..010aaaf 100644
> --- a/arch/mips/kernel/prom.c
> +++ b/arch/mips/kernel/prom.c
> @@ -60,20 +60,6 @@ void __init early_init_dt_setup_initrd_arch(unsigned long start,
> }
> #endif
>
> -/*
> - * irq_create_of_mapping - Hook to resolve OF irq specifier into a Linux irq#
> - *
> - * Currently the mapping mechanism is trivial; simple flat hwirq numbers are
> - * mapped 1:1 onto Linux irq numbers. Cascaded irq controllers are not
> - * supported.
> - */
> -unsigned int irq_create_of_mapping(struct device_node *controller,
> - const u32 *intspec, unsigned int intsize)
> -{
> - return intspec[0];
> -}
> -EXPORT_SYMBOL_GPL(irq_create_of_mapping);
> -
> void __init early_init_devtree(void *params)
> {
> /* Setup flat device-tree pointer */
> diff --git a/arch/powerpc/include/asm/irq.h b/arch/powerpc/include/asm/irq.h
> index 1bff591..66bf09e 100644
> --- a/arch/powerpc/include/asm/irq.h
> +++ b/arch/powerpc/include/asm/irq.h
> @@ -45,6 +45,7 @@ extern atomic_t ppc_n_lost_interrupts;
> * be big enough to enclose whatever representation is used by a given
> * platform.
> */
> +#define _IRQ_HW_NUMBER_T
> typedef unsigned long irq_hw_number_t;
>
> /* Interrupt controller "host" data structure. This could be defined as a
> diff --git a/arch/x86/include/asm/irq_controller.h b/arch/x86/include/asm/irq_controller.h
> deleted file mode 100644
> index 423bbbd..0000000
> --- a/arch/x86/include/asm/irq_controller.h
> +++ /dev/null
> @@ -1,12 +0,0 @@
> -#ifndef __IRQ_CONTROLLER__
> -#define __IRQ_CONTROLLER__
> -
> -struct irq_domain {
> - int (*xlate)(struct irq_domain *h, const u32 *intspec, u32 intsize,
> - u32 *out_hwirq, u32 *out_type);
> - void *priv;
> - struct device_node *controller;
> - struct list_head l;
> -};
> -
> -#endif
> diff --git a/arch/x86/include/asm/prom.h b/arch/x86/include/asm/prom.h
> index 971e0b4..2c8101e 100644
> --- a/arch/x86/include/asm/prom.h
> +++ b/arch/x86/include/asm/prom.h
> @@ -21,7 +21,6 @@
> #include <asm/irq.h>
> #include <asm/atomic.h>
> #include <asm/setup.h>
> -#include <asm/irq_controller.h>
>
> #ifdef CONFIG_OF
> extern int of_ioapic;
> @@ -54,15 +53,6 @@ extern char cmd_line[COMMAND_LINE_SIZE];
> #define pci_address_to_pio pci_address_to_pio
> unsigned long pci_address_to_pio(phys_addr_t addr);
>
> -/**
> - * irq_dispose_mapping - Unmap an interrupt
> - * @virq: linux virq number of the interrupt to unmap
> - *
> - * FIXME: We really should implement proper virq handling like power,
> - * but that's going to be major surgery.
> - */
> -static inline void irq_dispose_mapping(unsigned int virq) { }
> -
> #define HAVE_ARCH_DEVTREE_FIXUPS
>
> #endif /* __ASSEMBLY__ */
> diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c
> index 9aeb78a..250c2d5 100644
> --- a/arch/x86/kernel/devicetree.c
> +++ b/arch/x86/kernel/devicetree.c
> @@ -16,64 +16,14 @@
> #include <linux/initrd.h>
>
> #include <asm/hpet.h>
> -#include <asm/irq_controller.h>
> #include <asm/apic.h>
> #include <asm/pci_x86.h>
>
> __initdata u64 initial_dtb;
> char __initdata cmd_line[COMMAND_LINE_SIZE];
> -static LIST_HEAD(irq_domains);
> -static DEFINE_RAW_SPINLOCK(big_irq_lock);
>
> int __initdata of_ioapic;
>
> -#ifdef CONFIG_X86_IO_APIC
> -static void add_interrupt_host(struct irq_domain *ih)
> -{
> - unsigned long flags;
> -
> - raw_spin_lock_irqsave(&big_irq_lock, flags);
> - list_add(&ih->l, &irq_domains);
> - raw_spin_unlock_irqrestore(&big_irq_lock, flags);
> -}
> -#endif
> -
> -static struct irq_domain *get_ih_from_node(struct device_node *controller)
> -{
> - struct irq_domain *ih, *found = NULL;
> - unsigned long flags;
> -
> - raw_spin_lock_irqsave(&big_irq_lock, flags);
> - list_for_each_entry(ih, &irq_domains, l) {
> - if (ih->controller == controller) {
> - found = ih;
> - break;
> - }
> - }
> - raw_spin_unlock_irqrestore(&big_irq_lock, flags);
> - return found;
> -}
> -
> -unsigned int irq_create_of_mapping(struct device_node *controller,
> - const u32 *intspec, unsigned int intsize)
> -{
> - struct irq_domain *ih;
> - u32 virq, type;
> - int ret;
> -
> - ih = get_ih_from_node(controller);
> - if (!ih)
> - return 0;
> - ret = ih->xlate(ih, intspec, intsize, &virq, &type);
> - if (ret)
> - return 0;
> - if (type == IRQ_TYPE_NONE)
> - return virq;
> - irq_set_irq_type(virq, type);
> - return virq;
> -}
> -EXPORT_SYMBOL_GPL(irq_create_of_mapping);
> -
> unsigned long pci_address_to_pio(phys_addr_t address)
> {
> /*
> @@ -377,36 +327,49 @@ static struct of_ioapic_type of_ioapic_type[] =
> },
> };
>
> -static int ioapic_xlate(struct irq_domain *id, const u32 *intspec, u32 intsize,
> - u32 *out_hwirq, u32 *out_type)
> +static int ioapic_dt_translate(struct irq_domain *domain,
> + struct device_node *controller,
> + const u32 *intspec, u32 intsize,
> + irq_hw_number_t *out_hwirq, u32 *out_type)
> {
> - struct mp_ioapic_gsi *gsi_cfg;
> struct io_apic_irq_attr attr;
> struct of_ioapic_type *it;
> u32 line, idx, type;
> + int rc;
>
> - if (intsize < 2)
> + if (controller != domain->of_node)
> return -EINVAL;
>
> - line = *intspec;
> - idx = (u32) id->priv;
> - gsi_cfg = mp_ioapic_gsi_routing(idx);
> - *out_hwirq = line + gsi_cfg->gsi_base;
> + if (intsize < 2)
> + return -EINVAL;
>
> - intspec++;
> - type = *intspec;
> + line = intspec[0];
>
> - if (type >= ARRAY_SIZE(of_ioapic_type))
> + if (intspec[1] >= ARRAY_SIZE(of_ioapic_type))
> return -EINVAL;
>
> - it = of_ioapic_type + type;
> - *out_type = it->out_type;
> + it = of_ioapic_type + intspec[1];
> + type = it->out_type;
>
> + idx = (u32) domain->priv;
> set_io_apic_irq_attr(&attr, idx, line, it->trigger, it->polarity);
>
> - return io_apic_setup_irq_pin_once(*out_hwirq, cpu_to_node(0), &attr);
> + rc = io_apic_setup_irq_pin_once(irq_domain_to_irq(domain, line),
> + cpu_to_node(0), &attr);
> + if (rc)
> + return rc;
> +
> + if (out_hwirq)
> + *out_hwirq = line;
> + if (out_type)
> + *out_type = type;
> + return 0;
> }
>
> +const struct irq_domain_ops ioapic_irq_domain_ops = {
> + .dt_translate = ioapic_dt_translate,
> +};
> +
> static void __init ioapic_add_ofnode(struct device_node *np)
> {
> struct resource r;
> @@ -422,13 +385,17 @@ static void __init ioapic_add_ofnode(struct device_node *np)
> for (i = 0; i < nr_ioapics; i++) {
> if (r.start == mpc_ioapic_addr(i)) {
> struct irq_domain *id;
> + struct mp_ioapic_gsi *gsi_cfg;
> +
> + gsi_cfg = mp_ioapic_gsi_routing(i);
>
> id = kzalloc(sizeof(*id), GFP_KERNEL);
> BUG_ON(!id);
> - id->controller = np;
> - id->xlate = ioapic_xlate;
> + id->ops = &ioapic_irq_domain_ops;
> + id->irq_base = gsi_cfg->gsi_base;
> + id->of_node = np;
> id->priv = (void *)i;
> - add_interrupt_host(id);
> + irq_domain_add(id);
> return;
> }
> }
> diff --git a/include/linux/irq.h b/include/linux/irq.h
> index 8b45384..a103c01 100644
> --- a/include/linux/irq.h
> +++ b/include/linux/irq.h
> @@ -35,6 +35,16 @@ typedef void (*irq_flow_handler_t)(unsigned int irq,
> struct irq_desc *desc);
> typedef void (*irq_preflow_handler_t)(struct irq_data *data);
>
> +/* This type is the placeholder for a hardware interrupt number. It has to
> + * be big enough to enclose whatever representation is used by a given
> + * platform.
> + */
> +#ifndef _IRQ_HW_NUMBER_T
> +#define _IRQ_HW_NUMBER_T
> +typedef unsigned long irq_hw_number_t;
> +#endif
> +
> +
> /*
> * IRQ line status.
> *
> @@ -113,14 +123,18 @@ enum {
> };
>
> struct msi_desc;
> +struct irq_domain;
>
> /**
> * struct irq_data - per irq and irq chip data passed down to chip functions
> * @irq: interrupt number
> + * @hwirq: hardware interrupt number, local to the interrupt domain
> * @node: node index useful for balancing
> * @state_use_accessors: status information for irq chip functions.
> * Use accessor functions to deal with it
> * @chip: low level interrupt hardware access
> + * @domain: Interrupt translation domain; responsible for mapping
> + * between hwirq number and linux irq number.
> * @handler_data: per-IRQ data for the irq_chip methods
> * @chip_data: platform-specific per-chip private data for the chip
> * methods, to allow shared chip implementations
> @@ -133,9 +147,11 @@ struct msi_desc;
> */
> struct irq_data {
> unsigned int irq;
> + irq_hw_number_t hwirq;
> unsigned int node;
> unsigned int state_use_accessors;
> struct irq_chip *chip;
> + struct irq_domain *domain;
> void *handler_data;
> void *chip_data;
> struct msi_desc *msi_desc;
> @@ -716,6 +732,71 @@ static inline void irq_gc_unlock(struct irq_chip_generic *gc) { }
>
> #endif /* CONFIG_GENERIC_HARDIRQS */
>
> +/*
> + * irq-domain - IRQ translation domains
> + *
> + * Translation infrastructure between hw and linux irq numbers.
> + */
> +struct device_node;
> +
> +/**
> + * struct irq_domain_ops - Methods for irq_domain objects
> + * @to_irq: (optional) given a local hardware irq number, return the linux
> + * irq number. If to_irq is not implemented, then the irq_domain
> + * will use this translation: irq = (domain->irq_base + hwirq)
> + * @dt_translate: Given a device tree node and interrupt specifier, decode
> + * the hardware irq number and linux irq type value.
> + */
> +struct irq_domain_ops {
> + unsigned int (*to_irq)(struct irq_domain *d, irq_hw_number_t hwirq);
> +
> +#ifdef CONFIG_OF
> + int (*dt_translate)(struct irq_domain *d, struct device_node *node,
> + const u32 *intspec, unsigned int intsize,
> + irq_hw_number_t *out_hwirq, unsigned int *out_type);
> +#endif
> +};
> +
> +/**
> + * struct irq_domain - Hardware interrupt number translation object
> + * @list: Element in global irq_domain list.
> + * @irq_base: Start of irq_desc range assigned to the irq_domain. The creator
> + * of the irq_domain is responsible for allocating the array of
> + * irq_desc structures.
> + * @ops: pointer to irq_domain methods
> + * @priv: private data pointer for use by owner. Not touched by irq_domain
> + * core code.
> + * @of_node: (optional) Pointer to device tree nodes associated with the
> + * irq_domain. Used when decoding device tree interrupt specifiers.
> + */
> +struct irq_domain {
> + struct list_head list;
> + unsigned int irq_base;
> + const struct irq_domain_ops *ops;
> + void *priv;
> +
> + struct device_node *of_node;
> +};
> +
> +/**
> + * irq_domain_to_irq() - Translate from a hardware irq to a linux irq number
> + *
> + * Returns the linux irq number associated with a hardware irq. By default,
> + * the mapping is irq == domain->irq_base + hwirq, but this mapping can
> + * be overridden if the irq_domain implements a .to_irq() hook.
> + */
> +static inline unsigned int irq_domain_to_irq(struct irq_domain *d,
> + irq_hw_number_t hwirq)
> +{
> + return d->ops->to_irq ? d->ops->to_irq(d, hwirq) : d->irq_base + hwirq;
> +}
> +
> +extern void irq_domain_add(struct irq_domain *domain);
> +extern void irq_domain_del(struct irq_domain *domain);
> +extern unsigned int irq_domain_map(struct irq_domain *domain,
> + irq_hw_number_t hwirq);
> +extern void irq_domain_unmap(struct irq_domain *domain, irq_hw_number_t hw);
> +
> #endif /* !CONFIG_S390 */
>
> #endif /* _LINUX_IRQ_H */
> diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h
> index e6955f5..67a2a57 100644
> --- a/include/linux/of_irq.h
> +++ b/include/linux/of_irq.h
> @@ -63,6 +63,7 @@ extern int of_irq_map_one(struct device_node *device, int index,
> extern unsigned int irq_create_of_mapping(struct device_node *controller,
> const u32 *intspec,
> unsigned int intsize);
> +extern void irq_dispose_mapping(unsigned int irq);
> extern int of_irq_to_resource(struct device_node *dev, int index,
> struct resource *r);
> extern int of_irq_count(struct device_node *dev);
> @@ -70,6 +71,7 @@ extern int of_irq_to_resource_table(struct device_node *dev,
> struct resource *res, int nr_irqs);
> extern struct device_node *of_irq_find_parent(struct device_node *child);
>
> +
> #endif /* CONFIG_OF_IRQ */
> #endif /* CONFIG_OF */
> #endif /* __OF_IRQ_H */
> diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile
> index 7329005..aa8feed 100644
> --- a/kernel/irq/Makefile
> +++ b/kernel/irq/Makefile
> @@ -1,5 +1,5 @@
>
> -obj-y := irqdesc.o handle.o manage.o spurious.o resend.o chip.o dummychip.o devres.o
> +obj-y := irqdesc.o handle.o manage.o spurious.o resend.o chip.o dummychip.o devres.o irqdomain.o
> obj-$(CONFIG_GENERIC_IRQ_CHIP) += generic-chip.o
> obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o
> obj-$(CONFIG_PROC_FS) += proc.o
> diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c
> new file mode 100644
> index 0000000..8310d6e
> --- /dev/null
> +++ b/kernel/irq/irqdomain.c
> @@ -0,0 +1,137 @@
> +
> +#include <linux/irq.h>
> +#include <linux/module.h>
> +#include <linux/mutex.h>
> +
> +static LIST_HEAD(irq_domain_list);
> +static DEFINE_MUTEX(irq_domain_mutex);
> +
> +/**
> + * irq_domain_add() - Register an irq_domain
> + * @domain: ptr to initialized irq_domain structure
> + *
> + * Registers an irq_domain structure. The irq_domain must at a minimum be
> + * initialized with an ops structure pointer, and either a ->to_irq hook or
> + * a valid irq_base value. Everything else is optional.
> + */
> +void irq_domain_add(struct irq_domain *domain)
> +{
> + mutex_lock(&irq_domain_mutex);
> + list_add(&domain->list, &irq_domain_list);
> + mutex_unlock(&irq_domain_mutex);
> +}
> +
> +/**
> + * irq_domain_del() - Unregister an irq_domain
> + * @domain: ptr to registered irq_domain.
> + */
> +void irq_domain_del(struct irq_domain *domain)
> +{
> + mutex_lock(&irq_domain_mutex);
> + list_del(&domain->list);
> + mutex_unlock(&irq_domain_mutex);
> +}
> +
> +/**
> + * irq_domain_map() - Allocate and/or increment a reference to a hwirq
> + *
> + * TODO: Establish a linux irq number mapping for a hardware irq. If the
> + * mapping already exists, then increment the reference count and return the
> + * linux irq number.
> + *
> + * At the moment this function is an empty stub since irq_domain initially
> + * only supports the common case of mapping hw irq numbers into a contiguous
> + * range of pre-allocated linux irq_descs based at irq_domain->irq_base. When
> + * irq_domains are extended either to support non-contiguous mappings (ie. to
> + * support MSI interrupts) or to remove preallocation of all irq_descs (as
> + * powerpc does so that irq_descs are only allocated for in-use irq inputs),
> + * then this function will be extended to implement the irq_desc allocation
> + * and reference counting.
> + *
> + * Any caller to this function must arrange to also call irq_domain_unmap()
> + * if the irq ever becomes unused again.
> + */
> +unsigned int irq_domain_map(struct irq_domain *domain, irq_hw_number_t hwirq)
> +{
> + int irq = irq_domain_to_irq(domain, hwirq);
> + struct irq_data *d = irq_get_irq_data(irq);
> +
> + d->domain = domain;
> + d->hwirq = hwirq;
> +
> + return irq;
> +}
> +
> +/**
> + * irq_domain_unmap() - Release a reference to a hwirq
> + *
> + * TODO: decrement the reference count on a hardware irq number. If the ref
> + * count reaches zero, then the irq_desc can be freed.
> + *
> + * At the moment this function is an empty stub. See the comment on
> + * irq_domain_map() for details.
> + */
> +void irq_domain_unmap(struct irq_domain *domain, irq_hw_number_t hwirq)
> +{
> + int irq = irq_domain_to_irq(domain, hwirq);
> + struct irq_data *d = irq_get_irq_data(irq);
> +
> + d->domain = NULL;
> +}
> +
> +#if defined(CONFIG_OF_IRQ) && !defined(CONFIG_PPC)
> +/**
> + * irq_create_of_mapping() - Map a linux irq number from a DT interrupt spec
> + *
> + * Used by the device tree interrupt mapping code to translate a device tree
> + * interrupt specifier to a valid linux irq number. Returns either a valid
> + * linux IRQ number or 0.
> + *
> + * When the caller no longer need the irq number returned by this function it
> + * should arrange to call irq_dispose_mapping().
> + */
> +unsigned int irq_create_of_mapping(struct device_node *controller,
> + const u32 *intspec, unsigned int intsize)
> +{
> + struct irq_domain *domain;
> + irq_hw_number_t hwirq;
> + unsigned int irq, type;
> + int rc = -ENODEV;
> +
> + /* Find a domain which can translate the irq spec */
> + mutex_lock(&irq_domain_mutex);
> + list_for_each_entry(domain, &irq_domain_list, list) {
> + if (!domain->ops->dt_translate)
> + continue;
> + rc = domain->ops->dt_translate(domain, controller,
> + intspec, intsize, &hwirq, &type);
> + if (rc == 0)
> + break;
> + }
> + mutex_unlock(&irq_domain_mutex);
> +
> + if (rc != 0)
> + return 0;
> +
> + irq = irq_domain_map(domain, hwirq);
> + if (type != IRQ_TYPE_NONE)
> + irq_set_irq_type(irq, type);
> + return irq;
> +}
> +EXPORT_SYMBOL_GPL(irq_create_of_mapping);
> +
> +/**
> + * irq_dispose_mapping() - Discard a mapping created by irq_create_of_mapping()
> + * @irq: linux irq number to be discarded
> + *
> + * Calling this function indicates the caller no longer needs a reference to
> + * the linux irq number returned by a prior call to irq_create_of_mapping().
> + */
> +void irq_dispose_mapping(unsigned int irq)
> +{
> + struct irq_data *d = irq_get_irq_data(irq);
> + irq_domain_unmap(d->domain, d->hwirq);
> +}
> +EXPORT_SYMBOL_GPL(irq_dispose_mapping);
> +
> +#endif
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> [email protected]
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
--
Jazz is not dead. It just smells funny...