diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-02-20 13:52:23 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-02-20 13:52:23 -0500 |
commit | 1cd4027cfe33390dc3f442aea8e7caeeeaa861a1 (patch) | |
tree | 3a724adc4090ea83c0a364538308928ca910ab3f /drivers/irqchip | |
parent | 20dcfe1b7df4072a3c13bdb7506f7138125d0099 (diff) | |
parent | 3900dea4cda7c28d7921370bc4d22b08463ed94c (diff) |
Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull irq updates from Thomas Gleixner:
"This update provides:
- Yet another two irq controller chip drivers
- A few updates and fixes for GICV3
- A resource managed function for interrupt allocation
- Fixes, updates and enhancements all over the place"
* 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
irqchip/qcom: Fix error handling
genirq: Clarify logic calculating bogus irqreturn_t values
genirq/msi: Add stubs for get_cached_msi_msg/pci_write_msi_msg
genirq/devres: Use dev_name(dev) as default for devname
genirq: Fix /proc/interrupts output alignment
irqdesc: Add a resource managed version of irq_alloc_descs()
irqchip/gic-v3-its: Zero command on allocation
irqchip/gic-v3-its: Fix command buffer allocation
irqchip/mips-gic: Fix local interrupts
irqchip: Add a driver for Cortina Gemini
irqchip: DT bindings for Cortina Gemini irqchip
irqchip/gic-v3: Remove duplicate definition of GICD_TYPER_LPIS
irqchip/gic-v3-its: Rename MAPVI to MAPTI
irqchip/gic-v3-its: Drop deprecated GITS_BASER_TYPE_CPU
irqchip/gic-v3-its: Refactor command encoding
irqchip/gic-v3-its: Enable cacheable attribute Read-allocate hints
irqchip/qcom: Add IRQ combiner driver
ACPI: Add support for ResourceSource/IRQ domain mapping
ACPI: Generic GSI: Do not attempt to map non-GSI IRQs during bus scan
irq/platform-msi: Fix comment about maximal MSIs
Diffstat (limited to 'drivers/irqchip')
-rw-r--r-- | drivers/irqchip/Kconfig | 9 | ||||
-rw-r--r-- | drivers/irqchip/Makefile | 2 | ||||
-rw-r--r-- | drivers/irqchip/irq-gemini.c | 185 | ||||
-rw-r--r-- | drivers/irqchip/irq-gic-v3-its.c | 85 | ||||
-rw-r--r-- | drivers/irqchip/irq-mips-gic.c | 29 | ||||
-rw-r--r-- | drivers/irqchip/qcom-irq-combiner.c | 296 |
6 files changed, 566 insertions, 40 deletions
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig index ae96731cd2fb..125528f39e92 100644 --- a/drivers/irqchip/Kconfig +++ b/drivers/irqchip/Kconfig | |||
@@ -283,3 +283,12 @@ config EZNPS_GIC | |||
283 | config STM32_EXTI | 283 | config STM32_EXTI |
284 | bool | 284 | bool |
285 | select IRQ_DOMAIN | 285 | select IRQ_DOMAIN |
286 | |||
287 | config QCOM_IRQ_COMBINER | ||
288 | bool "QCOM IRQ combiner support" | ||
289 | depends on ARCH_QCOM && ACPI | ||
290 | select IRQ_DOMAIN | ||
291 | select IRQ_DOMAIN_HIERARCHY | ||
292 | help | ||
293 | Say yes here to add support for the IRQ combiner devices embedded | ||
294 | in Qualcomm Technologies chips. | ||
diff --git a/drivers/irqchip/Makefile b/drivers/irqchip/Makefile index 0e55d94065bf..152bc40b6762 100644 --- a/drivers/irqchip/Makefile +++ b/drivers/irqchip/Makefile | |||
@@ -6,6 +6,7 @@ obj-$(CONFIG_ATH79) += irq-ath79-misc.o | |||
6 | obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o | 6 | obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2835.o |
7 | obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2836.o | 7 | obj-$(CONFIG_ARCH_BCM2835) += irq-bcm2836.o |
8 | obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o | 8 | obj-$(CONFIG_ARCH_EXYNOS) += exynos-combiner.o |
9 | obj-$(CONFIG_ARCH_GEMINI) += irq-gemini.o | ||
9 | obj-$(CONFIG_ARCH_HIP04) += irq-hip04.o | 10 | obj-$(CONFIG_ARCH_HIP04) += irq-hip04.o |
10 | obj-$(CONFIG_ARCH_LPC32XX) += irq-lpc32xx.o | 11 | obj-$(CONFIG_ARCH_LPC32XX) += irq-lpc32xx.o |
11 | obj-$(CONFIG_ARCH_MMP) += irq-mmp.o | 12 | obj-$(CONFIG_ARCH_MMP) += irq-mmp.o |
@@ -75,3 +76,4 @@ obj-$(CONFIG_LS_SCFG_MSI) += irq-ls-scfg-msi.o | |||
75 | obj-$(CONFIG_EZNPS_GIC) += irq-eznps.o | 76 | obj-$(CONFIG_EZNPS_GIC) += irq-eznps.o |
76 | obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o | 77 | obj-$(CONFIG_ARCH_ASPEED) += irq-aspeed-vic.o |
77 | obj-$(CONFIG_STM32_EXTI) += irq-stm32-exti.o | 78 | obj-$(CONFIG_STM32_EXTI) += irq-stm32-exti.o |
79 | obj-$(CONFIG_QCOM_IRQ_COMBINER) += qcom-irq-combiner.o | ||
diff --git a/drivers/irqchip/irq-gemini.c b/drivers/irqchip/irq-gemini.c new file mode 100644 index 000000000000..495224c743ee --- /dev/null +++ b/drivers/irqchip/irq-gemini.c | |||
@@ -0,0 +1,185 @@ | |||
1 | /* | ||
2 | * irqchip for the Cortina Systems Gemini Copyright (C) 2017 Linus | ||
3 | * Walleij <linus.walleij@linaro.org> | ||
4 | * | ||
5 | * Based on arch/arm/mach-gemini/irq.c | ||
6 | * Copyright (C) 2001-2006 Storlink, Corp. | ||
7 | * Copyright (C) 2008-2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt> | ||
8 | */ | ||
9 | #include <linux/bitops.h> | ||
10 | #include <linux/irq.h> | ||
11 | #include <linux/io.h> | ||
12 | #include <linux/irqchip.h> | ||
13 | #include <linux/irqchip/versatile-fpga.h> | ||
14 | #include <linux/irqdomain.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/of.h> | ||
17 | #include <linux/of_address.h> | ||
18 | #include <linux/of_irq.h> | ||
19 | #include <linux/cpu.h> | ||
20 | |||
21 | #include <asm/exception.h> | ||
22 | #include <asm/mach/irq.h> | ||
23 | |||
24 | #define GEMINI_NUM_IRQS 32 | ||
25 | |||
26 | #define GEMINI_IRQ_SOURCE(base_addr) (base_addr + 0x00) | ||
27 | #define GEMINI_IRQ_MASK(base_addr) (base_addr + 0x04) | ||
28 | #define GEMINI_IRQ_CLEAR(base_addr) (base_addr + 0x08) | ||
29 | #define GEMINI_IRQ_MODE(base_addr) (base_addr + 0x0C) | ||
30 | #define GEMINI_IRQ_POLARITY(base_addr) (base_addr + 0x10) | ||
31 | #define GEMINI_IRQ_STATUS(base_addr) (base_addr + 0x14) | ||
32 | #define GEMINI_FIQ_SOURCE(base_addr) (base_addr + 0x20) | ||
33 | #define GEMINI_FIQ_MASK(base_addr) (base_addr + 0x24) | ||
34 | #define GEMINI_FIQ_CLEAR(base_addr) (base_addr + 0x28) | ||
35 | #define GEMINI_FIQ_MODE(base_addr) (base_addr + 0x2C) | ||
36 | #define GEMINI_FIQ_POLARITY(base_addr) (base_addr + 0x30) | ||
37 | #define GEMINI_FIQ_STATUS(base_addr) (base_addr + 0x34) | ||
38 | |||
39 | /** | ||
40 | * struct gemini_irq_data - irq data container for the Gemini IRQ controller | ||
41 | * @base: memory offset in virtual memory | ||
42 | * @chip: chip container for this instance | ||
43 | * @domain: IRQ domain for this instance | ||
44 | */ | ||
45 | struct gemini_irq_data { | ||
46 | void __iomem *base; | ||
47 | struct irq_chip chip; | ||
48 | struct irq_domain *domain; | ||
49 | }; | ||
50 | |||
51 | static void gemini_irq_mask(struct irq_data *d) | ||
52 | { | ||
53 | struct gemini_irq_data *g = irq_data_get_irq_chip_data(d); | ||
54 | unsigned int mask; | ||
55 | |||
56 | mask = readl(GEMINI_IRQ_MASK(g->base)); | ||
57 | mask &= ~BIT(irqd_to_hwirq(d)); | ||
58 | writel(mask, GEMINI_IRQ_MASK(g->base)); | ||
59 | } | ||
60 | |||
61 | static void gemini_irq_unmask(struct irq_data *d) | ||
62 | { | ||
63 | struct gemini_irq_data *g = irq_data_get_irq_chip_data(d); | ||
64 | unsigned int mask; | ||
65 | |||
66 | mask = readl(GEMINI_IRQ_MASK(g->base)); | ||
67 | mask |= BIT(irqd_to_hwirq(d)); | ||
68 | writel(mask, GEMINI_IRQ_MASK(g->base)); | ||
69 | } | ||
70 | |||
71 | static void gemini_irq_ack(struct irq_data *d) | ||
72 | { | ||
73 | struct gemini_irq_data *g = irq_data_get_irq_chip_data(d); | ||
74 | |||
75 | writel(BIT(irqd_to_hwirq(d)), GEMINI_IRQ_CLEAR(g->base)); | ||
76 | } | ||
77 | |||
78 | static int gemini_irq_set_type(struct irq_data *d, unsigned int trigger) | ||
79 | { | ||
80 | struct gemini_irq_data *g = irq_data_get_irq_chip_data(d); | ||
81 | int offset = irqd_to_hwirq(d); | ||
82 | u32 mode, polarity; | ||
83 | |||
84 | mode = readl(GEMINI_IRQ_MODE(g->base)); | ||
85 | polarity = readl(GEMINI_IRQ_POLARITY(g->base)); | ||
86 | |||
87 | if (trigger & (IRQ_TYPE_LEVEL_HIGH)) { | ||
88 | irq_set_handler_locked(d, handle_level_irq); | ||
89 | /* Disable edge detection */ | ||
90 | mode &= ~BIT(offset); | ||
91 | polarity &= ~BIT(offset); | ||
92 | } else if (trigger & IRQ_TYPE_EDGE_RISING) { | ||
93 | irq_set_handler_locked(d, handle_edge_irq); | ||
94 | mode |= BIT(offset); | ||
95 | polarity |= BIT(offset); | ||
96 | } else if (trigger & IRQ_TYPE_EDGE_FALLING) { | ||
97 | irq_set_handler_locked(d, handle_edge_irq); | ||
98 | mode |= BIT(offset); | ||
99 | polarity &= ~BIT(offset); | ||
100 | } else { | ||
101 | irq_set_handler_locked(d, handle_bad_irq); | ||
102 | pr_warn("GEMINI IRQ: no supported trigger selected for line %d\n", | ||
103 | offset); | ||
104 | } | ||
105 | |||
106 | writel(mode, GEMINI_IRQ_MODE(g->base)); | ||
107 | writel(polarity, GEMINI_IRQ_POLARITY(g->base)); | ||
108 | |||
109 | return 0; | ||
110 | } | ||
111 | |||
112 | static struct irq_chip gemini_irq_chip = { | ||
113 | .name = "GEMINI", | ||
114 | .irq_ack = gemini_irq_ack, | ||
115 | .irq_mask = gemini_irq_mask, | ||
116 | .irq_unmask = gemini_irq_unmask, | ||
117 | .irq_set_type = gemini_irq_set_type, | ||
118 | }; | ||
119 | |||
120 | /* Local static for the IRQ entry call */ | ||
121 | static struct gemini_irq_data girq; | ||
122 | |||
123 | asmlinkage void __exception_irq_entry gemini_irqchip_handle_irq(struct pt_regs *regs) | ||
124 | { | ||
125 | struct gemini_irq_data *g = &girq; | ||
126 | int irq; | ||
127 | u32 status; | ||
128 | |||
129 | while ((status = readl(GEMINI_IRQ_STATUS(g->base)))) { | ||
130 | irq = ffs(status) - 1; | ||
131 | handle_domain_irq(g->domain, irq, regs); | ||
132 | } | ||
133 | } | ||
134 | |||
135 | static int gemini_irqdomain_map(struct irq_domain *d, unsigned int irq, | ||
136 | irq_hw_number_t hwirq) | ||
137 | { | ||
138 | struct gemini_irq_data *g = d->host_data; | ||
139 | |||
140 | irq_set_chip_data(irq, g); | ||
141 | /* All IRQs should set up their type, flags as bad by default */ | ||
142 | irq_set_chip_and_handler(irq, &gemini_irq_chip, handle_bad_irq); | ||
143 | irq_set_probe(irq); | ||
144 | |||
145 | return 0; | ||
146 | } | ||
147 | |||
148 | static void gemini_irqdomain_unmap(struct irq_domain *d, unsigned int irq) | ||
149 | { | ||
150 | irq_set_chip_and_handler(irq, NULL, NULL); | ||
151 | irq_set_chip_data(irq, NULL); | ||
152 | } | ||
153 | |||
154 | static const struct irq_domain_ops gemini_irqdomain_ops = { | ||
155 | .map = gemini_irqdomain_map, | ||
156 | .unmap = gemini_irqdomain_unmap, | ||
157 | .xlate = irq_domain_xlate_onetwocell, | ||
158 | }; | ||
159 | |||
160 | int __init gemini_of_init_irq(struct device_node *node, | ||
161 | struct device_node *parent) | ||
162 | { | ||
163 | struct gemini_irq_data *g = &girq; | ||
164 | |||
165 | /* | ||
166 | * Disable the idle handler by default since it is buggy | ||
167 | * For more info see arch/arm/mach-gemini/idle.c | ||
168 | */ | ||
169 | cpu_idle_poll_ctrl(true); | ||
170 | |||
171 | g->base = of_iomap(node, 0); | ||
172 | WARN(!g->base, "unable to map gemini irq registers\n"); | ||
173 | |||
174 | /* Disable all interrupts */ | ||
175 | writel(0, GEMINI_IRQ_MASK(g->base)); | ||
176 | writel(0, GEMINI_FIQ_MASK(g->base)); | ||
177 | |||
178 | g->domain = irq_domain_add_simple(node, GEMINI_NUM_IRQS, 0, | ||
179 | &gemini_irqdomain_ops, g); | ||
180 | set_handle_irq(gemini_irqchip_handle_irq); | ||
181 | |||
182 | return 0; | ||
183 | } | ||
184 | IRQCHIP_DECLARE(gemini, "cortina,gemini-interrupt-controller", | ||
185 | gemini_of_init_irq); | ||
diff --git a/drivers/irqchip/irq-gic-v3-its.c b/drivers/irqchip/irq-gic-v3-its.c index 69b040f47d56..4a895c6d6805 100644 --- a/drivers/irqchip/irq-gic-v3-its.c +++ b/drivers/irqchip/irq-gic-v3-its.c | |||
@@ -161,7 +161,7 @@ struct its_cmd_desc { | |||
161 | struct its_device *dev; | 161 | struct its_device *dev; |
162 | u32 phys_id; | 162 | u32 phys_id; |
163 | u32 event_id; | 163 | u32 event_id; |
164 | } its_mapvi_cmd; | 164 | } its_mapti_cmd; |
165 | 165 | ||
166 | struct { | 166 | struct { |
167 | struct its_device *dev; | 167 | struct its_device *dev; |
@@ -193,58 +193,56 @@ struct its_cmd_block { | |||
193 | typedef struct its_collection *(*its_cmd_builder_t)(struct its_cmd_block *, | 193 | typedef struct its_collection *(*its_cmd_builder_t)(struct its_cmd_block *, |
194 | struct its_cmd_desc *); | 194 | struct its_cmd_desc *); |
195 | 195 | ||
196 | static void its_mask_encode(u64 *raw_cmd, u64 val, int h, int l) | ||
197 | { | ||
198 | u64 mask = GENMASK_ULL(h, l); | ||
199 | *raw_cmd &= ~mask; | ||
200 | *raw_cmd |= (val << l) & mask; | ||
201 | } | ||
202 | |||
196 | static void its_encode_cmd(struct its_cmd_block *cmd, u8 cmd_nr) | 203 | static void its_encode_cmd(struct its_cmd_block *cmd, u8 cmd_nr) |
197 | { | 204 | { |
198 | cmd->raw_cmd[0] &= ~0xffULL; | 205 | its_mask_encode(&cmd->raw_cmd[0], cmd_nr, 7, 0); |
199 | cmd->raw_cmd[0] |= cmd_nr; | ||
200 | } | 206 | } |
201 | 207 | ||
202 | static void its_encode_devid(struct its_cmd_block *cmd, u32 devid) | 208 | static void its_encode_devid(struct its_cmd_block *cmd, u32 devid) |
203 | { | 209 | { |
204 | cmd->raw_cmd[0] &= BIT_ULL(32) - 1; | 210 | its_mask_encode(&cmd->raw_cmd[0], devid, 63, 32); |
205 | cmd->raw_cmd[0] |= ((u64)devid) << 32; | ||
206 | } | 211 | } |
207 | 212 | ||
208 | static void its_encode_event_id(struct its_cmd_block *cmd, u32 id) | 213 | static void its_encode_event_id(struct its_cmd_block *cmd, u32 id) |
209 | { | 214 | { |
210 | cmd->raw_cmd[1] &= ~0xffffffffULL; | 215 | its_mask_encode(&cmd->raw_cmd[1], id, 31, 0); |
211 | cmd->raw_cmd[1] |= id; | ||
212 | } | 216 | } |
213 | 217 | ||
214 | static void its_encode_phys_id(struct its_cmd_block *cmd, u32 phys_id) | 218 | static void its_encode_phys_id(struct its_cmd_block *cmd, u32 phys_id) |
215 | { | 219 | { |
216 | cmd->raw_cmd[1] &= 0xffffffffULL; | 220 | its_mask_encode(&cmd->raw_cmd[1], phys_id, 63, 32); |
217 | cmd->raw_cmd[1] |= ((u64)phys_id) << 32; | ||
218 | } | 221 | } |
219 | 222 | ||
220 | static void its_encode_size(struct its_cmd_block *cmd, u8 size) | 223 | static void its_encode_size(struct its_cmd_block *cmd, u8 size) |
221 | { | 224 | { |
222 | cmd->raw_cmd[1] &= ~0x1fULL; | 225 | its_mask_encode(&cmd->raw_cmd[1], size, 4, 0); |
223 | cmd->raw_cmd[1] |= size & 0x1f; | ||
224 | } | 226 | } |
225 | 227 | ||
226 | static void its_encode_itt(struct its_cmd_block *cmd, u64 itt_addr) | 228 | static void its_encode_itt(struct its_cmd_block *cmd, u64 itt_addr) |
227 | { | 229 | { |
228 | cmd->raw_cmd[2] &= ~0xffffffffffffULL; | 230 | its_mask_encode(&cmd->raw_cmd[2], itt_addr >> 8, 50, 8); |
229 | cmd->raw_cmd[2] |= itt_addr & 0xffffffffff00ULL; | ||
230 | } | 231 | } |
231 | 232 | ||
232 | static void its_encode_valid(struct its_cmd_block *cmd, int valid) | 233 | static void its_encode_valid(struct its_cmd_block *cmd, int valid) |
233 | { | 234 | { |
234 | cmd->raw_cmd[2] &= ~(1ULL << 63); | 235 | its_mask_encode(&cmd->raw_cmd[2], !!valid, 63, 63); |
235 | cmd->raw_cmd[2] |= ((u64)!!valid) << 63; | ||
236 | } | 236 | } |
237 | 237 | ||
238 | static void its_encode_target(struct its_cmd_block *cmd, u64 target_addr) | 238 | static void its_encode_target(struct its_cmd_block *cmd, u64 target_addr) |
239 | { | 239 | { |
240 | cmd->raw_cmd[2] &= ~(0xffffffffULL << 16); | 240 | its_mask_encode(&cmd->raw_cmd[2], target_addr >> 16, 50, 16); |
241 | cmd->raw_cmd[2] |= (target_addr & (0xffffffffULL << 16)); | ||
242 | } | 241 | } |
243 | 242 | ||
244 | static void its_encode_collection(struct its_cmd_block *cmd, u16 col) | 243 | static void its_encode_collection(struct its_cmd_block *cmd, u16 col) |
245 | { | 244 | { |
246 | cmd->raw_cmd[2] &= ~0xffffULL; | 245 | its_mask_encode(&cmd->raw_cmd[2], col, 15, 0); |
247 | cmd->raw_cmd[2] |= col; | ||
248 | } | 246 | } |
249 | 247 | ||
250 | static inline void its_fixup_cmd(struct its_cmd_block *cmd) | 248 | static inline void its_fixup_cmd(struct its_cmd_block *cmd) |
@@ -289,18 +287,18 @@ static struct its_collection *its_build_mapc_cmd(struct its_cmd_block *cmd, | |||
289 | return desc->its_mapc_cmd.col; | 287 | return desc->its_mapc_cmd.col; |
290 | } | 288 | } |
291 | 289 | ||
292 | static struct its_collection *its_build_mapvi_cmd(struct its_cmd_block *cmd, | 290 | static struct its_collection *its_build_mapti_cmd(struct its_cmd_block *cmd, |
293 | struct its_cmd_desc *desc) | 291 | struct its_cmd_desc *desc) |
294 | { | 292 | { |
295 | struct its_collection *col; | 293 | struct its_collection *col; |
296 | 294 | ||
297 | col = dev_event_to_col(desc->its_mapvi_cmd.dev, | 295 | col = dev_event_to_col(desc->its_mapti_cmd.dev, |
298 | desc->its_mapvi_cmd.event_id); | 296 | desc->its_mapti_cmd.event_id); |
299 | 297 | ||
300 | its_encode_cmd(cmd, GITS_CMD_MAPVI); | 298 | its_encode_cmd(cmd, GITS_CMD_MAPTI); |
301 | its_encode_devid(cmd, desc->its_mapvi_cmd.dev->device_id); | 299 | its_encode_devid(cmd, desc->its_mapti_cmd.dev->device_id); |
302 | its_encode_event_id(cmd, desc->its_mapvi_cmd.event_id); | 300 | its_encode_event_id(cmd, desc->its_mapti_cmd.event_id); |
303 | its_encode_phys_id(cmd, desc->its_mapvi_cmd.phys_id); | 301 | its_encode_phys_id(cmd, desc->its_mapti_cmd.phys_id); |
304 | its_encode_collection(cmd, col->col_id); | 302 | its_encode_collection(cmd, col->col_id); |
305 | 303 | ||
306 | its_fixup_cmd(cmd); | 304 | its_fixup_cmd(cmd); |
@@ -413,6 +411,12 @@ static struct its_cmd_block *its_allocate_entry(struct its_node *its) | |||
413 | if (its->cmd_write == (its->cmd_base + ITS_CMD_QUEUE_NR_ENTRIES)) | 411 | if (its->cmd_write == (its->cmd_base + ITS_CMD_QUEUE_NR_ENTRIES)) |
414 | its->cmd_write = its->cmd_base; | 412 | its->cmd_write = its->cmd_base; |
415 | 413 | ||
414 | /* Clear command */ | ||
415 | cmd->raw_cmd[0] = 0; | ||
416 | cmd->raw_cmd[1] = 0; | ||
417 | cmd->raw_cmd[2] = 0; | ||
418 | cmd->raw_cmd[3] = 0; | ||
419 | |||
416 | return cmd; | 420 | return cmd; |
417 | } | 421 | } |
418 | 422 | ||
@@ -531,15 +535,15 @@ static void its_send_mapc(struct its_node *its, struct its_collection *col, | |||
531 | its_send_single_command(its, its_build_mapc_cmd, &desc); | 535 | its_send_single_command(its, its_build_mapc_cmd, &desc); |
532 | } | 536 | } |
533 | 537 | ||
534 | static void its_send_mapvi(struct its_device *dev, u32 irq_id, u32 id) | 538 | static void its_send_mapti(struct its_device *dev, u32 irq_id, u32 id) |
535 | { | 539 | { |
536 | struct its_cmd_desc desc; | 540 | struct its_cmd_desc desc; |
537 | 541 | ||
538 | desc.its_mapvi_cmd.dev = dev; | 542 | desc.its_mapti_cmd.dev = dev; |
539 | desc.its_mapvi_cmd.phys_id = irq_id; | 543 | desc.its_mapti_cmd.phys_id = irq_id; |
540 | desc.its_mapvi_cmd.event_id = id; | 544 | desc.its_mapti_cmd.event_id = id; |
541 | 545 | ||
542 | its_send_single_command(dev->its, its_build_mapvi_cmd, &desc); | 546 | its_send_single_command(dev->its, its_build_mapti_cmd, &desc); |
543 | } | 547 | } |
544 | 548 | ||
545 | static void its_send_movi(struct its_device *dev, | 549 | static void its_send_movi(struct its_device *dev, |
@@ -824,7 +828,7 @@ static int __init its_alloc_lpi_tables(void) | |||
824 | static const char *its_base_type_string[] = { | 828 | static const char *its_base_type_string[] = { |
825 | [GITS_BASER_TYPE_DEVICE] = "Devices", | 829 | [GITS_BASER_TYPE_DEVICE] = "Devices", |
826 | [GITS_BASER_TYPE_VCPU] = "Virtual CPUs", | 830 | [GITS_BASER_TYPE_VCPU] = "Virtual CPUs", |
827 | [GITS_BASER_TYPE_CPU] = "Physical CPUs", | 831 | [GITS_BASER_TYPE_RESERVED3] = "Reserved (3)", |
828 | [GITS_BASER_TYPE_COLLECTION] = "Interrupt Collections", | 832 | [GITS_BASER_TYPE_COLLECTION] = "Interrupt Collections", |
829 | [GITS_BASER_TYPE_RESERVED5] = "Reserved (5)", | 833 | [GITS_BASER_TYPE_RESERVED5] = "Reserved (5)", |
830 | [GITS_BASER_TYPE_RESERVED6] = "Reserved (6)", | 834 | [GITS_BASER_TYPE_RESERVED6] = "Reserved (6)", |
@@ -960,7 +964,7 @@ static bool its_parse_baser_device(struct its_node *its, struct its_baser *baser | |||
960 | u32 psz, u32 *order) | 964 | u32 psz, u32 *order) |
961 | { | 965 | { |
962 | u64 esz = GITS_BASER_ENTRY_SIZE(its_read_baser(its, baser)); | 966 | u64 esz = GITS_BASER_ENTRY_SIZE(its_read_baser(its, baser)); |
963 | u64 val = GITS_BASER_InnerShareable | GITS_BASER_WaWb; | 967 | u64 val = GITS_BASER_InnerShareable | GITS_BASER_RaWaWb; |
964 | u32 ids = its->device_ids; | 968 | u32 ids = its->device_ids; |
965 | u32 new_order = *order; | 969 | u32 new_order = *order; |
966 | bool indirect = false; | 970 | bool indirect = false; |
@@ -1025,7 +1029,7 @@ static int its_alloc_tables(struct its_node *its) | |||
1025 | u64 typer = gic_read_typer(its->base + GITS_TYPER); | 1029 | u64 typer = gic_read_typer(its->base + GITS_TYPER); |
1026 | u32 ids = GITS_TYPER_DEVBITS(typer); | 1030 | u32 ids = GITS_TYPER_DEVBITS(typer); |
1027 | u64 shr = GITS_BASER_InnerShareable; | 1031 | u64 shr = GITS_BASER_InnerShareable; |
1028 | u64 cache = GITS_BASER_WaWb; | 1032 | u64 cache = GITS_BASER_RaWaWb; |
1029 | u32 psz = SZ_64K; | 1033 | u32 psz = SZ_64K; |
1030 | int err, i; | 1034 | int err, i; |
1031 | 1035 | ||
@@ -1122,7 +1126,7 @@ static void its_cpu_init_lpis(void) | |||
1122 | /* set PROPBASE */ | 1126 | /* set PROPBASE */ |
1123 | val = (page_to_phys(gic_rdists->prop_page) | | 1127 | val = (page_to_phys(gic_rdists->prop_page) | |
1124 | GICR_PROPBASER_InnerShareable | | 1128 | GICR_PROPBASER_InnerShareable | |
1125 | GICR_PROPBASER_WaWb | | 1129 | GICR_PROPBASER_RaWaWb | |
1126 | ((LPI_NRBITS - 1) & GICR_PROPBASER_IDBITS_MASK)); | 1130 | ((LPI_NRBITS - 1) & GICR_PROPBASER_IDBITS_MASK)); |
1127 | 1131 | ||
1128 | gicr_write_propbaser(val, rbase + GICR_PROPBASER); | 1132 | gicr_write_propbaser(val, rbase + GICR_PROPBASER); |
@@ -1147,7 +1151,7 @@ static void its_cpu_init_lpis(void) | |||
1147 | /* set PENDBASE */ | 1151 | /* set PENDBASE */ |
1148 | val = (page_to_phys(pend_page) | | 1152 | val = (page_to_phys(pend_page) | |
1149 | GICR_PENDBASER_InnerShareable | | 1153 | GICR_PENDBASER_InnerShareable | |
1150 | GICR_PENDBASER_WaWb); | 1154 | GICR_PENDBASER_RaWaWb); |
1151 | 1155 | ||
1152 | gicr_write_pendbaser(val, rbase + GICR_PENDBASER); | 1156 | gicr_write_pendbaser(val, rbase + GICR_PENDBASER); |
1153 | tmp = gicr_read_pendbaser(rbase + GICR_PENDBASER); | 1157 | tmp = gicr_read_pendbaser(rbase + GICR_PENDBASER); |
@@ -1498,7 +1502,7 @@ static void its_irq_domain_activate(struct irq_domain *domain, | |||
1498 | its_dev->event_map.col_map[event] = cpumask_first(cpu_mask); | 1502 | its_dev->event_map.col_map[event] = cpumask_first(cpu_mask); |
1499 | 1503 | ||
1500 | /* Map the GIC IRQ and event to the device */ | 1504 | /* Map the GIC IRQ and event to the device */ |
1501 | its_send_mapvi(its_dev, d->hwirq, event); | 1505 | its_send_mapti(its_dev, d->hwirq, event); |
1502 | } | 1506 | } |
1503 | 1507 | ||
1504 | static void its_irq_domain_deactivate(struct irq_domain *domain, | 1508 | static void its_irq_domain_deactivate(struct irq_domain *domain, |
@@ -1693,7 +1697,8 @@ static int __init its_probe_one(struct resource *res, | |||
1693 | its->ite_size = ((gic_read_typer(its_base + GITS_TYPER) >> 4) & 0xf) + 1; | 1697 | its->ite_size = ((gic_read_typer(its_base + GITS_TYPER) >> 4) & 0xf) + 1; |
1694 | its->numa_node = numa_node; | 1698 | its->numa_node = numa_node; |
1695 | 1699 | ||
1696 | its->cmd_base = kzalloc(ITS_CMD_QUEUE_SZ, GFP_KERNEL); | 1700 | its->cmd_base = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, |
1701 | get_order(ITS_CMD_QUEUE_SZ)); | ||
1697 | if (!its->cmd_base) { | 1702 | if (!its->cmd_base) { |
1698 | err = -ENOMEM; | 1703 | err = -ENOMEM; |
1699 | goto out_free_its; | 1704 | goto out_free_its; |
@@ -1711,7 +1716,7 @@ static int __init its_probe_one(struct resource *res, | |||
1711 | goto out_free_tables; | 1716 | goto out_free_tables; |
1712 | 1717 | ||
1713 | baser = (virt_to_phys(its->cmd_base) | | 1718 | baser = (virt_to_phys(its->cmd_base) | |
1714 | GITS_CBASER_WaWb | | 1719 | GITS_CBASER_RaWaWb | |
1715 | GITS_CBASER_InnerShareable | | 1720 | GITS_CBASER_InnerShareable | |
1716 | (ITS_CMD_QUEUE_SZ / SZ_4K - 1) | | 1721 | (ITS_CMD_QUEUE_SZ / SZ_4K - 1) | |
1717 | GITS_CBASER_VALID); | 1722 | GITS_CBASER_VALID); |
@@ -1751,7 +1756,7 @@ static int __init its_probe_one(struct resource *res, | |||
1751 | out_free_tables: | 1756 | out_free_tables: |
1752 | its_free_tables(its); | 1757 | its_free_tables(its); |
1753 | out_free_cmd: | 1758 | out_free_cmd: |
1754 | kfree(its->cmd_base); | 1759 | free_pages((unsigned long)its->cmd_base, get_order(ITS_CMD_QUEUE_SZ)); |
1755 | out_free_its: | 1760 | out_free_its: |
1756 | kfree(its); | 1761 | kfree(its); |
1757 | out_unmap: | 1762 | out_unmap: |
diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c index c01c09e9916d..11d12bccc4e7 100644 --- a/drivers/irqchip/irq-mips-gic.c +++ b/drivers/irqchip/irq-mips-gic.c | |||
@@ -968,6 +968,34 @@ static struct irq_domain_ops gic_ipi_domain_ops = { | |||
968 | .match = gic_ipi_domain_match, | 968 | .match = gic_ipi_domain_match, |
969 | }; | 969 | }; |
970 | 970 | ||
971 | static void __init gic_map_single_int(struct device_node *node, | ||
972 | unsigned int irq) | ||
973 | { | ||
974 | unsigned int linux_irq; | ||
975 | struct irq_fwspec local_int_fwspec = { | ||
976 | .fwnode = &node->fwnode, | ||
977 | .param_count = 3, | ||
978 | .param = { | ||
979 | [0] = GIC_LOCAL, | ||
980 | [1] = irq, | ||
981 | [2] = IRQ_TYPE_NONE, | ||
982 | }, | ||
983 | }; | ||
984 | |||
985 | if (!gic_local_irq_is_routable(irq)) | ||
986 | return; | ||
987 | |||
988 | linux_irq = irq_create_fwspec_mapping(&local_int_fwspec); | ||
989 | WARN_ON(!linux_irq); | ||
990 | } | ||
991 | |||
992 | static void __init gic_map_interrupts(struct device_node *node) | ||
993 | { | ||
994 | gic_map_single_int(node, GIC_LOCAL_INT_TIMER); | ||
995 | gic_map_single_int(node, GIC_LOCAL_INT_PERFCTR); | ||
996 | gic_map_single_int(node, GIC_LOCAL_INT_FDC); | ||
997 | } | ||
998 | |||
971 | static void __init __gic_init(unsigned long gic_base_addr, | 999 | static void __init __gic_init(unsigned long gic_base_addr, |
972 | unsigned long gic_addrspace_size, | 1000 | unsigned long gic_addrspace_size, |
973 | unsigned int cpu_vec, unsigned int irqbase, | 1001 | unsigned int cpu_vec, unsigned int irqbase, |
@@ -1067,6 +1095,7 @@ static void __init __gic_init(unsigned long gic_base_addr, | |||
1067 | } | 1095 | } |
1068 | 1096 | ||
1069 | gic_basic_init(); | 1097 | gic_basic_init(); |
1098 | gic_map_interrupts(node); | ||
1070 | } | 1099 | } |
1071 | 1100 | ||
1072 | void __init gic_init(unsigned long gic_base_addr, | 1101 | void __init gic_init(unsigned long gic_base_addr, |
diff --git a/drivers/irqchip/qcom-irq-combiner.c b/drivers/irqchip/qcom-irq-combiner.c new file mode 100644 index 000000000000..226558698344 --- /dev/null +++ b/drivers/irqchip/qcom-irq-combiner.c | |||
@@ -0,0 +1,296 @@ | |||
1 | /* Copyright (c) 2015-2016, The Linux Foundation. All rights reserved. | ||
2 | * | ||
3 | * This program is free software; you can redistribute it and/or modify | ||
4 | * it under the terms of the GNU General Public License version 2 and | ||
5 | * only version 2 as published by the Free Software Foundation. | ||
6 | * | ||
7 | * This program is distributed in the hope that it will be useful, | ||
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
10 | * GNU General Public License for more details. | ||
11 | */ | ||
12 | |||
13 | /* | ||
14 | * Driver for interrupt combiners in the Top-level Control and Status | ||
15 | * Registers (TCSR) hardware block in Qualcomm Technologies chips. | ||
16 | * An interrupt combiner in this block combines a set of interrupts by | ||
17 | * OR'ing the individual interrupt signals into a summary interrupt | ||
18 | * signal routed to a parent interrupt controller, and provides read- | ||
19 | * only, 32-bit registers to query the status of individual interrupts. | ||
20 | * The status bit for IRQ n is bit (n % 32) within register (n / 32) | ||
21 | * of the given combiner. Thus, each combiner can be described as a set | ||
22 | * of register offsets and the number of IRQs managed. | ||
23 | */ | ||
24 | |||
25 | #define pr_fmt(fmt) "QCOM80B1:" fmt | ||
26 | |||
27 | #include <linux/acpi.h> | ||
28 | #include <linux/irqchip/chained_irq.h> | ||
29 | #include <linux/irqdomain.h> | ||
30 | #include <linux/platform_device.h> | ||
31 | |||
32 | #define REG_SIZE 32 | ||
33 | |||
34 | struct combiner_reg { | ||
35 | void __iomem *addr; | ||
36 | unsigned long enabled; | ||
37 | }; | ||
38 | |||
39 | struct combiner { | ||
40 | struct irq_domain *domain; | ||
41 | int parent_irq; | ||
42 | u32 nirqs; | ||
43 | u32 nregs; | ||
44 | struct combiner_reg regs[0]; | ||
45 | }; | ||
46 | |||
47 | static inline int irq_nr(u32 reg, u32 bit) | ||
48 | { | ||
49 | return reg * REG_SIZE + bit; | ||
50 | } | ||
51 | |||
52 | /* | ||
53 | * Handler for the cascaded IRQ. | ||
54 | */ | ||
55 | static void combiner_handle_irq(struct irq_desc *desc) | ||
56 | { | ||
57 | struct combiner *combiner = irq_desc_get_handler_data(desc); | ||
58 | struct irq_chip *chip = irq_desc_get_chip(desc); | ||
59 | u32 reg; | ||
60 | |||
61 | chained_irq_enter(chip, desc); | ||
62 | |||
63 | for (reg = 0; reg < combiner->nregs; reg++) { | ||
64 | int virq; | ||
65 | int hwirq; | ||
66 | u32 bit; | ||
67 | u32 status; | ||
68 | |||
69 | bit = readl_relaxed(combiner->regs[reg].addr); | ||
70 | status = bit & combiner->regs[reg].enabled; | ||
71 | if (!status) | ||
72 | pr_warn_ratelimited("Unexpected IRQ on CPU%d: (%08x %08lx %p)\n", | ||
73 | smp_processor_id(), bit, | ||
74 | combiner->regs[reg].enabled, | ||
75 | combiner->regs[reg].addr); | ||
76 | |||
77 | while (status) { | ||
78 | bit = __ffs(status); | ||
79 | status &= ~(1 << bit); | ||
80 | hwirq = irq_nr(reg, bit); | ||
81 | virq = irq_find_mapping(combiner->domain, hwirq); | ||
82 | if (virq > 0) | ||
83 | generic_handle_irq(virq); | ||
84 | |||
85 | } | ||
86 | } | ||
87 | |||
88 | chained_irq_exit(chip, desc); | ||
89 | } | ||
90 | |||
91 | static void combiner_irq_chip_mask_irq(struct irq_data *data) | ||
92 | { | ||
93 | struct combiner *combiner = irq_data_get_irq_chip_data(data); | ||
94 | struct combiner_reg *reg = combiner->regs + data->hwirq / REG_SIZE; | ||
95 | |||
96 | clear_bit(data->hwirq % REG_SIZE, ®->enabled); | ||
97 | } | ||
98 | |||
99 | static void combiner_irq_chip_unmask_irq(struct irq_data *data) | ||
100 | { | ||
101 | struct combiner *combiner = irq_data_get_irq_chip_data(data); | ||
102 | struct combiner_reg *reg = combiner->regs + data->hwirq / REG_SIZE; | ||
103 | |||
104 | set_bit(data->hwirq % REG_SIZE, ®->enabled); | ||
105 | } | ||
106 | |||
107 | static struct irq_chip irq_chip = { | ||
108 | .irq_mask = combiner_irq_chip_mask_irq, | ||
109 | .irq_unmask = combiner_irq_chip_unmask_irq, | ||
110 | .name = "qcom-irq-combiner" | ||
111 | }; | ||
112 | |||
113 | static int combiner_irq_map(struct irq_domain *domain, unsigned int irq, | ||
114 | irq_hw_number_t hwirq) | ||
115 | { | ||
116 | irq_set_chip_and_handler(irq, &irq_chip, handle_level_irq); | ||
117 | irq_set_chip_data(irq, domain->host_data); | ||
118 | irq_set_noprobe(irq); | ||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | static void combiner_irq_unmap(struct irq_domain *domain, unsigned int irq) | ||
123 | { | ||
124 | irq_domain_reset_irq_data(irq_get_irq_data(irq)); | ||
125 | } | ||
126 | |||
127 | static int combiner_irq_translate(struct irq_domain *d, struct irq_fwspec *fws, | ||
128 | unsigned long *hwirq, unsigned int *type) | ||
129 | { | ||
130 | struct combiner *combiner = d->host_data; | ||
131 | |||
132 | if (is_acpi_node(fws->fwnode)) { | ||
133 | if (WARN_ON((fws->param_count != 2) || | ||
134 | (fws->param[0] >= combiner->nirqs) || | ||
135 | (fws->param[1] & IORESOURCE_IRQ_LOWEDGE) || | ||
136 | (fws->param[1] & IORESOURCE_IRQ_HIGHEDGE))) | ||
137 | return -EINVAL; | ||
138 | |||
139 | *hwirq = fws->param[0]; | ||
140 | *type = fws->param[1]; | ||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | return -EINVAL; | ||
145 | } | ||
146 | |||
147 | static const struct irq_domain_ops domain_ops = { | ||
148 | .map = combiner_irq_map, | ||
149 | .unmap = combiner_irq_unmap, | ||
150 | .translate = combiner_irq_translate | ||
151 | }; | ||
152 | |||
153 | static acpi_status count_registers_cb(struct acpi_resource *ares, void *context) | ||
154 | { | ||
155 | int *count = context; | ||
156 | |||
157 | if (ares->type == ACPI_RESOURCE_TYPE_GENERIC_REGISTER) | ||
158 | ++(*count); | ||
159 | return AE_OK; | ||
160 | } | ||
161 | |||
162 | static int count_registers(struct platform_device *pdev) | ||
163 | { | ||
164 | acpi_handle ahandle = ACPI_HANDLE(&pdev->dev); | ||
165 | acpi_status status; | ||
166 | int count = 0; | ||
167 | |||
168 | if (!acpi_has_method(ahandle, METHOD_NAME__CRS)) | ||
169 | return -EINVAL; | ||
170 | |||
171 | status = acpi_walk_resources(ahandle, METHOD_NAME__CRS, | ||
172 | count_registers_cb, &count); | ||
173 | if (ACPI_FAILURE(status)) | ||
174 | return -EINVAL; | ||
175 | return count; | ||
176 | } | ||
177 | |||
178 | struct get_registers_context { | ||
179 | struct device *dev; | ||
180 | struct combiner *combiner; | ||
181 | int err; | ||
182 | }; | ||
183 | |||
184 | static acpi_status get_registers_cb(struct acpi_resource *ares, void *context) | ||
185 | { | ||
186 | struct get_registers_context *ctx = context; | ||
187 | struct acpi_resource_generic_register *reg; | ||
188 | phys_addr_t paddr; | ||
189 | void __iomem *vaddr; | ||
190 | |||
191 | if (ares->type != ACPI_RESOURCE_TYPE_GENERIC_REGISTER) | ||
192 | return AE_OK; | ||
193 | |||
194 | reg = &ares->data.generic_reg; | ||
195 | paddr = reg->address; | ||
196 | if ((reg->space_id != ACPI_SPACE_MEM) || | ||
197 | (reg->bit_offset != 0) || | ||
198 | (reg->bit_width > REG_SIZE)) { | ||
199 | dev_err(ctx->dev, "Bad register resource @%pa\n", &paddr); | ||
200 | ctx->err = -EINVAL; | ||
201 | return AE_ERROR; | ||
202 | } | ||
203 | |||
204 | vaddr = devm_ioremap(ctx->dev, reg->address, REG_SIZE); | ||
205 | if (!vaddr) { | ||
206 | dev_err(ctx->dev, "Can't map register @%pa\n", &paddr); | ||
207 | ctx->err = -ENOMEM; | ||
208 | return AE_ERROR; | ||
209 | } | ||
210 | |||
211 | ctx->combiner->regs[ctx->combiner->nregs].addr = vaddr; | ||
212 | ctx->combiner->nirqs += reg->bit_width; | ||
213 | ctx->combiner->nregs++; | ||
214 | return AE_OK; | ||
215 | } | ||
216 | |||
217 | static int get_registers(struct platform_device *pdev, struct combiner *comb) | ||
218 | { | ||
219 | acpi_handle ahandle = ACPI_HANDLE(&pdev->dev); | ||
220 | acpi_status status; | ||
221 | struct get_registers_context ctx; | ||
222 | |||
223 | if (!acpi_has_method(ahandle, METHOD_NAME__CRS)) | ||
224 | return -EINVAL; | ||
225 | |||
226 | ctx.dev = &pdev->dev; | ||
227 | ctx.combiner = comb; | ||
228 | ctx.err = 0; | ||
229 | |||
230 | status = acpi_walk_resources(ahandle, METHOD_NAME__CRS, | ||
231 | get_registers_cb, &ctx); | ||
232 | if (ACPI_FAILURE(status)) | ||
233 | return ctx.err; | ||
234 | return 0; | ||
235 | } | ||
236 | |||
237 | static int __init combiner_probe(struct platform_device *pdev) | ||
238 | { | ||
239 | struct combiner *combiner; | ||
240 | size_t alloc_sz; | ||
241 | u32 nregs; | ||
242 | int err; | ||
243 | |||
244 | nregs = count_registers(pdev); | ||
245 | if (nregs <= 0) { | ||
246 | dev_err(&pdev->dev, "Error reading register resources\n"); | ||
247 | return -EINVAL; | ||
248 | } | ||
249 | |||
250 | alloc_sz = sizeof(*combiner) + sizeof(struct combiner_reg) * nregs; | ||
251 | combiner = devm_kzalloc(&pdev->dev, alloc_sz, GFP_KERNEL); | ||
252 | if (!combiner) | ||
253 | return -ENOMEM; | ||
254 | |||
255 | err = get_registers(pdev, combiner); | ||
256 | if (err < 0) | ||
257 | return err; | ||
258 | |||
259 | combiner->parent_irq = platform_get_irq(pdev, 0); | ||
260 | if (combiner->parent_irq <= 0) { | ||
261 | dev_err(&pdev->dev, "Error getting IRQ resource\n"); | ||
262 | return -EPROBE_DEFER; | ||
263 | } | ||
264 | |||
265 | combiner->domain = irq_domain_create_linear(pdev->dev.fwnode, combiner->nirqs, | ||
266 | &domain_ops, combiner); | ||
267 | if (!combiner->domain) | ||
268 | /* Errors printed by irq_domain_create_linear */ | ||
269 | return -ENODEV; | ||
270 | |||
271 | irq_set_chained_handler_and_data(combiner->parent_irq, | ||
272 | combiner_handle_irq, combiner); | ||
273 | |||
274 | dev_info(&pdev->dev, "Initialized with [p=%d,n=%d,r=%p]\n", | ||
275 | combiner->parent_irq, combiner->nirqs, combiner->regs[0].addr); | ||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | static const struct acpi_device_id qcom_irq_combiner_ids[] = { | ||
280 | { "QCOM80B1", }, | ||
281 | { } | ||
282 | }; | ||
283 | |||
284 | static struct platform_driver qcom_irq_combiner_probe = { | ||
285 | .driver = { | ||
286 | .name = "qcom-irq-combiner", | ||
287 | .acpi_match_table = ACPI_PTR(qcom_irq_combiner_ids), | ||
288 | }, | ||
289 | .probe = combiner_probe, | ||
290 | }; | ||
291 | |||
292 | static int __init register_qcom_irq_combiner(void) | ||
293 | { | ||
294 | return platform_driver_register(&qcom_irq_combiner_probe); | ||
295 | } | ||
296 | device_initcall(register_qcom_irq_combiner); | ||