Blame view
drivers/irqchip/exynos-combiner.c
6.74 KB
d2912cb15 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
a900e5d99 ARM: exynos: move... |
2 3 4 5 6 |
/* * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. * http://www.samsung.com * * Combiner irqchip for EXYNOS |
a900e5d99 ARM: exynos: move... |
7 8 9 10 11 |
*/ #include <linux/err.h> #include <linux/export.h> #include <linux/init.h> #include <linux/io.h> |
d34f03d4a irqchip: exynos: ... |
12 |
#include <linux/slab.h> |
6fd4899a5 irqchip: exynos-c... |
13 |
#include <linux/syscore_ops.h> |
a900e5d99 ARM: exynos: move... |
14 |
#include <linux/irqdomain.h> |
41a83e06e irqchip: Prepare ... |
15 |
#include <linux/irqchip.h> |
de88cbb7b arm: Move chained... |
16 |
#include <linux/irqchip/chained_irq.h> |
bc64690e3 irqchip: exynos-c... |
17 |
#include <linux/interrupt.h> |
a900e5d99 ARM: exynos: move... |
18 19 |
#include <linux/of_address.h> #include <linux/of_irq.h> |
a900e5d99 ARM: exynos: move... |
20 |
|
a900e5d99 ARM: exynos: move... |
21 22 23 |
#define COMBINER_ENABLE_SET 0x0 #define COMBINER_ENABLE_CLEAR 0x4 #define COMBINER_INT_STATUS 0xC |
6761dcfe8 irqchip: exynos: ... |
24 |
#define IRQ_IN_COMBINER 8 |
a900e5d99 ARM: exynos: move... |
25 26 27 |
static DEFINE_SPINLOCK(irq_controller_lock); struct combiner_chip_data { |
20adee8fa irqchip: exynos: ... |
28 |
unsigned int hwirq_offset; |
a900e5d99 ARM: exynos: move... |
29 30 |
unsigned int irq_mask; void __iomem *base; |
df7ef462a irqchip: exynos-c... |
31 |
unsigned int parent_irq; |
6fd4899a5 irqchip: exynos-c... |
32 33 34 |
#ifdef CONFIG_PM u32 pm_save; #endif |
a900e5d99 ARM: exynos: move... |
35 |
}; |
6fd4899a5 irqchip: exynos-c... |
36 |
static struct combiner_chip_data *combiner_data; |
a900e5d99 ARM: exynos: move... |
37 |
static struct irq_domain *combiner_irq_domain; |
6fd4899a5 irqchip: exynos-c... |
38 |
static unsigned int max_nr = 20; |
a900e5d99 ARM: exynos: move... |
39 40 41 42 43 44 45 46 47 48 49 50 |
static inline void __iomem *combiner_base(struct irq_data *data) { struct combiner_chip_data *combiner_data = irq_data_get_irq_chip_data(data); return combiner_data->base; } static void combiner_mask_irq(struct irq_data *data) { u32 mask = 1 << (data->hwirq % 32); |
2a4fe14bc irqchip/exynos-co... |
51 |
writel_relaxed(mask, combiner_base(data) + COMBINER_ENABLE_CLEAR); |
a900e5d99 ARM: exynos: move... |
52 53 54 55 56 |
} static void combiner_unmask_irq(struct irq_data *data) { u32 mask = 1 << (data->hwirq % 32); |
2a4fe14bc irqchip/exynos-co... |
57 |
writel_relaxed(mask, combiner_base(data) + COMBINER_ENABLE_SET); |
a900e5d99 ARM: exynos: move... |
58 |
} |
bd0b9ac40 genirq: Remove ir... |
59 |
static void combiner_handle_cascade_irq(struct irq_desc *desc) |
a900e5d99 ARM: exynos: move... |
60 |
{ |
5b29264c6 irqchip: Use irq_... |
61 62 |
struct combiner_chip_data *chip_data = irq_desc_get_handler_data(desc); struct irq_chip *chip = irq_desc_get_chip(desc); |
a900e5d99 ARM: exynos: move... |
63 64 65 66 67 68 |
unsigned int cascade_irq, combiner_irq; unsigned long status; chained_irq_enter(chip, desc); spin_lock(&irq_controller_lock); |
2a4fe14bc irqchip/exynos-co... |
69 |
status = readl_relaxed(chip_data->base + COMBINER_INT_STATUS); |
a900e5d99 ARM: exynos: move... |
70 71 72 73 74 |
spin_unlock(&irq_controller_lock); status &= chip_data->irq_mask; if (status == 0) goto out; |
20adee8fa irqchip: exynos: ... |
75 76 |
combiner_irq = chip_data->hwirq_offset + __ffs(status); cascade_irq = irq_find_mapping(combiner_irq_domain, combiner_irq); |
a900e5d99 ARM: exynos: move... |
77 |
|
20adee8fa irqchip: exynos: ... |
78 |
if (unlikely(!cascade_irq)) |
bd0b9ac40 genirq: Remove ir... |
79 |
handle_bad_irq(desc); |
a900e5d99 ARM: exynos: move... |
80 81 82 83 84 85 |
else generic_handle_irq(cascade_irq); out: chained_irq_exit(chip, desc); } |
df7ef462a irqchip: exynos-c... |
86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
#ifdef CONFIG_SMP static int combiner_set_affinity(struct irq_data *d, const struct cpumask *mask_val, bool force) { struct combiner_chip_data *chip_data = irq_data_get_irq_chip_data(d); struct irq_chip *chip = irq_get_chip(chip_data->parent_irq); struct irq_data *data = irq_get_irq_data(chip_data->parent_irq); if (chip && chip->irq_set_affinity) return chip->irq_set_affinity(data, mask_val, force); else return -EINVAL; } #endif |
a900e5d99 ARM: exynos: move... |
100 |
static struct irq_chip combiner_chip = { |
df7ef462a irqchip: exynos-c... |
101 102 103 104 105 106 |
.name = "COMBINER", .irq_mask = combiner_mask_irq, .irq_unmask = combiner_unmask_irq, #ifdef CONFIG_SMP .irq_set_affinity = combiner_set_affinity, #endif |
a900e5d99 ARM: exynos: move... |
107 |
}; |
d34f03d4a irqchip: exynos: ... |
108 |
static void __init combiner_cascade_irq(struct combiner_chip_data *combiner_data, |
4e164dc5f irqchip: exynos-c... |
109 110 |
unsigned int irq) { |
741ff9661 irqchip/exynos-co... |
111 112 |
irq_set_chained_handler_and_data(irq, combiner_handle_cascade_irq, combiner_data); |
a900e5d99 ARM: exynos: move... |
113 |
} |
d34f03d4a irqchip: exynos: ... |
114 115 |
static void __init combiner_init_one(struct combiner_chip_data *combiner_data, unsigned int combiner_nr, |
df7ef462a irqchip: exynos-c... |
116 |
void __iomem *base, unsigned int irq) |
a900e5d99 ARM: exynos: move... |
117 |
{ |
d34f03d4a irqchip: exynos: ... |
118 |
combiner_data->base = base; |
20adee8fa irqchip: exynos: ... |
119 |
combiner_data->hwirq_offset = (combiner_nr & ~3) * IRQ_IN_COMBINER; |
d34f03d4a irqchip: exynos: ... |
120 121 |
combiner_data->irq_mask = 0xff << ((combiner_nr % 4) << 3); combiner_data->parent_irq = irq; |
a900e5d99 ARM: exynos: move... |
122 123 |
/* Disable all interrupts */ |
2a4fe14bc irqchip/exynos-co... |
124 |
writel_relaxed(combiner_data->irq_mask, base + COMBINER_ENABLE_CLEAR); |
a900e5d99 ARM: exynos: move... |
125 |
} |
a900e5d99 ARM: exynos: move... |
126 127 128 129 130 131 |
static int combiner_irq_domain_xlate(struct irq_domain *d, struct device_node *controller, const u32 *intspec, unsigned int intsize, unsigned long *out_hwirq, unsigned int *out_type) { |
5d4c9bc77 irqdomain: Use ir... |
132 |
if (irq_domain_get_of_node(d) != controller) |
a900e5d99 ARM: exynos: move... |
133 134 135 136 |
return -EINVAL; if (intsize < 2) return -EINVAL; |
6761dcfe8 irqchip: exynos: ... |
137 |
*out_hwirq = intspec[0] * IRQ_IN_COMBINER + intspec[1]; |
a900e5d99 ARM: exynos: move... |
138 139 140 141 |
*out_type = 0; return 0; } |
a900e5d99 ARM: exynos: move... |
142 143 144 145 |
static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) { |
d34f03d4a irqchip: exynos: ... |
146 |
struct combiner_chip_data *combiner_data = d->host_data; |
a900e5d99 ARM: exynos: move... |
147 148 |
irq_set_chip_and_handler(irq, &combiner_chip, handle_level_irq); irq_set_chip_data(irq, &combiner_data[hw >> 3]); |
d17cab445 irqchip: Kill off... |
149 |
irq_set_probe(irq); |
a900e5d99 ARM: exynos: move... |
150 151 152 |
return 0; } |
960097365 irqchip: Constify... |
153 |
static const struct irq_domain_ops combiner_irq_domain_ops = { |
a900e5d99 ARM: exynos: move... |
154 155 156 |
.xlate = combiner_irq_domain_xlate, .map = combiner_irq_domain_map, }; |
b8394dee7 irqchip: exynos-c... |
157 |
static void __init combiner_init(void __iomem *combiner_base, |
6fd4899a5 irqchip: exynos-c... |
158 |
struct device_node *np) |
a900e5d99 ARM: exynos: move... |
159 |
{ |
863a08dc8 irqchip: exynos: ... |
160 |
int i, irq; |
6761dcfe8 irqchip: exynos: ... |
161 |
unsigned int nr_irq; |
a900e5d99 ARM: exynos: move... |
162 |
|
6761dcfe8 irqchip: exynos: ... |
163 |
nr_irq = max_nr * IRQ_IN_COMBINER; |
4e164dc5f irqchip: exynos-c... |
164 |
|
d34f03d4a irqchip: exynos: ... |
165 166 |
combiner_data = kcalloc(max_nr, sizeof (*combiner_data), GFP_KERNEL); if (!combiner_data) { |
faca10b9e drivers/irqchip: ... |
167 168 |
pr_warn("%s: could not allocate combiner data ", __func__); |
d34f03d4a irqchip: exynos: ... |
169 |
return; |
a900e5d99 ARM: exynos: move... |
170 |
} |
9403ac882 irqchip: exynos-c... |
171 |
combiner_irq_domain = irq_domain_add_linear(np, nr_irq, |
d34f03d4a irqchip: exynos: ... |
172 |
&combiner_irq_domain_ops, combiner_data); |
a900e5d99 ARM: exynos: move... |
173 |
if (WARN_ON(!combiner_irq_domain)) { |
faca10b9e drivers/irqchip: ... |
174 175 |
pr_warn("%s: irq domain init failed ", __func__); |
a900e5d99 ARM: exynos: move... |
176 177 178 179 |
return; } for (i = 0; i < max_nr; i++) { |
0f5615117 irqchip: exynos: ... |
180 |
irq = irq_of_parse_and_map(np, i); |
92c8e4962 irqchip: exynos: ... |
181 |
|
d34f03d4a irqchip: exynos: ... |
182 183 184 |
combiner_init_one(&combiner_data[i], i, combiner_base + (i >> 2) * 0x10, irq); combiner_cascade_irq(&combiner_data[i], irq); |
a900e5d99 ARM: exynos: move... |
185 186 |
} } |
6fd4899a5 irqchip: exynos-c... |
187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 |
#ifdef CONFIG_PM /** * combiner_suspend - save interrupt combiner state before suspend * * Save the interrupt enable set register for all combiner groups since * the state is lost when the system enters into a sleep state. * */ static int combiner_suspend(void) { int i; for (i = 0; i < max_nr; i++) combiner_data[i].pm_save = |
2a4fe14bc irqchip/exynos-co... |
202 |
readl_relaxed(combiner_data[i].base + COMBINER_ENABLE_SET); |
6fd4899a5 irqchip: exynos-c... |
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 |
return 0; } /** * combiner_resume - restore interrupt combiner state after resume * * Restore the interrupt enable set register for all combiner groups since * the state is lost when the system enters into a sleep state on suspend. * */ static void combiner_resume(void) { int i; for (i = 0; i < max_nr; i++) { |
2a4fe14bc irqchip/exynos-co... |
219 |
writel_relaxed(combiner_data[i].irq_mask, |
6fd4899a5 irqchip: exynos-c... |
220 |
combiner_data[i].base + COMBINER_ENABLE_CLEAR); |
2a4fe14bc irqchip/exynos-co... |
221 |
writel_relaxed(combiner_data[i].pm_save, |
6fd4899a5 irqchip: exynos-c... |
222 223 224 225 226 227 228 229 230 231 232 233 234 |
combiner_data[i].base + COMBINER_ENABLE_SET); } } #else #define combiner_suspend NULL #define combiner_resume NULL #endif static struct syscore_ops combiner_syscore_ops = { .suspend = combiner_suspend, .resume = combiner_resume, }; |
a900e5d99 ARM: exynos: move... |
235 236 237 238 239 240 241 242 243 244 245 |
static int __init combiner_of_init(struct device_node *np, struct device_node *parent) { void __iomem *combiner_base; combiner_base = of_iomap(np, 0); if (!combiner_base) { pr_err("%s: failed to map combiner registers ", __func__); return -ENXIO; } |
6761dcfe8 irqchip: exynos: ... |
246 247 248 249 250 251 |
if (of_property_read_u32(np, "samsung,combiner-nr", &max_nr)) { pr_info("%s: number of combiners not specified, " "setting default as %d. ", __func__, max_nr); } |
6fd4899a5 irqchip: exynos-c... |
252 253 254 |
combiner_init(combiner_base, np); register_syscore_ops(&combiner_syscore_ops); |
a900e5d99 ARM: exynos: move... |
255 256 257 258 259 |
return 0; } IRQCHIP_DECLARE(exynos4210_combiner, "samsung,exynos4210-combiner", combiner_of_init); |