diff options
author | Kevin Cernekee <cernekee@gmail.com> | 2014-12-25 12:49:04 -0500 |
---|---|---|
committer | Ralf Baechle <ralf@linux-mips.org> | 2015-04-01 11:21:37 -0400 |
commit | ca40f1b23df70c6f31b14a5743a6f3b60e862ce1 (patch) | |
tree | 46d27490a1391cd890a4600fd49cf7bb6ffeee4b /drivers/irqchip | |
parent | 5b5468cf1fe9d16e568b45685b31dd4c72588778 (diff) |
IRQCHIP: bcm7120-l2: Split STB-specific logic into its own function
The BCM7xxx instances of this block (listed in the register manual as
simply "IRQ0") all have the following items in common:
- brcm,int-map-mask: for routing different bits in the L2 to different
parent IRQs
- brcm,int-fwd-mask: for hardwiring certain IRQs to bypass the L2 and
use dedicated L1 lines
- one enable/status pair (32 bits only)
Much of the driver code can be shared with BCM3380-style controllers, but
in order to do this cleanly, let's split out the BCM7xxx-specific logic
first.
Signed-off-by: Kevin Cernekee <cernekee@gmail.com>
Cc: f.fainelli@gmail.com
Cc: jaedon.shin@gmail.com
Cc: abrestic@chromium.org
Cc: tglx@linutronix.de
Cc: jason@lakedaemon.net
Cc: jogo@openwrt.org
Cc: arnd@arndb.de
Cc: computersforpeace@gmail.com
Cc: linux-mips@linux-mips.org
Cc: devicetree@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Patchwork: https://patchwork.linux-mips.org/patch/8842/
Signed-off-by: Ralf Baechle <ralf@linux-mips.org>
Diffstat (limited to 'drivers/irqchip')
-rw-r--r-- | drivers/irqchip/irq-bcm7120-l2.c | 123 |
1 files changed, 68 insertions, 55 deletions
diff --git a/drivers/irqchip/irq-bcm7120-l2.c b/drivers/irqchip/irq-bcm7120-l2.c index e8441ee7454c..6a6285897df1 100644 --- a/drivers/irqchip/irq-bcm7120-l2.c +++ b/drivers/irqchip/irq-bcm7120-l2.c | |||
@@ -34,7 +34,7 @@ | |||
34 | #define IRQSTAT 0x04 | 34 | #define IRQSTAT 0x04 |
35 | 35 | ||
36 | #define MAX_WORDS 4 | 36 | #define MAX_WORDS 4 |
37 | #define MAX_MAPPINGS MAX_WORDS | 37 | #define MAX_MAPPINGS (MAX_WORDS * 2) |
38 | #define IRQS_PER_WORD 32 | 38 | #define IRQS_PER_WORD 32 |
39 | 39 | ||
40 | struct bcm7120_l2_intc_data { | 40 | struct bcm7120_l2_intc_data { |
@@ -47,6 +47,8 @@ struct bcm7120_l2_intc_data { | |||
47 | bool can_wake; | 47 | bool can_wake; |
48 | u32 irq_fwd_mask[MAX_WORDS]; | 48 | u32 irq_fwd_mask[MAX_WORDS]; |
49 | u32 irq_map_mask[MAX_WORDS]; | 49 | u32 irq_map_mask[MAX_WORDS]; |
50 | int num_parent_irqs; | ||
51 | const __be32 *map_mask_prop; | ||
50 | }; | 52 | }; |
51 | 53 | ||
52 | static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc) | 54 | static void bcm7120_l2_intc_irq_handle(unsigned int irq, struct irq_desc *desc) |
@@ -104,7 +106,7 @@ static void bcm7120_l2_intc_resume(struct irq_data *d) | |||
104 | 106 | ||
105 | static int bcm7120_l2_intc_init_one(struct device_node *dn, | 107 | static int bcm7120_l2_intc_init_one(struct device_node *dn, |
106 | struct bcm7120_l2_intc_data *data, | 108 | struct bcm7120_l2_intc_data *data, |
107 | int irq, const __be32 *map_mask) | 109 | int irq) |
108 | { | 110 | { |
109 | int parent_irq; | 111 | int parent_irq; |
110 | unsigned int idx; | 112 | unsigned int idx; |
@@ -120,7 +122,8 @@ static int bcm7120_l2_intc_init_one(struct device_node *dn, | |||
120 | */ | 122 | */ |
121 | for (idx = 0; idx < data->n_words; idx++) | 123 | for (idx = 0; idx < data->n_words; idx++) |
122 | data->irq_map_mask[idx] |= | 124 | data->irq_map_mask[idx] |= |
123 | be32_to_cpup(map_mask + irq * data->n_words + idx); | 125 | be32_to_cpup(data->map_mask_prop + |
126 | irq * data->n_words + idx); | ||
124 | 127 | ||
125 | irq_set_handler_data(parent_irq, data); | 128 | irq_set_handler_data(parent_irq, data); |
126 | irq_set_chained_handler(parent_irq, bcm7120_l2_intc_irq_handle); | 129 | irq_set_chained_handler(parent_irq, bcm7120_l2_intc_irq_handle); |
@@ -128,74 +131,76 @@ static int bcm7120_l2_intc_init_one(struct device_node *dn, | |||
128 | return 0; | 131 | return 0; |
129 | } | 132 | } |
130 | 133 | ||
131 | int __init bcm7120_l2_intc_of_init(struct device_node *dn, | 134 | static int __init bcm7120_l2_intc_iomap_7120(struct device_node *dn, |
132 | struct device_node *parent) | 135 | struct bcm7120_l2_intc_data *data) |
136 | { | ||
137 | int ret; | ||
138 | |||
139 | data->map_base[0] = of_iomap(dn, 0); | ||
140 | if (!data->map_base[0]) { | ||
141 | pr_err("unable to map registers\n"); | ||
142 | return -ENOMEM; | ||
143 | } | ||
144 | |||
145 | data->pair_base[0] = data->map_base[0]; | ||
146 | data->en_offset[0] = IRQEN; | ||
147 | data->stat_offset[0] = IRQSTAT; | ||
148 | data->n_words = 1; | ||
149 | |||
150 | ret = of_property_read_u32_array(dn, "brcm,int-fwd-mask", | ||
151 | data->irq_fwd_mask, data->n_words); | ||
152 | if (ret != 0 && ret != -EINVAL) { | ||
153 | /* property exists but has the wrong number of words */ | ||
154 | pr_err("invalid brcm,int-fwd-mask property\n"); | ||
155 | return -EINVAL; | ||
156 | } | ||
157 | |||
158 | data->map_mask_prop = of_get_property(dn, "brcm,int-map-mask", &ret); | ||
159 | if (!data->map_mask_prop || | ||
160 | (ret != (sizeof(__be32) * data->num_parent_irqs * data->n_words))) { | ||
161 | pr_err("invalid brcm,int-map-mask property\n"); | ||
162 | return -EINVAL; | ||
163 | } | ||
164 | |||
165 | return 0; | ||
166 | } | ||
167 | |||
168 | int __init bcm7120_l2_intc_probe(struct device_node *dn, | ||
169 | struct device_node *parent, | ||
170 | int (*iomap_regs_fn)(struct device_node *, | ||
171 | struct bcm7120_l2_intc_data *), | ||
172 | const char *intc_name) | ||
133 | { | 173 | { |
134 | unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; | 174 | unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; |
135 | struct bcm7120_l2_intc_data *data; | 175 | struct bcm7120_l2_intc_data *data; |
136 | struct irq_chip_generic *gc; | 176 | struct irq_chip_generic *gc; |
137 | struct irq_chip_type *ct; | 177 | struct irq_chip_type *ct; |
138 | const __be32 *map_mask; | 178 | int ret = 0; |
139 | int num_parent_irqs; | ||
140 | int ret = 0, len; | ||
141 | unsigned int idx, irq, flags; | 179 | unsigned int idx, irq, flags; |
142 | 180 | ||
143 | data = kzalloc(sizeof(*data), GFP_KERNEL); | 181 | data = kzalloc(sizeof(*data), GFP_KERNEL); |
144 | if (!data) | 182 | if (!data) |
145 | return -ENOMEM; | 183 | return -ENOMEM; |
146 | 184 | ||
147 | for (idx = 0; idx < MAX_WORDS; idx++) { | 185 | data->num_parent_irqs = of_irq_count(dn); |
148 | data->map_base[idx] = of_iomap(dn, idx); | 186 | if (data->num_parent_irqs <= 0) { |
149 | if (!data->map_base[idx]) | ||
150 | break; | ||
151 | |||
152 | data->pair_base[idx] = data->map_base[idx]; | ||
153 | data->en_offset[idx] = IRQEN; | ||
154 | data->stat_offset[idx] = IRQSTAT; | ||
155 | |||
156 | data->n_words = idx + 1; | ||
157 | } | ||
158 | if (!data->n_words) { | ||
159 | pr_err("failed to remap intc L2 registers\n"); | ||
160 | ret = -ENOMEM; | ||
161 | goto out_unmap; | ||
162 | } | ||
163 | |||
164 | /* Enable all interrupts specified in the interrupt forward mask; | ||
165 | * disable all others. If the property doesn't exist (-EINVAL), | ||
166 | * assume all zeroes. | ||
167 | */ | ||
168 | ret = of_property_read_u32_array(dn, "brcm,int-fwd-mask", | ||
169 | data->irq_fwd_mask, data->n_words); | ||
170 | if (ret == 0 || ret == -EINVAL) { | ||
171 | for (idx = 0; idx < data->n_words; idx++) | ||
172 | __raw_writel(data->irq_fwd_mask[idx], | ||
173 | data->pair_base[idx] + | ||
174 | data->en_offset[idx]); | ||
175 | } else { | ||
176 | /* property exists but has the wrong number of words */ | ||
177 | pr_err("invalid int-fwd-mask property\n"); | ||
178 | ret = -EINVAL; | ||
179 | goto out_unmap; | ||
180 | } | ||
181 | |||
182 | num_parent_irqs = of_irq_count(dn); | ||
183 | if (num_parent_irqs <= 0) { | ||
184 | pr_err("invalid number of parent interrupts\n"); | 187 | pr_err("invalid number of parent interrupts\n"); |
185 | ret = -ENOMEM; | 188 | ret = -ENOMEM; |
186 | goto out_unmap; | 189 | goto out_unmap; |
187 | } | 190 | } |
188 | 191 | ||
189 | map_mask = of_get_property(dn, "brcm,int-map-mask", &len); | 192 | ret = iomap_regs_fn(dn, data); |
190 | if (!map_mask || | 193 | if (ret < 0) |
191 | (len != (sizeof(*map_mask) * num_parent_irqs * data->n_words))) { | ||
192 | pr_err("invalid brcm,int-map-mask property\n"); | ||
193 | ret = -EINVAL; | ||
194 | goto out_unmap; | 194 | goto out_unmap; |
195 | |||
196 | for (idx = 0; idx < data->n_words; idx++) { | ||
197 | __raw_writel(data->irq_fwd_mask[idx], | ||
198 | data->pair_base[idx] + | ||
199 | data->en_offset[idx]); | ||
195 | } | 200 | } |
196 | 201 | ||
197 | for (irq = 0; irq < num_parent_irqs; irq++) { | 202 | for (irq = 0; irq < data->num_parent_irqs; irq++) { |
198 | ret = bcm7120_l2_intc_init_one(dn, data, irq, map_mask); | 203 | ret = bcm7120_l2_intc_init_one(dn, data, irq); |
199 | if (ret) | 204 | if (ret) |
200 | goto out_unmap; | 205 | goto out_unmap; |
201 | } | 206 | } |
@@ -251,8 +256,8 @@ int __init bcm7120_l2_intc_of_init(struct device_node *dn, | |||
251 | } | 256 | } |
252 | } | 257 | } |
253 | 258 | ||
254 | pr_info("registered BCM7120 L2 intc (mem: 0x%p, parent IRQ(s): %d)\n", | 259 | pr_info("registered %s intc (mem: 0x%p, parent IRQ(s): %d)\n", |
255 | data->map_base[0], num_parent_irqs); | 260 | intc_name, data->map_base[0], data->num_parent_irqs); |
256 | 261 | ||
257 | return 0; | 262 | return 0; |
258 | 263 | ||
@@ -266,5 +271,13 @@ out_unmap: | |||
266 | kfree(data); | 271 | kfree(data); |
267 | return ret; | 272 | return ret; |
268 | } | 273 | } |
274 | |||
275 | int __init bcm7120_l2_intc_probe_7120(struct device_node *dn, | ||
276 | struct device_node *parent) | ||
277 | { | ||
278 | return bcm7120_l2_intc_probe(dn, parent, bcm7120_l2_intc_iomap_7120, | ||
279 | "BCM7120 L2"); | ||
280 | } | ||
281 | |||
269 | IRQCHIP_DECLARE(bcm7120_l2_intc, "brcm,bcm7120-l2-intc", | 282 | IRQCHIP_DECLARE(bcm7120_l2_intc, "brcm,bcm7120-l2-intc", |
270 | bcm7120_l2_intc_of_init); | 283 | bcm7120_l2_intc_probe_7120); |