diff options
Diffstat (limited to 'drivers/irqchip')
-rw-r--r-- | drivers/irqchip/Kconfig | 6 | ||||
-rw-r--r-- | drivers/irqchip/Makefile | 1 | ||||
-rw-r--r-- | drivers/irqchip/irq-atmel-aic-common.c | 2 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic-v3-its.c | 23 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic.c | 13 | ||||
-rw-r--r-- | drivers/irqchip/irq-mxs.c | 1 | ||||
-rw-r--r-- | drivers/irqchip/irq-pic32-evic.c | 324 | ||||
-rw-r--r-- | drivers/irqchip/irq-renesas-h8s.c | 10 | ||||
-rw-r--r-- | drivers/irqchip/irq-s3c24xx.c | 2 | ||||
-rw-r--r-- | drivers/irqchip/irq-sun4i.c | 1 | ||||
-rw-r--r-- | drivers/irqchip/irq-versatile-fpga.c | 5 |
11 files changed, 361 insertions, 27 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 11fc2a27fa2e..fb50911b3940 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig @@ -130,6 +130,11 @@ config ORION_IRQCHIP select IRQ_DOMAIN select MULTI_IRQ_HANDLER +config PIC32_EVIC + bool + select GENERIC_IRQ_CHIP + select IRQ_DOMAIN + config RENESAS_INTC_IRQPIN bool select IRQ_DOMAIN @@ -154,6 +159,7 @@ config TB10X_IRQC config TS4800_IRQ tristate "TS-4800 IRQ controller" select IRQ_DOMAIN + depends on HAS_IOMEM help Support for the TS-4800 FPGA IRQ controller diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index d4c2e4ebc308..18caacb60d58 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile @@ -58,3 +58,4 @@ obj-$(CONFIG_RENESAS_H8S_INTC) += irq-renesas-h8s.o obj-$(CONFIG_ARCH_SA1100) += irq-sa11x0.o obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o obj-$(CONFIG_IMX_GPCV2) += irq-imx-gpcv2.o +obj-$(CONFIG_PIC32_EVIC) += irq-pic32-evic.o diff --git a/drivers/irqchip/irq-atmel-aic-common.c b/drivers/irqchip/irq-atmel-aic-common.c index b12a5d58546f..37199b9b2cfa 100644 --- a/drivers/irqchip/irq-atmel-aic-common.c +++ b/drivers/irqchip/irq-atmel-aic-common.c @@ -86,7 +86,7 @@ int aic_common_set_priority(int priority, unsigned *val) priority > AT91_AIC_IRQ_MAX_PRIORITY) return -EINVAL; - *val &= AT91_AIC_PRIOR; + *val &= ~AT91_AIC_PRIOR; *val |= priority; return 0; diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index e23d1d18f9d6..0a73632b28d5 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c @@ -66,7 +66,10 @@ struct its_node { unsigned long phys_base; struct its_cmd_block *cmd_base; struct its_cmd_block *cmd_write; - void *tables[GITS_BASER_NR_REGS]; + struct { + void *base; + u32 order; + } tables[GITS_BASER_NR_REGS]; struct its_collection *collections; struct list_head its_device_list; u64 flags; @@ -807,9 +810,10 @@ static void its_free_tables(struct its_node *its) int i; for (i = 0; i < GITS_BASER_NR_REGS; i++) { - if (its->tables[i]) { - free_page((unsigned long)its->tables[i]); - its->tables[i] = NULL; + if (its->tables[i].base) { + free_pages((unsigned long)its->tables[i].base, + its->tables[i].order); + its->tables[i].base = NULL; } } } @@ -875,6 +879,7 @@ static int its_alloc_tables(const char *node_name, struct its_node *its) } alloc_size = (1 << order) * PAGE_SIZE; +retry_alloc_baser: alloc_pages = (alloc_size / psz); if (alloc_pages > GITS_BASER_PAGES_MAX) { alloc_pages = GITS_BASER_PAGES_MAX; @@ -889,7 +894,8 @@ static int its_alloc_tables(const char *node_name, struct its_node *its) goto out_free; } - its->tables[i] = base; + its->tables[i].base = base; + its->tables[i].order = order; retry_baser: val = (virt_to_phys(base) | @@ -938,13 +944,16 @@ retry_baser: * size and retry. If we reach 4K, then * something is horribly wrong... */ + free_pages((unsigned long)base, order); + its->tables[i].base = NULL; + switch (psz) { case SZ_16K: psz = SZ_4K; - goto retry_baser; + goto retry_alloc_baser; case SZ_64K: psz = SZ_16K; - goto retry_baser; + goto retry_alloc_baser; } } diff --git a/drivers/irqchip/irq-gic.c b/drivers/irqchip/irq-gic.c index 911758c056c1..8f9ebf714e2b 100644 --- a/drivers/irqchip/irq-gic.c +++ b/drivers/irqchip/irq-gic.c @@ -384,9 +384,6 @@ static struct irq_chip gic_chip = { .irq_unmask = gic_unmask_irq, .irq_eoi = gic_eoi_irq, .irq_set_type = gic_set_type, -#ifdef CONFIG_SMP - .irq_set_affinity = gic_set_affinity, -#endif .irq_get_irqchip_state = gic_irq_get_irqchip_state, .irq_set_irqchip_state = gic_irq_set_irqchip_state, .flags = IRQCHIP_SET_TYPE_MASKED | @@ -400,9 +397,6 @@ static struct irq_chip gic_eoimode1_chip = { .irq_unmask = gic_unmask_irq, .irq_eoi = gic_eoimode1_eoi_irq, .irq_set_type = gic_set_type, -#ifdef CONFIG_SMP - .irq_set_affinity = gic_set_affinity, -#endif .irq_get_irqchip_state = gic_irq_get_irqchip_state, .irq_set_irqchip_state = gic_irq_set_irqchip_state, .irq_set_vcpu_affinity = gic_irq_set_vcpu_affinity, @@ -443,7 +437,7 @@ static void gic_cpu_if_up(struct gic_chip_data *gic) u32 bypass = 0; u32 mode = 0; - if (static_key_true(&supports_deactivate)) + if (gic == &gic_data[0] && static_key_true(&supports_deactivate)) mode = GIC_CPU_CTRL_EOImodeNS; /* @@ -1039,6 +1033,11 @@ static void __init __gic_init_bases(unsigned int gic_nr, int irq_start, gic->chip.name = kasprintf(GFP_KERNEL, "GIC-%d", gic_nr); } +#ifdef CONFIG_SMP + if (gic_nr == 0) + gic->chip.irq_set_affinity = gic_set_affinity; +#endif + #ifdef CONFIG_GIC_NON_BANKED if (percpu_offset) { /* Frankein-GIC without banked registers... */ unsigned int cpu; diff --git a/drivers/irqchip/irq-mxs.c b/drivers/irqchip/irq-mxs.c index c22e2d40cb30..efe50845939d 100644 --- a/drivers/irqchip/irq-mxs.c +++ b/drivers/irqchip/irq-mxs.c @@ -241,6 +241,7 @@ static int __init asm9260_of_init(struct device_node *np, writel(0, icoll_priv.intr + i); icoll_add_domain(np, ASM9260_NUM_IRQS); + set_handle_irq(icoll_handle_irq); return 0; } diff --git a/drivers/irqchip/irq-pic32-evic.c b/drivers/irqchip/irq-pic32-evic.c new file mode 100644 index 000000000000..e7155db01d55 --- /dev/null +++ b/drivers/irqchip/irq-pic32-evic.c @@ -0,0 +1,324 @@ +/* + * Cristian Birsan <cristian.birsan@microchip.com> + * Joshua Henderson <joshua.henderson@microchip.com> + * Copyright (C) 2016 Microchip Technology Inc. All rights reserved. + * + * 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. + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include <linux/irqdomain.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <linux/io.h> +#include <linux/irqchip.h> +#include <linux/irq.h> + +#include <asm/irq.h> +#include <asm/traps.h> +#include <asm/mach-pic32/pic32.h> + +#define REG_INTCON 0x0000 +#define REG_INTSTAT 0x0020 +#define REG_IFS_OFFSET 0x0040 +#define REG_IEC_OFFSET 0x00C0 +#define REG_IPC_OFFSET 0x0140 +#define REG_OFF_OFFSET 0x0540 + +#define MAJPRI_MASK 0x07 +#define SUBPRI_MASK 0x03 +#define PRIORITY_MASK 0x1F + +#define PIC32_INT_PRI(pri, subpri) \ + ((((pri) & MAJPRI_MASK) << 2) | ((subpri) & SUBPRI_MASK)) + +struct evic_chip_data { + u32 irq_types[NR_IRQS]; + u32 ext_irqs[8]; +}; + +static struct irq_domain *evic_irq_domain; +static void __iomem *evic_base; + +asmlinkage void __weak plat_irq_dispatch(void) +{ + unsigned int irq, hwirq; + + hwirq = readl(evic_base + REG_INTSTAT) & 0xFF; + irq = irq_linear_revmap(evic_irq_domain, hwirq); + do_IRQ(irq); +} + +static struct evic_chip_data *irqd_to_priv(struct irq_data *data) +{ + return (struct evic_chip_data *)data->domain->host_data; +} + +static int pic32_set_ext_polarity(int bit, u32 type) +{ + /* + * External interrupts can be either edge rising or edge falling, + * but not both. + */ + switch (type) { + case IRQ_TYPE_EDGE_RISING: + writel(BIT(bit), evic_base + PIC32_SET(REG_INTCON)); + break; + case IRQ_TYPE_EDGE_FALLING: + writel(BIT(bit), evic_base + PIC32_CLR(REG_INTCON)); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int pic32_set_type_edge(struct irq_data *data, + unsigned int flow_type) +{ + struct evic_chip_data *priv = irqd_to_priv(data); + int ret; + int i; + + if (!(flow_type & IRQ_TYPE_EDGE_BOTH)) + return -EBADR; + + /* set polarity for external interrupts only */ + for (i = 0; i < ARRAY_SIZE(priv->ext_irqs); i++) { + if (priv->ext_irqs[i] == data->hwirq) { + ret = pic32_set_ext_polarity(i + 1, flow_type); + if (ret) + return ret; + } + } + + irqd_set_trigger_type(data, flow_type); + + return IRQ_SET_MASK_OK; +} + +static void pic32_bind_evic_interrupt(int irq, int set) +{ + writel(set, evic_base + REG_OFF_OFFSET + irq * 4); +} + +static void pic32_set_irq_priority(int irq, int priority) +{ + u32 reg, shift; + + reg = irq / 4; + shift = (irq % 4) * 8; + + writel(PRIORITY_MASK << shift, + evic_base + PIC32_CLR(REG_IPC_OFFSET + reg * 0x10)); + writel(priority << shift, + evic_base + PIC32_SET(REG_IPC_OFFSET + reg * 0x10)); +} + +#define IRQ_REG_MASK(_hwirq, _reg, _mask) \ + do { \ + _reg = _hwirq / 32; \ + _mask = 1 << (_hwirq % 32); \ + } while (0) + +static int pic32_irq_domain_map(struct irq_domain *d, unsigned int virq, + irq_hw_number_t hw) +{ + struct evic_chip_data *priv = d->host_data; + struct irq_data *data; + int ret; + u32 iecclr, ifsclr; + u32 reg, mask; + + ret = irq_map_generic_chip(d, virq, hw); + if (ret) + return ret; + + /* + * Piggyback on xlate function to move to an alternate chip as necessary + * at time of mapping instead of allowing the flow handler/chip to be + * changed later. This requires all interrupts to be configured through + * DT. + */ + if (priv->irq_types[hw] & IRQ_TYPE_SENSE_MASK) { + data = irq_domain_get_irq_data(d, virq); + irqd_set_trigger_type(data, priv->irq_types[hw]); + irq_setup_alt_chip(data, priv->irq_types[hw]); + } + + IRQ_REG_MASK(hw, reg, mask); + + iecclr = PIC32_CLR(REG_IEC_OFFSET + reg * 0x10); + ifsclr = PIC32_CLR(REG_IFS_OFFSET + reg * 0x10); + + /* mask and clear flag */ + writel(mask, evic_base + iecclr); + writel(mask, evic_base + ifsclr); + + /* default priority is required */ + pic32_set_irq_priority(hw, PIC32_INT_PRI(2, 0)); + + return ret; +} + +int pic32_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr, + const u32 *intspec, unsigned int intsize, + irq_hw_number_t *out_hwirq, unsigned int *out_type) +{ + struct evic_chip_data *priv = d->host_data; + + if (WARN_ON(intsize < 2)) + return -EINVAL; + + if (WARN_ON(intspec[0] >= NR_IRQS)) + return -EINVAL; + + *out_hwirq = intspec[0]; + *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK; + + priv->irq_types[intspec[0]] = intspec[1] & IRQ_TYPE_SENSE_MASK; + + return 0; +} + +static const struct irq_domain_ops pic32_irq_domain_ops = { + .map = pic32_irq_domain_map, + .xlate = pic32_irq_domain_xlate, +}; + +static void __init pic32_ext_irq_of_init(struct irq_domain *domain) +{ + struct device_node *node = irq_domain_get_of_node(domain); + struct evic_chip_data *priv = domain->host_data; + struct property *prop; + const __le32 *p; + u32 hwirq; + int i = 0; + const char *pname = "microchip,external-irqs"; + + of_property_for_each_u32(node, pname, prop, p, hwirq) { + if (i >= ARRAY_SIZE(priv->ext_irqs)) { + pr_warn("More than %d external irq, skip rest\n", + ARRAY_SIZE(priv->ext_irqs)); + break; + } + + priv->ext_irqs[i] = hwirq; + i++; + } +} + +static int __init pic32_of_init(struct device_node *node, + struct device_node *parent) +{ + struct irq_chip_generic *gc; + struct evic_chip_data *priv; + unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; + int nchips, ret; + int i; + + nchips = DIV_ROUND_UP(NR_IRQS, 32); + + evic_base = of_iomap(node, 0); + if (!evic_base) + return -ENOMEM; + + priv = kcalloc(nchips, sizeof(*priv), GFP_KERNEL); + if (!priv) { + ret = -ENOMEM; + goto err_iounmap; + } + + evic_irq_domain = irq_domain_add_linear(node, nchips * 32, + &pic32_irq_domain_ops, + priv); + if (!evic_irq_domain) { + ret = -ENOMEM; + goto err_free_priv; + } + + /* + * The PIC32 EVIC has a linear list of irqs and the type of each + * irq is determined by the hardware peripheral the EVIC is arbitrating. + * These irq types are defined in the datasheet as "persistent" and + * "non-persistent" which are mapped here to level and edge + * respectively. To manage the different flow handler requirements of + * each irq type, different chip_types are used. + */ + ret = irq_alloc_domain_generic_chips(evic_irq_domain, 32, 2, + "evic-level", handle_level_irq, + clr, 0, 0); + if (ret) + goto err_domain_remove; + + board_bind_eic_interrupt = &pic32_bind_evic_interrupt; + + for (i = 0; i < nchips; i++) { + u32 ifsclr = PIC32_CLR(REG_IFS_OFFSET + (i * 0x10)); + u32 iec = REG_IEC_OFFSET + (i * 0x10); + + gc = irq_get_domain_generic_chip(evic_irq_domain, i * 32); + + gc->reg_base = evic_base; + gc->unused = 0; + + /* + * Level/persistent interrupts have a special requirement that + * the condition generating the interrupt be cleared before the + * interrupt flag (ifs) can be cleared. chip.irq_eoi is used to + * complete the interrupt with an ack. + */ + gc->chip_types[0].type = IRQ_TYPE_LEVEL_MASK; + gc->chip_types[0].handler = handle_fasteoi_irq; + gc->chip_types[0].regs.ack = ifsclr; + gc->chip_types[0].regs.mask = iec; + gc->chip_types[0].chip.name = "evic-level"; + gc->chip_types[0].chip.irq_eoi = irq_gc_ack_set_bit; + gc->chip_types[0].chip.irq_mask = irq_gc_mask_clr_bit; + gc->chip_types[0].chip.irq_unmask = irq_gc_mask_set_bit; + gc->chip_types[0].chip.flags = IRQCHIP_SKIP_SET_WAKE; + + /* Edge interrupts */ + gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH; + gc->chip_types[1].handler = handle_edge_irq; + gc->chip_types[1].regs.ack = ifsclr; + gc->chip_types[1].regs.mask = iec; + gc->chip_types[1].chip.name = "evic-edge"; + gc->chip_types[1].chip.irq_ack = irq_gc_ack_set_bit; + gc->chip_types[1].chip.irq_mask = irq_gc_mask_clr_bit; + gc->chip_types[1].chip.irq_unmask = irq_gc_mask_set_bit; + gc->chip_types[1].chip.irq_set_type = pic32_set_type_edge; + gc->chip_types[1].chip.flags = IRQCHIP_SKIP_SET_WAKE; + + gc->private = &priv[i]; + } + + irq_set_default_host(evic_irq_domain); + + /* + * External interrupts have software configurable edge polarity. These + * interrupts are defined in DT allowing polarity to be configured only + * for these interrupts when requested. + */ + pic32_ext_irq_of_init(evic_irq_domain); + + return 0; + +err_domain_remove: + irq_domain_remove(evic_irq_domain); + +err_free_priv: + kfree(priv); + +err_iounmap: + iounmap(evic_base); + + return ret; +} + +IRQCHIP_DECLARE(pic32_evic, "microchip,pic32mzda-evic", pic32_of_init); diff --git a/drivers/irqchip/irq-renesas-h8s.c b/drivers/irqchip/irq-renesas-h8s.c index 8098ead1eb22..af8c6c61c824 100644 --- a/drivers/irqchip/irq-renesas-h8s.c +++ b/drivers/irqchip/irq-renesas-h8s.c @@ -40,8 +40,8 @@ static void h8s_disable_irq(struct irq_data *data) addr = IPRA + ((ipr_table[irq - 16] & 0xf0) >> 3); pos = (ipr_table[irq - 16] & 0x0f) * 4; pri = ~(0x000f << pos); - pri &= ctrl_inw(addr); - ctrl_outw(pri, addr); + pri &= readw(addr); + writew(pri, addr); } static void h8s_enable_irq(struct irq_data *data) @@ -54,9 +54,9 @@ static void h8s_enable_irq(struct irq_data *data) addr = IPRA + ((ipr_table[irq - 16] & 0xf0) >> 3); pos = (ipr_table[irq - 16] & 0x0f) * 4; pri = ~(0x000f << pos); - pri &= ctrl_inw(addr); + pri &= readw(addr); pri |= 1 << pos; - ctrl_outw(pri, addr); + writew(pri, addr); } struct irq_chip h8s_irq_chip = { @@ -90,7 +90,7 @@ static int __init h8s_intc_of_init(struct device_node *intc, /* All interrupt priority is 0 (disable) */ /* IPRA to IPRK */ for (n = 0; n <= 'k' - 'a'; n++) - ctrl_outw(0x0000, IPRA + (n * 2)); + writew(0x0000, IPRA + (n * 2)); domain = irq_domain_add_linear(intc, NR_IRQS, &irq_ops, NULL); BUG_ON(!domain); diff --git a/drivers/irqchip/irq-s3c24xx.c b/drivers/irqchip/irq-s3c24xx.c index c71914e8f596..5dc5a760c723 100644 --- a/drivers/irqchip/irq-s3c24xx.c +++ b/drivers/irqchip/irq-s3c24xx.c @@ -605,7 +605,7 @@ err: return ERR_PTR(ret); } -static struct s3c_irq_data init_eint[32] = { +static struct s3c_irq_data __maybe_unused init_eint[32] = { { .type = S3C_IRQTYPE_NONE, }, /* reserved */ { .type = S3C_IRQTYPE_NONE, }, /* reserved */ { .type = S3C_IRQTYPE_NONE, }, /* reserved */ diff --git a/drivers/irqchip/irq-sun4i.c b/drivers/irqchip/irq-sun4i.c index 0704362f4c82..376b28074e0d 100644 --- a/drivers/irqchip/irq-sun4i.c +++ b/drivers/irqchip/irq-sun4i.c @@ -22,7 +22,6 @@ #include <linux/of_irq.h> #include <asm/exception.h> -#include <asm/mach/irq.h> #define SUN4I_IRQ_VECTOR_REG 0x00 #define SUN4I_IRQ_PROTECTION_REG 0x08 diff --git a/drivers/irqchip/irq-versatile-fpga.c b/drivers/irqchip/irq-versatile-fpga.c index cadf104e3074..598ab3f0e0ac 100644 --- a/drivers/irqchip/irq-versatile-fpga.c +++ b/drivers/irqchip/irq-versatile-fpga.c @@ -210,12 +210,7 @@ int __init fpga_irq_of_init(struct device_node *node, parent_irq = -1; } -#ifdef CONFIG_ARCH_VERSATILE - fpga_irq_init(base, node->name, IRQ_SIC_START, parent_irq, valid_mask, - node); -#else fpga_irq_init(base, node->name, 0, parent_irq, valid_mask, node); -#endif writel(clear_mask, base + IRQ_ENABLE_CLEAR); writel(clear_mask, base + FIQ_ENABLE_CLEAR); |