Blame view
drivers/irqchip/exynos-combiner.c
5.74 KB
a900e5d99 ARM: exynos: move... |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
/* * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd. * http://www.samsung.com * * Combiner irqchip for EXYNOS * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include <linux/err.h> #include <linux/export.h> #include <linux/init.h> #include <linux/io.h> |
d34f03d4a irqchip: exynos: ... |
15 |
#include <linux/slab.h> |
a900e5d99 ARM: exynos: move... |
16 |
#include <linux/irqdomain.h> |
de88cbb7b arm: Move chained... |
17 |
#include <linux/irqchip/chained_irq.h> |
bc64690e3 irqchip: exynos-c... |
18 |
#include <linux/interrupt.h> |
a900e5d99 ARM: exynos: move... |
19 20 |
#include <linux/of_address.h> #include <linux/of_irq.h> |
a900e5d99 ARM: exynos: move... |
21 |
|
a900e5d99 ARM: exynos: move... |
22 23 24 25 26 |
#include "irqchip.h" #define COMBINER_ENABLE_SET 0x0 #define COMBINER_ENABLE_CLEAR 0x4 #define COMBINER_INT_STATUS 0xC |
6761dcfe8 irqchip: exynos: ... |
27 |
#define IRQ_IN_COMBINER 8 |
a900e5d99 ARM: exynos: move... |
28 29 30 |
static DEFINE_SPINLOCK(irq_controller_lock); struct combiner_chip_data { |
20adee8fa irqchip: exynos: ... |
31 |
unsigned int hwirq_offset; |
a900e5d99 ARM: exynos: move... |
32 33 |
unsigned int irq_mask; void __iomem *base; |
df7ef462a irqchip: exynos-c... |
34 |
unsigned int parent_irq; |
a900e5d99 ARM: exynos: move... |
35 36 37 |
}; static struct irq_domain *combiner_irq_domain; |
a900e5d99 ARM: exynos: move... |
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
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); __raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_CLEAR); } static void combiner_unmask_irq(struct irq_data *data) { u32 mask = 1 << (data->hwirq % 32); __raw_writel(mask, combiner_base(data) + COMBINER_ENABLE_SET); } static void combiner_handle_cascade_irq(unsigned int irq, struct irq_desc *desc) { struct combiner_chip_data *chip_data = irq_get_handler_data(irq); struct irq_chip *chip = irq_get_chip(irq); unsigned int cascade_irq, combiner_irq; unsigned long status; chained_irq_enter(chip, desc); spin_lock(&irq_controller_lock); status = __raw_readl(chip_data->base + COMBINER_INT_STATUS); spin_unlock(&irq_controller_lock); status &= chip_data->irq_mask; if (status == 0) goto out; |
20adee8fa irqchip: exynos: ... |
77 78 |
combiner_irq = chip_data->hwirq_offset + __ffs(status); cascade_irq = irq_find_mapping(combiner_irq_domain, combiner_irq); |
a900e5d99 ARM: exynos: move... |
79 |
|
20adee8fa irqchip: exynos: ... |
80 |
if (unlikely(!cascade_irq)) |
a83784859 irqchip: exynos-c... |
81 |
handle_bad_irq(irq, desc); |
a900e5d99 ARM: exynos: move... |
82 83 84 85 86 87 |
else generic_handle_irq(cascade_irq); out: chained_irq_exit(chip, desc); } |
df7ef462a irqchip: exynos-c... |
88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
#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... |
102 |
static struct irq_chip combiner_chip = { |
df7ef462a irqchip: exynos-c... |
103 104 105 106 107 108 |
.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... |
109 |
}; |
d34f03d4a irqchip: exynos: ... |
110 |
static void __init combiner_cascade_irq(struct combiner_chip_data *combiner_data, |
4e164dc5f irqchip: exynos-c... |
111 112 |
unsigned int irq) { |
d34f03d4a irqchip: exynos: ... |
113 |
if (irq_set_handler_data(irq, combiner_data) != 0) |
a900e5d99 ARM: exynos: move... |
114 115 116 |
BUG(); irq_set_chained_handler(irq, combiner_handle_cascade_irq); } |
d34f03d4a irqchip: exynos: ... |
117 118 |
static void __init combiner_init_one(struct combiner_chip_data *combiner_data, unsigned int combiner_nr, |
df7ef462a irqchip: exynos-c... |
119 |
void __iomem *base, unsigned int irq) |
a900e5d99 ARM: exynos: move... |
120 |
{ |
d34f03d4a irqchip: exynos: ... |
121 |
combiner_data->base = base; |
20adee8fa irqchip: exynos: ... |
122 |
combiner_data->hwirq_offset = (combiner_nr & ~3) * IRQ_IN_COMBINER; |
d34f03d4a irqchip: exynos: ... |
123 124 |
combiner_data->irq_mask = 0xff << ((combiner_nr % 4) << 3); combiner_data->parent_irq = irq; |
a900e5d99 ARM: exynos: move... |
125 126 |
/* Disable all interrupts */ |
d34f03d4a irqchip: exynos: ... |
127 |
__raw_writel(combiner_data->irq_mask, base + COMBINER_ENABLE_CLEAR); |
a900e5d99 ARM: exynos: move... |
128 |
} |
a900e5d99 ARM: exynos: move... |
129 130 131 132 133 134 135 136 137 138 139 |
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) { if (d->of_node != controller) return -EINVAL; if (intsize < 2) return -EINVAL; |
6761dcfe8 irqchip: exynos: ... |
140 |
*out_hwirq = intspec[0] * IRQ_IN_COMBINER + intspec[1]; |
a900e5d99 ARM: exynos: move... |
141 142 143 144 |
*out_type = 0; return 0; } |
a900e5d99 ARM: exynos: move... |
145 146 147 148 |
static int combiner_irq_domain_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) { |
d34f03d4a irqchip: exynos: ... |
149 |
struct combiner_chip_data *combiner_data = d->host_data; |
a900e5d99 ARM: exynos: move... |
150 151 152 153 154 155 156 157 158 159 160 |
irq_set_chip_and_handler(irq, &combiner_chip, handle_level_irq); irq_set_chip_data(irq, &combiner_data[hw >> 3]); set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); return 0; } static struct irq_domain_ops combiner_irq_domain_ops = { .xlate = combiner_irq_domain_xlate, .map = combiner_irq_domain_map, }; |
b8394dee7 irqchip: exynos-c... |
161 162 |
static void __init combiner_init(void __iomem *combiner_base, struct device_node *np, |
9403ac882 irqchip: exynos-c... |
163 |
unsigned int max_nr) |
a900e5d99 ARM: exynos: move... |
164 |
{ |
863a08dc8 irqchip: exynos: ... |
165 |
int i, irq; |
6761dcfe8 irqchip: exynos: ... |
166 |
unsigned int nr_irq; |
d34f03d4a irqchip: exynos: ... |
167 |
struct combiner_chip_data *combiner_data; |
a900e5d99 ARM: exynos: move... |
168 |
|
6761dcfe8 irqchip: exynos: ... |
169 |
nr_irq = max_nr * IRQ_IN_COMBINER; |
4e164dc5f irqchip: exynos-c... |
170 |
|
d34f03d4a irqchip: exynos: ... |
171 172 173 174 175 |
combiner_data = kcalloc(max_nr, sizeof (*combiner_data), GFP_KERNEL); if (!combiner_data) { pr_warning("%s: could not allocate combiner data ", __func__); return; |
a900e5d99 ARM: exynos: move... |
176 |
} |
9403ac882 irqchip: exynos-c... |
177 |
combiner_irq_domain = irq_domain_add_linear(np, nr_irq, |
d34f03d4a irqchip: exynos: ... |
178 |
&combiner_irq_domain_ops, combiner_data); |
a900e5d99 ARM: exynos: move... |
179 180 181 182 183 184 185 |
if (WARN_ON(!combiner_irq_domain)) { pr_warning("%s: irq domain init failed ", __func__); return; } for (i = 0; i < max_nr; i++) { |
0f5615117 irqchip: exynos: ... |
186 |
irq = irq_of_parse_and_map(np, i); |
92c8e4962 irqchip: exynos: ... |
187 |
|
d34f03d4a irqchip: exynos: ... |
188 189 190 |
combiner_init_one(&combiner_data[i], i, combiner_base + (i >> 2) * 0x10, irq); combiner_cascade_irq(&combiner_data[i], irq); |
a900e5d99 ARM: exynos: move... |
191 192 |
} } |
a900e5d99 ARM: exynos: move... |
193 194 195 196 |
static int __init combiner_of_init(struct device_node *np, struct device_node *parent) { void __iomem *combiner_base; |
6761dcfe8 irqchip: exynos: ... |
197 |
unsigned int max_nr = 20; |
a900e5d99 ARM: exynos: move... |
198 199 200 201 202 203 204 |
combiner_base = of_iomap(np, 0); if (!combiner_base) { pr_err("%s: failed to map combiner registers ", __func__); return -ENXIO; } |
6761dcfe8 irqchip: exynos: ... |
205 206 207 208 209 210 |
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); } |
9403ac882 irqchip: exynos-c... |
211 |
combiner_init(combiner_base, np, max_nr); |
a900e5d99 ARM: exynos: move... |
212 213 214 215 216 |
return 0; } IRQCHIP_DECLARE(exynos4210_combiner, "samsung,exynos4210-combiner", combiner_of_init); |