diff options
Diffstat (limited to 'drivers/sh/intc')
| -rw-r--r-- | drivers/sh/intc/Kconfig | 6 | ||||
| -rw-r--r-- | drivers/sh/intc/Makefile | 2 | ||||
| -rw-r--r-- | drivers/sh/intc/access.c | 45 | ||||
| -rw-r--r-- | drivers/sh/intc/balancing.c | 2 | ||||
| -rw-r--r-- | drivers/sh/intc/chip.c | 53 | ||||
| -rw-r--r-- | drivers/sh/intc/core.c | 192 | ||||
| -rw-r--r-- | drivers/sh/intc/dynamic.c | 64 | ||||
| -rw-r--r-- | drivers/sh/intc/handle.c | 7 | ||||
| -rw-r--r-- | drivers/sh/intc/internals.h | 26 | ||||
| -rw-r--r-- | drivers/sh/intc/irqdomain.c | 68 | ||||
| -rw-r--r-- | drivers/sh/intc/userimask.c | 17 | ||||
| -rw-r--r-- | drivers/sh/intc/virq.c | 24 |
12 files changed, 279 insertions, 227 deletions
diff --git a/drivers/sh/intc/Kconfig b/drivers/sh/intc/Kconfig index c88cbccc62b..60228fae943 100644 --- a/drivers/sh/intc/Kconfig +++ b/drivers/sh/intc/Kconfig @@ -1,8 +1,12 @@ +config SH_INTC + def_bool y + select IRQ_DOMAIN + comment "Interrupt controller options" config INTC_USERIMASK bool "Userspace interrupt masking support" - depends on ARCH_SHMOBILE || (SUPERH && CPU_SH4A) + depends on (SUPERH && CPU_SH4A) || COMPILE_TEST help This enables support for hardware-assisted userspace hardirq masking. diff --git a/drivers/sh/intc/Makefile b/drivers/sh/intc/Makefile index bb5df868d77..54ec2a0643d 100644 --- a/drivers/sh/intc/Makefile +++ b/drivers/sh/intc/Makefile @@ -1,4 +1,4 @@ -obj-y := access.o chip.o core.o dynamic.o handle.o virq.o +obj-y := access.o chip.o core.o handle.o irqdomain.o virq.o obj-$(CONFIG_INTC_BALANCING) += balancing.o obj-$(CONFIG_INTC_USERIMASK) += userimask.o diff --git a/drivers/sh/intc/access.c b/drivers/sh/intc/access.c index f892ae1d212..114390f967d 100644 --- a/drivers/sh/intc/access.c +++ b/drivers/sh/intc/access.c @@ -75,54 +75,61 @@ unsigned long intc_get_field_from_handle(unsigned int value, unsigned int handle static unsigned long test_8(unsigned long addr, unsigned long h, unsigned long ignore) { - return intc_get_field_from_handle(__raw_readb(addr), h); + void __iomem *ptr = (void __iomem *)addr; + return intc_get_field_from_handle(__raw_readb(ptr), h); } static unsigned long test_16(unsigned long addr, unsigned long h, unsigned long ignore) { - return intc_get_field_from_handle(__raw_readw(addr), h); + void __iomem *ptr = (void __iomem *)addr; + return intc_get_field_from_handle(__raw_readw(ptr), h); } static unsigned long test_32(unsigned long addr, unsigned long h, unsigned long ignore) { - return intc_get_field_from_handle(__raw_readl(addr), h); + void __iomem *ptr = (void __iomem *)addr; + return intc_get_field_from_handle(__raw_readl(ptr), h); } static unsigned long write_8(unsigned long addr, unsigned long h, unsigned long data) { - __raw_writeb(intc_set_field_from_handle(0, data, h), addr); - (void)__raw_readb(addr); /* Defeat write posting */ + void __iomem *ptr = (void __iomem *)addr; + __raw_writeb(intc_set_field_from_handle(0, data, h), ptr); + (void)__raw_readb(ptr); /* Defeat write posting */ return 0; } static unsigned long write_16(unsigned long addr, unsigned long h, unsigned long data) { - __raw_writew(intc_set_field_from_handle(0, data, h), addr); - (void)__raw_readw(addr); /* Defeat write posting */ + void __iomem *ptr = (void __iomem *)addr; + __raw_writew(intc_set_field_from_handle(0, data, h), ptr); + (void)__raw_readw(ptr); /* Defeat write posting */ return 0; } static unsigned long write_32(unsigned long addr, unsigned long h, unsigned long data) { - __raw_writel(intc_set_field_from_handle(0, data, h), addr); - (void)__raw_readl(addr); /* Defeat write posting */ + void __iomem *ptr = (void __iomem *)addr; + __raw_writel(intc_set_field_from_handle(0, data, h), ptr); + (void)__raw_readl(ptr); /* Defeat write posting */ return 0; } static unsigned long modify_8(unsigned long addr, unsigned long h, unsigned long data) { + void __iomem *ptr = (void __iomem *)addr; unsigned long flags; unsigned int value; local_irq_save(flags); - value = intc_set_field_from_handle(__raw_readb(addr), data, h); - __raw_writeb(value, addr); - (void)__raw_readb(addr); /* Defeat write posting */ + value = intc_set_field_from_handle(__raw_readb(ptr), data, h); + __raw_writeb(value, ptr); + (void)__raw_readb(ptr); /* Defeat write posting */ local_irq_restore(flags); return 0; } @@ -130,12 +137,13 @@ static unsigned long modify_8(unsigned long addr, unsigned long h, static unsigned long modify_16(unsigned long addr, unsigned long h, unsigned long data) { + void __iomem *ptr = (void __iomem *)addr; unsigned long flags; unsigned int value; local_irq_save(flags); - value = intc_set_field_from_handle(__raw_readw(addr), data, h); - __raw_writew(value, addr); - (void)__raw_readw(addr); /* Defeat write posting */ + value = intc_set_field_from_handle(__raw_readw(ptr), data, h); + __raw_writew(value, ptr); + (void)__raw_readw(ptr); /* Defeat write posting */ local_irq_restore(flags); return 0; } @@ -143,12 +151,13 @@ static unsigned long modify_16(unsigned long addr, unsigned long h, static unsigned long modify_32(unsigned long addr, unsigned long h, unsigned long data) { + void __iomem *ptr = (void __iomem *)addr; unsigned long flags; unsigned int value; local_irq_save(flags); - value = intc_set_field_from_handle(__raw_readl(addr), data, h); - __raw_writel(value, addr); - (void)__raw_readl(addr); /* Defeat write posting */ + value = intc_set_field_from_handle(__raw_readl(ptr), data, h); + __raw_writel(value, ptr); + (void)__raw_readl(ptr); /* Defeat write posting */ local_irq_restore(flags); return 0; } diff --git a/drivers/sh/intc/balancing.c b/drivers/sh/intc/balancing.c index cec7a96f2c0..bc780807ccb 100644 --- a/drivers/sh/intc/balancing.c +++ b/drivers/sh/intc/balancing.c @@ -9,7 +9,7 @@ */ #include "internals.h" -static unsigned long dist_handle[NR_IRQS]; +static unsigned long dist_handle[INTC_NR_IRQS]; void intc_balancing_enable(unsigned int irq) { diff --git a/drivers/sh/intc/chip.c b/drivers/sh/intc/chip.c index f33e2dd9793..46427b48e2f 100644 --- a/drivers/sh/intc/chip.c +++ b/drivers/sh/intc/chip.c @@ -2,13 +2,14 @@ * IRQ chip definitions for INTC IRQs. * * Copyright (C) 2007, 2008 Magnus Damm - * Copyright (C) 2009, 2010 Paul Mundt + * Copyright (C) 2009 - 2012 Paul Mundt * * This file is subject to the terms and conditions of the GNU General Public * License. See the file "COPYING" in the main directory of this archive * for more details. */ #include <linux/cpumask.h> +#include <linux/bsearch.h> #include <linux/io.h> #include "internals.h" @@ -58,11 +59,6 @@ static void intc_disable(struct irq_data *data) } } -static int intc_set_wake(struct irq_data *data, unsigned int on) -{ - return 0; /* allow wakeup, but setup hardware in intc_suspend() */ -} - #ifdef CONFIG_SMP /* * This is held with the irq desc lock held, so we don't require any @@ -78,7 +74,7 @@ static int intc_set_affinity(struct irq_data *data, cpumask_copy(data->affinity, cpumask); - return 0; + return IRQ_SET_MASK_OK_NOCOPY; } #endif @@ -87,7 +83,7 @@ static void intc_mask_ack(struct irq_data *data) unsigned int irq = data->irq; struct intc_desc_int *d = get_intc_desc(irq); unsigned long handle = intc_get_ack_handle(irq); - unsigned long addr; + void __iomem *addr; intc_disable(data); @@ -95,7 +91,7 @@ static void intc_mask_ack(struct irq_data *data) if (handle) { unsigned int value; - addr = INTC_REG(d, _INTC_ADDR_D(handle), 0); + addr = (void __iomem *)INTC_REG(d, _INTC_ADDR_D(handle), 0); value = intc_set_field_from_handle(0, 1, handle); switch (_INTC_FN(handle)) { @@ -122,28 +118,12 @@ static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp, unsigned int nr_hp, unsigned int irq) { - int i; - - /* - * this doesn't scale well, but... - * - * this function should only be used for cerain uncommon - * operations such as intc_set_priority() and intc_set_type() - * and in those rare cases performance doesn't matter that much. - * keeping the memory footprint low is more important. - * - * one rather simple way to speed this up and still keep the - * memory footprint down is to make sure the array is sorted - * and then perform a bisect to lookup the irq. - */ - for (i = 0; i < nr_hp; i++) { - if ((hp + i)->irq != irq) - continue; + struct intc_handle_int key; - return hp + i; - } + key.irq = irq; + key.handle = 0; - return NULL; + return bsearch(&key, hp, nr_hp, sizeof(*hp), intc_handle_int_cmp); } int intc_set_priority(unsigned int irq, unsigned int prio) @@ -186,6 +166,9 @@ static unsigned char intc_irq_sense_table[IRQ_TYPE_SENSE_MASK + 1] = { !defined(CONFIG_CPU_SUBTYPE_SH7709) [IRQ_TYPE_LEVEL_HIGH] = VALID(3), #endif +#if defined(CONFIG_ARM) /* all recent SH-Mobile / R-Mobile ARM support this */ + [IRQ_TYPE_EDGE_BOTH] = VALID(4), +#endif }; static int intc_set_type(struct irq_data *data, unsigned int type) @@ -199,11 +182,16 @@ static int intc_set_type(struct irq_data *data, unsigned int type) if (!value) return -EINVAL; + value &= ~SENSE_VALID_FLAG; + ihp = intc_find_irq(d->sense, d->nr_sense, irq); if (ihp) { + /* PINT has 2-bit sense registers, should fail on EDGE_BOTH */ + if (value >= (1 << _INTC_WIDTH(ihp->handle))) + return -EINVAL; + addr = INTC_REG(d, _INTC_ADDR_E(ihp->handle), 0); - intc_reg_fns[_INTC_FN(ihp->handle)](addr, ihp->handle, - value & ~SENSE_VALID_FLAG); + intc_reg_fns[_INTC_FN(ihp->handle)](addr, ihp->handle, value); } return 0; @@ -215,10 +203,9 @@ struct irq_chip intc_irq_chip = { .irq_mask_ack = intc_mask_ack, .irq_enable = intc_enable, .irq_disable = intc_disable, - .irq_shutdown = intc_disable, .irq_set_type = intc_set_type, - .irq_set_wake = intc_set_wake, #ifdef CONFIG_SMP .irq_set_affinity = intc_set_affinity, #endif + .flags = IRQCHIP_SKIP_SET_WAKE, }; diff --git a/drivers/sh/intc/core.c b/drivers/sh/intc/core.c index 9739431092d..81f22980b2d 100644 --- a/drivers/sh/intc/core.c +++ b/drivers/sh/intc/core.c @@ -2,7 +2,7 @@ * Shared interrupt handling code for IPR and INTC2 types of IRQs. * * Copyright (C) 2007, 2008 Magnus Damm - * Copyright (C) 2009, 2010 Paul Mundt + * Copyright (C) 2009 - 2012 Paul Mundt * * Based on intc2.c and ipr.c * @@ -22,24 +22,29 @@ #include <linux/irq.h> #include <linux/io.h> #include <linux/slab.h> +#include <linux/stat.h> #include <linux/interrupt.h> #include <linux/sh_intc.h> -#include <linux/sysdev.h> +#include <linux/irqdomain.h> +#include <linux/device.h> +#include <linux/syscore_ops.h> #include <linux/list.h> #include <linux/spinlock.h> #include <linux/radix-tree.h> +#include <linux/export.h> +#include <linux/sort.h> #include "internals.h" LIST_HEAD(intc_list); DEFINE_RAW_SPINLOCK(intc_big_lock); -unsigned int nr_intc_controllers; +static unsigned int nr_intc_controllers; /* * Default priority level * - this needs to be at least 2 for 5-bit priorities on 7780 */ static unsigned int default_prio_level = 2; /* 2 - 16 */ -static unsigned int intc_prio_level[NR_IRQS]; /* for now */ +static unsigned int intc_prio_level[INTC_NR_IRQS]; /* for now */ unsigned int intc_get_dfl_prio_level(void) { @@ -62,7 +67,7 @@ void intc_set_prio_level(unsigned int irq, unsigned int level) static void intc_redirect_irq(unsigned int irq, struct irq_desc *desc) { - generic_handle_irq((unsigned int)get_irq_data(irq)); + generic_handle_irq((unsigned int)irq_get_handler_data(irq)); } static void __init intc_register_irq(struct intc_desc *desc, @@ -75,12 +80,6 @@ static void __init intc_register_irq(struct intc_desc *desc, unsigned int data[2], primary; unsigned long flags; - /* - * Register the IRQ position with the global IRQ map, then insert - * it in to the radix tree. - */ - irq_reserve_irq(irq); - raw_spin_lock_irqsave(&intc_big_lock, flags); radix_tree_insert(&d->tree, enum_id, intc_irq_xlate_get(irq)); raw_spin_unlock_irqrestore(&intc_big_lock, flags); @@ -115,9 +114,9 @@ static void __init intc_register_irq(struct intc_desc *desc, irq_data = irq_get_irq_data(irq); disable_irq_nosync(irq); - set_irq_chip_and_handler_name(irq, &d->chip, - handle_level_irq, "level"); - set_irq_chip_data(irq, (void *)data[primary]); + irq_set_chip_and_handler_name(irq, &d->chip, handle_level_irq, + "level"); + irq_set_chip_data(irq, (void *)data[primary]); /* * set priority level @@ -264,6 +263,9 @@ int __init register_intc_controller(struct intc_desc *desc) k += save_reg(d, k, hw->prio_regs[i].set_reg, smp); k += save_reg(d, k, hw->prio_regs[i].clr_reg, smp); } + + sort(d->prio, hw->nr_prio_regs, sizeof(*d->prio), + intc_handle_int_cmp, NULL); } if (hw->sense_regs) { @@ -274,6 +276,9 @@ int __init register_intc_controller(struct intc_desc *desc) for (i = 0; i < hw->nr_sense_regs; i++) k += save_reg(d, k, hw->sense_regs[i].reg, 0); + + sort(d->sense, hw->nr_sense_regs, sizeof(*d->sense), + intc_handle_int_cmp, NULL); } if (hw->subgroups) @@ -300,6 +305,8 @@ int __init register_intc_controller(struct intc_desc *desc) BUG_ON(k > 256); /* _INTC_ADDR_E() and _INTC_ADDR_D() are 8 bits */ + intc_irq_domain_init(d, hw); + /* register the vectors one by one */ for (i = 0; i < hw->nr_vectors; i++) { struct intc_vect *vect = hw->vectors + i; @@ -309,10 +316,18 @@ int __init register_intc_controller(struct intc_desc *desc) if (!vect->enum_id) continue; - res = irq_alloc_desc_at(irq, numa_node_id()); - if (res != irq && res != -EEXIST) { - pr_err("can't get irq_desc for %d\n", irq); - continue; + res = irq_create_identity_mapping(d->domain, irq); + if (unlikely(res)) { + if (res == -EEXIST) { + res = irq_domain_associate(d->domain, irq, irq); + if (unlikely(res)) { + pr_err("domain association failure\n"); + continue; + } + } else { + pr_err("can't identity map IRQ %d\n", irq); + continue; + } } intc_irq_xlate_set(irq, vect->enum_id, d); @@ -330,18 +345,29 @@ int __init register_intc_controller(struct intc_desc *desc) * IRQ support, each vector still needs to have * its own backing irq_desc. */ - res = irq_alloc_desc_at(irq2, numa_node_id()); - if (res != irq2 && res != -EEXIST) { - pr_err("can't get irq_desc for %d\n", irq2); - continue; + res = irq_create_identity_mapping(d->domain, irq2); + if (unlikely(res)) { + if (res == -EEXIST) { + res = irq_domain_associate(d->domain, + irq2, irq2); + if (unlikely(res)) { + pr_err("domain association " + "failure\n"); + continue; + } + } else { + pr_err("can't identity map IRQ %d\n", + irq); + continue; + } } vect2->enum_id = 0; /* redirect this interrupts to the first one */ - set_irq_chip(irq2, &dummy_irq_chip); - set_irq_chained_handler(irq2, intc_redirect_irq); - set_irq_data(irq2, (void *)irq); + irq_set_chip(irq2, &dummy_irq_chip); + irq_set_chained_handler(irq2, intc_redirect_irq); + irq_set_handler_data(irq2, (void *)irq); } } @@ -351,6 +377,8 @@ int __init register_intc_controller(struct intc_desc *desc) if (desc->force_enable) intc_enable_disable_enum(desc, d, desc->force_enable, 1); + d->skip_suspend = desc->skip_syscore_suspend; + nr_intc_controllers++; return 0; @@ -376,108 +404,108 @@ err0: return -ENOMEM; } -static ssize_t -show_intc_name(struct sys_device *dev, struct sysdev_attribute *attr, char *buf) +static int intc_suspend(void) { struct intc_desc_int *d; - d = container_of(dev, struct intc_desc_int, sysdev); + list_for_each_entry(d, &intc_list, list) { + int irq; - return sprintf(buf, "%s\n", d->chip.name); -} + if (d->skip_suspend) + continue; -static SYSDEV_ATTR(name, S_IRUGO, show_intc_name, NULL); + /* enable wakeup irqs belonging to this intc controller */ + for_each_active_irq(irq) { + struct irq_data *data; + struct irq_chip *chip; + + data = irq_get_irq_data(irq); + chip = irq_data_get_irq_chip(data); + if (chip != &d->chip) + continue; + if (irqd_is_wakeup_set(data)) + chip->irq_enable(data); + } + } + return 0; +} -static int intc_suspend(struct sys_device *dev, pm_message_t state) +static void intc_resume(void) { struct intc_desc_int *d; - struct irq_data *data; - struct irq_desc *desc; - struct irq_chip *chip; - int irq; - /* get intc controller associated with this sysdev */ - d = container_of(dev, struct intc_desc_int, sysdev); + list_for_each_entry(d, &intc_list, list) { + int irq; - switch (state.event) { - case PM_EVENT_ON: - if (d->state.event != PM_EVENT_FREEZE) - break; + if (d->skip_suspend) + continue; for_each_active_irq(irq) { - desc = irq_to_desc(irq); + struct irq_data *data; + struct irq_chip *chip; + data = irq_get_irq_data(irq); chip = irq_data_get_irq_chip(data); - /* * This will catch the redirect and VIRQ cases * due to the dummy_irq_chip being inserted. */ if (chip != &d->chip) continue; - if (desc->status & IRQ_DISABLED) + if (irqd_irq_disabled(data)) chip->irq_disable(data); else chip->irq_enable(data); } - break; - case PM_EVENT_FREEZE: - /* nothing has to be done */ - break; - case PM_EVENT_SUSPEND: - /* enable wakeup irqs belonging to this intc controller */ - for_each_active_irq(irq) { - desc = irq_to_desc(irq); - data = irq_get_irq_data(irq); - chip = irq_data_get_irq_chip(data); - - if (chip != &d->chip) - continue; - if ((desc->status & IRQ_WAKEUP)) - chip->irq_enable(data); - } - break; } +} - d->state = state; +struct syscore_ops intc_syscore_ops = { + .suspend = intc_suspend, + .resume = intc_resume, +}; - return 0; -} +struct bus_type intc_subsys = { + .name = "intc", + .dev_name = "intc", +}; -static int intc_resume(struct sys_device *dev) +static ssize_t +show_intc_name(struct device *dev, struct device_attribute *attr, char *buf) { - return intc_suspend(dev, PMSG_ON); + struct intc_desc_int *d; + + d = container_of(dev, struct intc_desc_int, dev); + + return sprintf(buf, "%s\n", d->chip.name); } -struct sysdev_class intc_sysdev_class = { - .name = "intc", - .suspend = intc_suspend, - .resume = intc_resume, -}; +static DEVICE_ATTR(name, S_IRUGO, show_intc_name, NULL); -/* register this intc as sysdev to allow suspend/resume */ -static int __init register_intc_sysdevs(void) +static int __init register_intc_devs(void) { struct intc_desc_int *d; int error; - error = sysdev_class_register(&intc_sysdev_class); + register_syscore_ops(&intc_syscore_ops); + + error = subsys_system_register(&intc_subsys, NULL); if (!error) { list_for_each_entry(d, &intc_list, list) { - d->sysdev.id = d->index; - d->sysdev.cls = &intc_sysdev_class; - error = sysdev_register(&d->sysdev); + d->dev.id = d->index; + d->dev.bus = &intc_subsys; + error = device_register(&d->dev); if (error == 0) - error = sysdev_create_file(&d->sysdev, - &attr_name); + error = device_create_file(&d->dev, + &dev_attr_name); if (error) break; } } if (error) - pr_err("sysdev registration error\n"); + pr_err("device registration error\n"); return error; } -device_initcall(register_intc_sysdevs); +device_initcall(register_intc_devs); diff --git a/drivers/sh/intc/dynamic.c b/drivers/sh/intc/dynamic.c deleted file mode 100644 index a3677c9dfe3..00000000000 --- a/drivers/sh/intc/dynamic.c +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Dynamic IRQ management - * - * Copyright (C) 2010 Paul Mundt - * - * Modelled after arch/x86/kernel/apic/io_apic.c - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ -#define pr_fmt(fmt) "intc: " fmt - -#include <linux/irq.h> -#include <linux/bitmap.h> -#include <linux/spinlock.h> -#include "internals.h" /* only for activate_irq() damage.. */ - -/* - * The IRQ bitmap provides a global map of bound IRQ vectors for a - * given platform. Allocation of IRQs are either static through the CPU - * vector map, or dynamic in the case of board mux vectors or MSI. - * - * As this is a central point for all IRQ controllers on the system, - * each of the available sources are mapped out here. This combined with - * sparseirq makes it quite trivial to keep the vector map tightly packed - * when dynamically creating IRQs, as well as tying in to otherwise - * unused irq_desc positions in the sparse array. - */ - -/* - * Dynamic IRQ allocation and deallocation - */ -unsigned int create_irq_nr(unsigned int irq_want, int node) -{ - int irq = irq_alloc_desc_at(irq_want, node); - if (irq < 0) - return 0; - - activate_irq(irq); - return irq; -} - -int create_irq(void) -{ - int irq = irq_alloc_desc(numa_node_id()); - if (irq >= 0) - activate_irq(irq); - - return irq; -} - -void destroy_irq(unsigned int irq) -{ - irq_free_desc(irq); -} - -void reserve_intc_vectors(struct intc_vect *vectors, unsigned int nr_vecs) -{ - int i; - - for (i = 0; i < nr_vecs; i++) - irq_reserve_irq(evt2irq(vectors[i].vect)); -} diff --git a/drivers/sh/intc/handle.c b/drivers/sh/intc/handle.c index 057ce56829b..7863a44918a 100644 --- a/drivers/sh/intc/handle.c +++ b/drivers/sh/intc/handle.c @@ -13,7 +13,7 @@ #include <linux/spinlock.h> #include "internals.h" -static unsigned long ack_handle[NR_IRQS]; +static unsigned long ack_handle[INTC_NR_IRQS]; static intc_enum __init intc_grp_id(struct intc_desc *desc, intc_enum enum_id) @@ -172,9 +172,8 @@ intc_get_prio_handle(struct intc_desc *desc, struct intc_desc_int *d, return 0; } -static unsigned int __init intc_ack_data(struct intc_desc *desc, - struct intc_desc_int *d, - intc_enum enum_id) +static unsigned int intc_ack_data(struct intc_desc *desc, + struct intc_desc_int *d, intc_enum enum_id) { struct intc_mask_reg *mr = desc->hw.ack_regs; unsigned int i, j, fn, mode; diff --git a/drivers/sh/intc/internals.h b/drivers/sh/intc/internals.h index 0cf8260971d..7dff08e2a07 100644 --- a/drivers/sh/intc/internals.h +++ b/drivers/sh/intc/internals.h @@ -1,10 +1,11 @@ #include <linux/sh_intc.h> #include <linux/irq.h> +#include <linux/irqdomain.h> #include <linux/list.h> #include <linux/kernel.h> #include <linux/types.h> #include <linux/radix-tree.h> -#include <linux/sysdev.h> +#include <linux/device.h> #define _INTC_MK(fn, mode, addr_e, addr_d, width, shift) \ ((shift) | ((width) << 5) | ((fn) << 9) | ((mode) << 13) | \ @@ -51,9 +52,8 @@ struct intc_subgroup_entry { struct intc_desc_int { struct list_head list; - struct sys_device sysdev; + struct device dev; struct radix_tree_root tree; - pm_message_t state; raw_spinlock_t lock; unsigned int index; unsigned long *reg; @@ -67,7 +67,9 @@ struct intc_desc_int { unsigned int nr_sense; struct intc_window *window; unsigned int nr_windows; + struct irq_domain *domain; struct irq_chip chip; + bool skip_suspend; }; @@ -87,7 +89,7 @@ enum { MODE_ENABLE_REG = 0, /* Bit(s) set -> interrupt enabled */ static inline struct intc_desc_int *get_intc_desc(unsigned int irq) { - struct irq_chip *chip = get_irq_chip(irq); + struct irq_chip *chip = irq_get_chip(irq); return container_of(chip, struct intc_desc_int, chip); } @@ -104,10 +106,18 @@ static inline void activate_irq(int irq) set_irq_flags(irq, IRQF_VALID); #else /* same effect on other architectures */ - set_irq_noprobe(irq); + irq_set_noprobe(irq); #endif } +static inline int intc_handle_int_cmp(const void *a, const void *b) +{ + const struct intc_handle_int *_a = a; + const struct intc_handle_int *_b = b; + + return _a->irq - _b->irq; +} + /* access.c */ extern unsigned long (*intc_reg_fns[])(unsigned long addr, unsigned long h, unsigned long data); @@ -157,8 +167,7 @@ void _intc_enable(struct irq_data *data, unsigned long handle); /* core.c */ extern struct list_head intc_list; extern raw_spinlock_t intc_big_lock; -extern unsigned int nr_intc_controllers; -extern struct sysdev_class intc_sysdev_class; +extern struct bus_type intc_subsys; unsigned int intc_get_dfl_prio_level(void); unsigned int intc_get_prio_level(unsigned int irq); @@ -180,6 +189,9 @@ unsigned long intc_get_ack_handle(unsigned int irq); void intc_enable_disable_enum(struct intc_desc *desc, struct intc_desc_int *d, intc_enum enum_id, int enable); +/* irqdomain.c */ +void intc_irq_domain_init(struct intc_desc_int *d, struct intc_hw_desc *hw); + /* virq.c */ void intc_subgroup_init(struct intc_desc *desc, struct intc_desc_int *d); void intc_irq_xlate_set(unsigned int irq, intc_enum id, struct intc_desc_int *d); diff --git a/drivers/sh/intc/irqdomain.c b/drivers/sh/intc/irqdomain.c new file mode 100644 index 00000000000..3968f1c3c5c --- /dev/null +++ b/drivers/sh/intc/irqdomain.c @@ -0,0 +1,68 @@ +/* + * IRQ domain support for SH INTC subsystem + * + * Copyright (C) 2012 Paul Mundt + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + */ +#define pr_fmt(fmt) "intc: " fmt + +#include <linux/irqdomain.h> +#include <linux/sh_intc.h> +#include <linux/export.h> +#include "internals.h" + +/** + * intc_irq_domain_evt_xlate() - Generic xlate for vectored IRQs. + * + * This takes care of exception vector to hwirq translation through + * by way of evt2irq() translation. + * + * Note: For platforms that use a flat vector space without INTEVT this + * basically just mimics irq_domain_xlate_onecell() by way of a nopped + * out evt2irq() implementation. + */ +static int intc_evt_xlate(struct irq_domain *d, struct device_node *ctrlr, + const u32 *intspec, unsigned int intsize, + unsigned long *out_hwirq, unsigned int *out_type) +{ + if (WARN_ON(intsize < 1)) + return -EINVAL; + + *out_hwirq = evt2irq(intspec[0]); + *out_type = IRQ_TYPE_NONE; + + return 0; +} + +static const struct irq_domain_ops intc_evt_ops = { + .xlate = intc_evt_xlate, +}; + +void __init intc_irq_domain_init(struct intc_desc_int *d, + struct intc_hw_desc *hw) +{ + unsigned int irq_base, irq_end; + + /* + * Quick linear revmap check + */ + irq_base = evt2irq(hw->vectors[0].vect); + irq_end = evt2irq(hw->vectors[hw->nr_vectors - 1].vect); + + /* + * Linear domains have a hard-wired assertion that IRQs start at + * 0 in order to make some performance optimizations. Lamely + * restrict the linear case to these conditions here, taking the + * tree penalty for linear cases with non-zero hwirq bases. + */ + if (irq_base == 0 && irq_end == (irq_base + hw->nr_vectors - 1)) + d->domain = irq_domain_add_linear(NULL, hw->nr_vectors, + &intc_evt_ops, NULL); + else + d->domain = irq_domain_add_tree(NULL, &intc_evt_ops, NULL); + + BUG_ON(!d->domain); +} diff --git a/drivers/sh/intc/userimask.c b/drivers/sh/intc/userimask.c index e32304b66cf..e649ceaaa41 100644 --- a/drivers/sh/intc/userimask.c +++ b/drivers/sh/intc/userimask.c @@ -10,24 +10,25 @@ #define pr_fmt(fmt) "intc: " fmt #include <linux/errno.h> -#include <linux/sysdev.h> +#include <linux/device.h> #include <linux/init.h> #include <linux/io.h> +#include <linux/stat.h> #include <asm/sizes.h> #include "internals.h" static void __iomem *uimask; static ssize_t -show_intc_userimask(struct sysdev_class *cls, - struct sysdev_class_attribute *attr, char *buf) +show_intc_userimask(struct device *dev, + struct device_attribute *attr, char *buf) { return sprintf(buf, "%d\n", (__raw_readl(uimask) >> 4) & 0xf); } static ssize_t -store_intc_userimask(struct sysdev_class *cls, - struct sysdev_class_attribute *attr, +store_intc_userimask(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) { unsigned long level; @@ -54,8 +55,8 @@ store_intc_userimask(struct sysdev_class *cls, return count; } -static SYSDEV_CLASS_ATTR(userimask, S_IRUSR | S_IWUSR, - show_intc_userimask, store_intc_userimask); +static DEVICE_ATTR(userimask, S_IRUSR | S_IWUSR, + show_intc_userimask, store_intc_userimask); static int __init userimask_sysdev_init(void) @@ -63,7 +64,7 @@ static int __init userimask_sysdev_init(void) if (unlikely(!uimask)) return -ENXIO; - return sysdev_class_create_file(&intc_sysdev_class, &attr_userimask); + return device_create_file(intc_subsys.dev_root, &dev_attr_userimask); } late_initcall(userimask_sysdev_init); diff --git a/drivers/sh/intc/virq.c b/drivers/sh/intc/virq.c index 4e0ff718116..f30ac9354ff 100644 --- a/drivers/sh/intc/virq.c +++ b/drivers/sh/intc/virq.c @@ -14,9 +14,10 @@ #include <linux/list.h> #include <linux/radix-tree.h> #include <linux/spinlock.h> +#include <linux/export.h> #include "internals.h" -static struct intc_map_entry intc_irq_xlate[NR_IRQS]; +static struct intc_map_entry intc_irq_xlate[INTC_NR_IRQS]; struct intc_virq_list { unsigned int irq; @@ -110,7 +111,7 @@ static void intc_virq_handler(unsigned int irq, struct irq_desc *desc) { struct irq_data *data = irq_get_irq_data(irq); struct irq_chip *chip = irq_data_get_irq_chip(data); - struct intc_virq_list *entry, *vlist = irq_data_get_irq_data(data); + struct intc_virq_list *entry, *vlist = irq_data_get_irq_handler_data(data); struct intc_desc_int *d = get_intc_desc(irq); chip->irq_mask_ack(data); @@ -118,7 +119,7 @@ static void intc_virq_handler(unsigned int irq, struct irq_desc *desc) for_each_virq(entry, vlist) { unsigned long addr, handle; - handle = (unsigned long)get_irq_data(entry->irq); + handle = (unsigned long)irq_get_handler_data(entry->irq); addr = INTC_REG(d, _INTC_ADDR_E(handle), 0); if (intc_reg_fns[_INTC_FN(handle)](addr, handle, 0)) @@ -218,24 +219,31 @@ restart: if (radix_tree_deref_retry(entry)) goto restart; - irq = create_irq(); + irq = irq_alloc_desc(numa_node_id()); if (unlikely(irq < 0)) { pr_err("no more free IRQs, bailing..\n"); break; } + activate_irq(irq); + pr_info("Setting up a chained VIRQ from %d -> %d\n", irq, entry->pirq); intc_irq_xlate_set(irq, entry->enum_id, d); - set_irq_chip_and_handler_name(irq, get_irq_chip(entry->pirq), + irq_set_chip_and_handler_name(irq, irq_get_chip(entry->pirq), handle_simple_irq, "virq"); - set_irq_chip_data(irq, get_irq_chip_data(entry->pirq)); + irq_set_chip_data(irq, irq_get_chip_data(entry->pirq)); + + irq_set_handler_data(irq, (void *)entry->handle); - set_irq_data(irq, (void *)entry->handle); + /* + * Set the virtual IRQ as non-threadable. + */ + irq_set_nothread(irq); - set_irq_chained_handler(entry->pirq, intc_virq_handler); + irq_set_chained_handler(entry->pirq, intc_virq_handler); add_virq_to_pirq(entry->pirq, irq); radix_tree_tag_clear(&d->tree, entry->enum_id, |
