From 2be6bb0c79c7fbda3425b65ee51c558bbaf4cf91 Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Tue, 5 Oct 2010 22:10:30 +0900 Subject: sh: intc: Split up the INTC code. This splits up the sh intc core in to something more vaguely resembling a subsystem. Most of the functionality was alread fairly well compartmentalized, and there were only a handful of interdependencies that needed to be resolved in the process. This also serves as future-proofing for the genirq and sparseirq rework, which will make some of the split out functionality wholly generic, allowing things to be killed off in place with minimal migration pain. Signed-off-by: Paul Mundt --- drivers/sh/intc/virq.c | 255 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 255 insertions(+) create mode 100644 drivers/sh/intc/virq.c (limited to 'drivers/sh/intc/virq.c') diff --git a/drivers/sh/intc/virq.c b/drivers/sh/intc/virq.c new file mode 100644 index 000000000000..643dfd4d2057 --- /dev/null +++ b/drivers/sh/intc/virq.c @@ -0,0 +1,255 @@ +/* + * Support for virtual IRQ subgroups. + * + * Copyright (C) 2010 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 +#include +#include +#include +#include +#include "internals.h" + +static struct intc_map_entry intc_irq_xlate[NR_IRQS]; + +struct intc_virq_list { + unsigned int irq; + struct intc_virq_list *next; +}; + +#define for_each_virq(entry, head) \ + for (entry = head; entry; entry = entry->next) + +/* + * Tags for the radix tree + */ +#define INTC_TAG_VIRQ_NEEDS_ALLOC 0 + +void intc_irq_xlate_set(unsigned int irq, intc_enum id, struct intc_desc_int *d) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&intc_big_lock, flags); + intc_irq_xlate[irq].enum_id = id; + intc_irq_xlate[irq].desc = d; + raw_spin_unlock_irqrestore(&intc_big_lock, flags); +} + +struct intc_map_entry *intc_irq_xlate_get(unsigned int irq) +{ + return intc_irq_xlate + irq; +} + +int intc_irq_lookup(const char *chipname, intc_enum enum_id) +{ + struct intc_map_entry *ptr; + struct intc_desc_int *d; + int irq = -1; + + list_for_each_entry(d, &intc_list, list) { + int tagged; + + if (strcmp(d->chip.name, chipname) != 0) + continue; + + /* + * Catch early lookups for subgroup VIRQs that have not + * yet been allocated an IRQ. This already includes a + * fast-path out if the tree is untagged, so there is no + * need to explicitly test the root tree. + */ + tagged = radix_tree_tag_get(&d->tree, enum_id, + INTC_TAG_VIRQ_NEEDS_ALLOC); + if (unlikely(tagged)) + break; + + ptr = radix_tree_lookup(&d->tree, enum_id); + if (ptr) { + irq = ptr - intc_irq_xlate; + break; + } + } + + return irq; +} +EXPORT_SYMBOL_GPL(intc_irq_lookup); + +static int add_virq_to_pirq(unsigned int irq, unsigned int virq) +{ + struct intc_virq_list **last, *entry; + struct irq_desc *desc = irq_to_desc(irq); + + /* scan for duplicates */ + last = (struct intc_virq_list **)&desc->handler_data; + for_each_virq(entry, desc->handler_data) { + if (entry->irq == virq) + return 0; + last = &entry->next; + } + + entry = kzalloc(sizeof(struct intc_virq_list), GFP_ATOMIC); + if (!entry) { + pr_err("can't allocate VIRQ mapping for %d\n", virq); + return -ENOMEM; + } + + entry->irq = virq; + + *last = entry; + + return 0; +} + +static void intc_virq_handler(unsigned int irq, struct irq_desc *desc) +{ + struct intc_virq_list *entry, *vlist = get_irq_data(irq); + struct intc_desc_int *d = get_intc_desc(irq); + + desc->chip->mask_ack(irq); + + for_each_virq(entry, vlist) { + unsigned long addr, handle; + + handle = (unsigned long)get_irq_data(entry->irq); + addr = INTC_REG(d, _INTC_ADDR_E(handle), 0); + + if (intc_reg_fns[_INTC_FN(handle)](addr, handle, 0)) + generic_handle_irq(entry->irq); + } + + desc->chip->unmask(irq); +} + +static unsigned long __init intc_subgroup_data(struct intc_subgroup *subgroup, + struct intc_desc_int *d, + unsigned int index) +{ + unsigned int fn = REG_FN_TEST_BASE + (subgroup->reg_width >> 3) - 1; + + return _INTC_MK(fn, MODE_ENABLE_REG, intc_get_reg(d, subgroup->reg), + 0, 1, (subgroup->reg_width - 1) - index); +} + +static void __init intc_subgroup_init_one(struct intc_desc *desc, + struct intc_desc_int *d, + struct intc_subgroup *subgroup) +{ + struct intc_map_entry *mapped; + unsigned int pirq; + unsigned long flags; + int i; + + mapped = radix_tree_lookup(&d->tree, subgroup->parent_id); + if (!mapped) { + WARN_ON(1); + return; + } + + pirq = mapped - intc_irq_xlate; + + raw_spin_lock_irqsave(&d->lock, flags); + + for (i = 0; i < ARRAY_SIZE(subgroup->enum_ids); i++) { + struct intc_subgroup_entry *entry; + int err; + + if (!subgroup->enum_ids[i]) + continue; + + entry = kmalloc(sizeof(*entry), GFP_NOWAIT); + if (!entry) + break; + + entry->pirq = pirq; + entry->enum_id = subgroup->enum_ids[i]; + entry->handle = intc_subgroup_data(subgroup, d, i); + + err = radix_tree_insert(&d->tree, entry->enum_id, entry); + if (unlikely(err < 0)) + break; + + radix_tree_tag_set(&d->tree, entry->enum_id, + INTC_TAG_VIRQ_NEEDS_ALLOC); + } + + raw_spin_unlock_irqrestore(&d->lock, flags); +} + +void __init intc_subgroup_init(struct intc_desc *desc, struct intc_desc_int *d) +{ + int i; + + if (!desc->hw.subgroups) + return; + + for (i = 0; i < desc->hw.nr_subgroups; i++) + intc_subgroup_init_one(desc, d, desc->hw.subgroups + i); +} + +static void __init intc_subgroup_map(struct intc_desc_int *d) +{ + struct intc_subgroup_entry *entries[32]; + unsigned long flags; + unsigned int nr_found; + int i; + + raw_spin_lock_irqsave(&d->lock, flags); + +restart: + nr_found = radix_tree_gang_lookup_tag_slot(&d->tree, + (void ***)entries, 0, ARRAY_SIZE(entries), + INTC_TAG_VIRQ_NEEDS_ALLOC); + + for (i = 0; i < nr_found; i++) { + struct intc_subgroup_entry *entry; + int irq; + + entry = radix_tree_deref_slot((void **)entries[i]); + if (unlikely(!entry)) + continue; + if (unlikely(entry == RADIX_TREE_RETRY)) + goto restart; + + irq = create_irq(); + if (unlikely(irq < 0)) { + pr_err("no more free IRQs, bailing..\n"); + break; + } + + 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), + handle_simple_irq, "virq"); + set_irq_chip_data(irq, get_irq_chip_data(entry->pirq)); + + set_irq_data(irq, (void *)entry->handle); + + set_irq_chained_handler(entry->pirq, intc_virq_handler); + add_virq_to_pirq(entry->pirq, irq); + + radix_tree_tag_clear(&d->tree, entry->enum_id, + INTC_TAG_VIRQ_NEEDS_ALLOC); + radix_tree_replace_slot((void **)entries[i], + &intc_irq_xlate[irq]); + } + + raw_spin_unlock_irqrestore(&d->lock, flags); +} + +void __init intc_finalize(void) +{ + struct intc_desc_int *d; + + list_for_each_entry(d, &intc_list, list) + if (radix_tree_tagged(&d->tree, INTC_TAG_VIRQ_NEEDS_ALLOC)) + intc_subgroup_map(d); +} -- cgit v1.2.3-59-g8ed1b From 26599a94dcadbed528a3e32a4f482a9766332f5b Mon Sep 17 00:00:00 2001 From: Paul Mundt Date: Wed, 27 Oct 2010 15:42:10 +0900 Subject: sh: intc: irq_data conversion. Signed-off-by: Paul Mundt --- drivers/sh/intc/chip.c | 53 +++++++++++++++++++++++++-------------------- drivers/sh/intc/core.c | 39 ++++++++++++++++++++++++--------- drivers/sh/intc/internals.h | 2 +- drivers/sh/intc/virq.c | 14 +++++++----- 4 files changed, 68 insertions(+), 40 deletions(-) (limited to 'drivers/sh/intc/virq.c') diff --git a/drivers/sh/intc/chip.c b/drivers/sh/intc/chip.c index 35c03706cc21..de885a0f917a 100644 --- a/drivers/sh/intc/chip.c +++ b/drivers/sh/intc/chip.c @@ -12,15 +12,16 @@ #include #include "internals.h" -void _intc_enable(unsigned int irq, unsigned long handle) +void _intc_enable(struct irq_data *data, unsigned long handle) { + unsigned int irq = data->irq; struct intc_desc_int *d = get_intc_desc(irq); unsigned long addr; unsigned int cpu; for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_E(handle)); cpu++) { #ifdef CONFIG_SMP - if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity)) + if (!cpumask_test_cpu(cpu, data->affinity)) continue; #endif addr = INTC_REG(d, _INTC_ADDR_E(handle), cpu); @@ -31,15 +32,16 @@ void _intc_enable(unsigned int irq, unsigned long handle) intc_balancing_enable(irq); } -static void intc_enable(unsigned int irq) +static void intc_enable(struct irq_data *data) { - _intc_enable(irq, (unsigned long)get_irq_chip_data(irq)); + _intc_enable(data, (unsigned long)irq_data_get_irq_chip_data(data)); } -static void intc_disable(unsigned int irq) +static void intc_disable(struct irq_data *data) { + unsigned int irq = data->irq; struct intc_desc_int *d = get_intc_desc(irq); - unsigned long handle = (unsigned long)get_irq_chip_data(irq); + unsigned long handle = (unsigned long)irq_data_get_irq_chip_data(data); unsigned long addr; unsigned int cpu; @@ -47,7 +49,7 @@ static void intc_disable(unsigned int irq) for (cpu = 0; cpu < SMP_NR(d, _INTC_ADDR_D(handle)); cpu++) { #ifdef CONFIG_SMP - if (!cpumask_test_cpu(cpu, irq_to_desc(irq)->affinity)) + if (!cpumask_test_cpu(cpu, data->affinity)) continue; #endif addr = INTC_REG(d, _INTC_ADDR_D(handle), cpu); @@ -56,7 +58,7 @@ static void intc_disable(unsigned int irq) } } -static int intc_set_wake(unsigned int irq, unsigned int on) +static int intc_set_wake(struct irq_data *data, unsigned int on) { return 0; /* allow wakeup, but setup hardware in intc_suspend() */ } @@ -67,24 +69,27 @@ static int intc_set_wake(unsigned int irq, unsigned int on) * additional locking here at the intc desc level. The affinity mask is * later tested in the enable/disable paths. */ -static int intc_set_affinity(unsigned int irq, const struct cpumask *cpumask) +static int intc_set_affinity(struct irq_data *data, + const struct cpumask *cpumask, + bool force) { if (!cpumask_intersects(cpumask, cpu_online_mask)) return -1; - cpumask_copy(irq_to_desc(irq)->affinity, cpumask); + cpumask_copy(data->affinity, cpumask); return 0; } #endif -static void intc_mask_ack(unsigned int irq) +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; - intc_disable(irq); + intc_disable(data); /* read register and write zero only to the associated bit */ if (handle) { @@ -144,6 +149,7 @@ static struct intc_handle_int *intc_find_irq(struct intc_handle_int *hp, int intc_set_priority(unsigned int irq, unsigned int prio) { struct intc_desc_int *d = get_intc_desc(irq); + struct irq_data *data = irq_get_irq_data(irq); struct intc_handle_int *ihp; if (!intc_get_prio_level(irq) || prio <= 1) @@ -162,7 +168,7 @@ int intc_set_priority(unsigned int irq, unsigned int prio) * priority level will be set during next enable() */ if (_INTC_FN(ihp->handle) != REG_FN_ERR) - _intc_enable(irq, ihp->handle); + _intc_enable(data, ihp->handle); } return 0; } @@ -181,8 +187,9 @@ static unsigned char intc_irq_sense_table[IRQ_TYPE_SENSE_MASK + 1] = { #endif }; -static int intc_set_type(unsigned int irq, unsigned int type) +static int intc_set_type(struct irq_data *data, unsigned int type) { + unsigned int irq = data->irq; struct intc_desc_int *d = get_intc_desc(irq); unsigned char value = intc_irq_sense_table[type & IRQ_TYPE_SENSE_MASK]; struct intc_handle_int *ihp; @@ -201,15 +208,15 @@ static int intc_set_type(unsigned int irq, unsigned int type) } struct irq_chip intc_irq_chip = { - .mask = intc_disable, - .unmask = intc_enable, - .mask_ack = intc_mask_ack, - .enable = intc_enable, - .disable = intc_disable, - .shutdown = intc_disable, - .set_type = intc_set_type, - .set_wake = intc_set_wake, + .irq_mask = intc_disable, + .irq_unmask = intc_enable, + .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 - .set_affinity = intc_set_affinity, + .irq_set_affinity = intc_set_affinity, #endif }; diff --git a/drivers/sh/intc/core.c b/drivers/sh/intc/core.c index 0801089828e7..338fad2a3fa4 100644 --- a/drivers/sh/intc/core.c +++ b/drivers/sh/intc/core.c @@ -71,6 +71,7 @@ static void __init intc_register_irq(struct intc_desc *desc, unsigned int irq) { struct intc_handle_int *hp; + struct irq_data *irq_data; unsigned int data[2], primary; unsigned long flags; @@ -111,6 +112,8 @@ static void __init intc_register_irq(struct intc_desc *desc, BUG_ON(!data[primary]); /* must have primary masking method */ + irq_data = irq_get_irq_data(irq); + disable_irq_nosync(irq); set_irq_chip_and_handler_name(irq, &d->chip, handle_level_irq, "level"); @@ -123,7 +126,7 @@ static void __init intc_register_irq(struct intc_desc *desc, /* enable secondary masking method if present */ if (data[!primary]) - _intc_enable(irq, data[!primary]); + _intc_enable(irq_data, data[!primary]); /* add irq to d->prio list if priority is available */ if (data[1]) { @@ -151,7 +154,7 @@ static void __init intc_register_irq(struct intc_desc *desc, } /* irq should be disabled by default */ - d->chip.mask(irq); + d->chip.irq_mask(irq_data); intc_set_ack_handle(irq, desc, d, enum_id); intc_set_dist_handle(irq, desc, d, enum_id); @@ -284,7 +287,7 @@ int __init register_intc_controller(struct intc_desc *desc) for (i = 0; i < hw->nr_ack_regs; i++) k += save_reg(d, k, hw->ack_regs[i].set_reg, 0); else - d->chip.mask_ack = d->chip.disable; + d->chip.irq_mask_ack = d->chip.irq_disable; /* disable bits matching force_disable before registering irqs */ if (desc->force_disable) @@ -387,7 +390,9 @@ static SYSDEV_ATTR(name, S_IRUGO, show_intc_name, NULL); static int intc_suspend(struct sys_device *dev, pm_message_t state) { 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 */ @@ -398,17 +403,24 @@ static int intc_suspend(struct sys_device *dev, pm_message_t state) if (d->state.event != PM_EVENT_FREEZE) break; - for_each_irq_desc(irq, desc) { + for_each_irq_nr(irq) { + desc = irq_to_desc(irq); + if (!desc) + continue; + + 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 (desc->chip != &d->chip) + if (chip != &d->chip) continue; if (desc->status & IRQ_DISABLED) - desc->chip->disable(irq); + chip->irq_disable(data); else - desc->chip->enable(irq); + chip->irq_enable(data); } break; case PM_EVENT_FREEZE: @@ -416,11 +428,18 @@ static int intc_suspend(struct sys_device *dev, pm_message_t state) break; case PM_EVENT_SUSPEND: /* enable wakeup irqs belonging to this intc controller */ - for_each_irq_desc(irq, desc) { - if (desc->chip != &d->chip) + for_each_irq_nr(irq) { + desc = irq_to_desc(irq); + if (!desc) + continue; + + data = irq_get_irq_data(irq); + chip = irq_data_get_irq_chip(data); + + if (chip != &d->chip) continue; if ((desc->status & IRQ_WAKEUP)) - desc->chip->enable(irq); + chip->irq_enable(data); } break; } diff --git a/drivers/sh/intc/internals.h b/drivers/sh/intc/internals.h index d49482c623fa..0cf8260971d4 100644 --- a/drivers/sh/intc/internals.h +++ b/drivers/sh/intc/internals.h @@ -152,7 +152,7 @@ intc_set_dist_handle(unsigned int irq, struct intc_desc *desc, /* chip.c */ extern struct irq_chip intc_irq_chip; -void _intc_enable(unsigned int irq, unsigned long handle); +void _intc_enable(struct irq_data *data, unsigned long handle); /* core.c */ extern struct list_head intc_list; diff --git a/drivers/sh/intc/virq.c b/drivers/sh/intc/virq.c index 643dfd4d2057..e5bf5d3c698e 100644 --- a/drivers/sh/intc/virq.c +++ b/drivers/sh/intc/virq.c @@ -83,11 +83,11 @@ EXPORT_SYMBOL_GPL(intc_irq_lookup); static int add_virq_to_pirq(unsigned int irq, unsigned int virq) { struct intc_virq_list **last, *entry; - struct irq_desc *desc = irq_to_desc(irq); + struct irq_data *data = irq_get_irq_data(irq); /* scan for duplicates */ - last = (struct intc_virq_list **)&desc->handler_data; - for_each_virq(entry, desc->handler_data) { + last = (struct intc_virq_list **)&data->handler_data; + for_each_virq(entry, data->handler_data) { if (entry->irq == virq) return 0; last = &entry->next; @@ -108,10 +108,12 @@ static int add_virq_to_pirq(unsigned int irq, unsigned int virq) static void intc_virq_handler(unsigned int irq, struct irq_desc *desc) { - struct intc_virq_list *entry, *vlist = get_irq_data(irq); + 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_desc_int *d = get_intc_desc(irq); - desc->chip->mask_ack(irq); + chip->irq_mask_ack(data); for_each_virq(entry, vlist) { unsigned long addr, handle; @@ -123,7 +125,7 @@ static void intc_virq_handler(unsigned int irq, struct irq_desc *desc) generic_handle_irq(entry->irq); } - desc->chip->unmask(irq); + chip->irq_unmask(data); } static unsigned long __init intc_subgroup_data(struct intc_subgroup *subgroup, -- cgit v1.2.3-59-g8ed1b