diff options
| -rw-r--r-- | drivers/irqchip/Kconfig | 5 | ||||
| -rw-r--r-- | drivers/irqchip/Makefile | 1 | ||||
| -rw-r--r-- | drivers/irqchip/irq-pic32-evic.c | 324 |
3 files changed, 330 insertions, 0 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index 4d7294e5d982..d5bafdd5f01f 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig | |||
| @@ -117,6 +117,11 @@ config ORION_IRQCHIP | |||
| 117 | select IRQ_DOMAIN | 117 | select IRQ_DOMAIN |
| 118 | select MULTI_IRQ_HANDLER | 118 | select MULTI_IRQ_HANDLER |
| 119 | 119 | ||
| 120 | config PIC32_EVIC | ||
| 121 | bool | ||
| 122 | select GENERIC_IRQ_CHIP | ||
| 123 | select IRQ_DOMAIN | ||
| 124 | |||
| 120 | config RENESAS_INTC_IRQPIN | 125 | config RENESAS_INTC_IRQPIN |
| 121 | bool | 126 | bool |
| 122 | select IRQ_DOMAIN | 127 | select IRQ_DOMAIN |
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 177f78f6e6d6..5278893ffb4e 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile | |||
| @@ -55,3 +55,4 @@ obj-$(CONFIG_RENESAS_H8S_INTC) += irq-renesas-h8s.o | |||
| 55 | obj-$(CONFIG_ARCH_SA1100) += irq-sa11x0.o | 55 | obj-$(CONFIG_ARCH_SA1100) += irq-sa11x0.o |
| 56 | obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o | 56 | obj-$(CONFIG_INGENIC_IRQ) += irq-ingenic.o |
| 57 | obj-$(CONFIG_IMX_GPCV2) += irq-imx-gpcv2.o | 57 | obj-$(CONFIG_IMX_GPCV2) += irq-imx-gpcv2.o |
| 58 | obj-$(CONFIG_PIC32_EVIC) += irq-pic32-evic.o | ||
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 @@ | |||
| 1 | /* | ||
| 2 | * Cristian Birsan <cristian.birsan@microchip.com> | ||
| 3 | * Joshua Henderson <joshua.henderson@microchip.com> | ||
| 4 | * Copyright (C) 2016 Microchip Technology Inc. All rights reserved. | ||
| 5 | * | ||
| 6 | * This program is free software; you can redistribute it and/or modify it | ||
| 7 | * under the terms of the GNU General Public License as published by the | ||
| 8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
| 9 | * option) any later version. | ||
| 10 | */ | ||
| 11 | #include <linux/kernel.h> | ||
| 12 | #include <linux/module.h> | ||
| 13 | #include <linux/interrupt.h> | ||
| 14 | #include <linux/irqdomain.h> | ||
| 15 | #include <linux/of_address.h> | ||
| 16 | #include <linux/slab.h> | ||
| 17 | #include <linux/io.h> | ||
| 18 | #include <linux/irqchip.h> | ||
| 19 | #include <linux/irq.h> | ||
| 20 | |||
| 21 | #include <asm/irq.h> | ||
| 22 | #include <asm/traps.h> | ||
| 23 | #include <asm/mach-pic32/pic32.h> | ||
| 24 | |||
| 25 | #define REG_INTCON 0x0000 | ||
| 26 | #define REG_INTSTAT 0x0020 | ||
| 27 | #define REG_IFS_OFFSET 0x0040 | ||
| 28 | #define REG_IEC_OFFSET 0x00C0 | ||
| 29 | #define REG_IPC_OFFSET 0x0140 | ||
| 30 | #define REG_OFF_OFFSET 0x0540 | ||
| 31 | |||
| 32 | #define MAJPRI_MASK 0x07 | ||
| 33 | #define SUBPRI_MASK 0x03 | ||
| 34 | #define PRIORITY_MASK 0x1F | ||
| 35 | |||
| 36 | #define PIC32_INT_PRI(pri, subpri) \ | ||
| 37 | ((((pri) & MAJPRI_MASK) << 2) | ((subpri) & SUBPRI_MASK)) | ||
| 38 | |||
| 39 | struct evic_chip_data { | ||
| 40 | u32 irq_types[NR_IRQS]; | ||
| 41 | u32 ext_irqs[8]; | ||
| 42 | }; | ||
| 43 | |||
| 44 | static struct irq_domain *evic_irq_domain; | ||
| 45 | static void __iomem *evic_base; | ||
| 46 | |||
| 47 | asmlinkage void __weak plat_irq_dispatch(void) | ||
| 48 | { | ||
| 49 | unsigned int irq, hwirq; | ||
| 50 | |||
| 51 | hwirq = readl(evic_base + REG_INTSTAT) & 0xFF; | ||
| 52 | irq = irq_linear_revmap(evic_irq_domain, hwirq); | ||
| 53 | do_IRQ(irq); | ||
| 54 | } | ||
| 55 | |||
| 56 | static struct evic_chip_data *irqd_to_priv(struct irq_data *data) | ||
| 57 | { | ||
| 58 | return (struct evic_chip_data *)data->domain->host_data; | ||
| 59 | } | ||
| 60 | |||
| 61 | static int pic32_set_ext_polarity(int bit, u32 type) | ||
| 62 | { | ||
| 63 | /* | ||
| 64 | * External interrupts can be either edge rising or edge falling, | ||
| 65 | * but not both. | ||
| 66 | */ | ||
| 67 | switch (type) { | ||
| 68 | case IRQ_TYPE_EDGE_RISING: | ||
| 69 | writel(BIT(bit), evic_base + PIC32_SET(REG_INTCON)); | ||
| 70 | break; | ||
| 71 | case IRQ_TYPE_EDGE_FALLING: | ||
| 72 | writel(BIT(bit), evic_base + PIC32_CLR(REG_INTCON)); | ||
| 73 | break; | ||
| 74 | default: | ||
| 75 | return -EINVAL; | ||
| 76 | } | ||
| 77 | |||
| 78 | return 0; | ||
| 79 | } | ||
| 80 | |||
| 81 | static int pic32_set_type_edge(struct irq_data *data, | ||
| 82 | unsigned int flow_type) | ||
| 83 | { | ||
| 84 | struct evic_chip_data *priv = irqd_to_priv(data); | ||
| 85 | int ret; | ||
| 86 | int i; | ||
| 87 | |||
| 88 | if (!(flow_type & IRQ_TYPE_EDGE_BOTH)) | ||
| 89 | return -EBADR; | ||
| 90 | |||
| 91 | /* set polarity for external interrupts only */ | ||
| 92 | for (i = 0; i < ARRAY_SIZE(priv->ext_irqs); i++) { | ||
| 93 | if (priv->ext_irqs[i] == data->hwirq) { | ||
| 94 | ret = pic32_set_ext_polarity(i + 1, flow_type); | ||
| 95 | if (ret) | ||
| 96 | return ret; | ||
| 97 | } | ||
| 98 | } | ||
| 99 | |||
| 100 | irqd_set_trigger_type(data, flow_type); | ||
| 101 | |||
| 102 | return IRQ_SET_MASK_OK; | ||
| 103 | } | ||
| 104 | |||
| 105 | static void pic32_bind_evic_interrupt(int irq, int set) | ||
| 106 | { | ||
| 107 | writel(set, evic_base + REG_OFF_OFFSET + irq * 4); | ||
| 108 | } | ||
| 109 | |||
| 110 | static void pic32_set_irq_priority(int irq, int priority) | ||
| 111 | { | ||
| 112 | u32 reg, shift; | ||
| 113 | |||
| 114 | reg = irq / 4; | ||
| 115 | shift = (irq % 4) * 8; | ||
| 116 | |||
| 117 | writel(PRIORITY_MASK << shift, | ||
| 118 | evic_base + PIC32_CLR(REG_IPC_OFFSET + reg * 0x10)); | ||
| 119 | writel(priority << shift, | ||
| 120 | evic_base + PIC32_SET(REG_IPC_OFFSET + reg * 0x10)); | ||
| 121 | } | ||
| 122 | |||
| 123 | #define IRQ_REG_MASK(_hwirq, _reg, _mask) \ | ||
| 124 | do { \ | ||
| 125 | _reg = _hwirq / 32; \ | ||
| 126 | _mask = 1 << (_hwirq % 32); \ | ||
| 127 | } while (0) | ||
| 128 | |||
| 129 | static int pic32_irq_domain_map(struct irq_domain *d, unsigned int virq, | ||
| 130 | irq_hw_number_t hw) | ||
| 131 | { | ||
| 132 | struct evic_chip_data *priv = d->host_data; | ||
| 133 | struct irq_data *data; | ||
| 134 | int ret; | ||
| 135 | u32 iecclr, ifsclr; | ||
| 136 | u32 reg, mask; | ||
| 137 | |||
| 138 | ret = irq_map_generic_chip(d, virq, hw); | ||
| 139 | if (ret) | ||
| 140 | return ret; | ||
| 141 | |||
| 142 | /* | ||
| 143 | * Piggyback on xlate function to move to an alternate chip as necessary | ||
| 144 | * at time of mapping instead of allowing the flow handler/chip to be | ||
| 145 | * changed later. This requires all interrupts to be configured through | ||
| 146 | * DT. | ||
| 147 | */ | ||
| 148 | if (priv->irq_types[hw] & IRQ_TYPE_SENSE_MASK) { | ||
| 149 | data = irq_domain_get_irq_data(d, virq); | ||
| 150 | irqd_set_trigger_type(data, priv->irq_types[hw]); | ||
| 151 | irq_setup_alt_chip(data, priv->irq_types[hw]); | ||
| 152 | } | ||
| 153 | |||
| 154 | IRQ_REG_MASK(hw, reg, mask); | ||
| 155 | |||
| 156 | iecclr = PIC32_CLR(REG_IEC_OFFSET + reg * 0x10); | ||
| 157 | ifsclr = PIC32_CLR(REG_IFS_OFFSET + reg * 0x10); | ||
| 158 | |||
| 159 | /* mask and clear flag */ | ||
| 160 | writel(mask, evic_base + iecclr); | ||
| 161 | writel(mask, evic_base + ifsclr); | ||
| 162 | |||
| 163 | /* default priority is required */ | ||
| 164 | pic32_set_irq_priority(hw, PIC32_INT_PRI(2, 0)); | ||
| 165 | |||
| 166 | return ret; | ||
| 167 | } | ||
| 168 | |||
| 169 | int pic32_irq_domain_xlate(struct irq_domain *d, struct device_node *ctrlr, | ||
| 170 | const u32 *intspec, unsigned int intsize, | ||
| 171 | irq_hw_number_t *out_hwirq, unsigned int *out_type) | ||
| 172 | { | ||
| 173 | struct evic_chip_data *priv = d->host_data; | ||
| 174 | |||
| 175 | if (WARN_ON(intsize < 2)) | ||
| 176 | return -EINVAL; | ||
| 177 | |||
| 178 | if (WARN_ON(intspec[0] >= NR_IRQS)) | ||
| 179 | return -EINVAL; | ||
| 180 | |||
| 181 | *out_hwirq = intspec[0]; | ||
| 182 | *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK; | ||
| 183 | |||
| 184 | priv->irq_types[intspec[0]] = intspec[1] & IRQ_TYPE_SENSE_MASK; | ||
| 185 | |||
| 186 | return 0; | ||
| 187 | } | ||
| 188 | |||
