tangled
alpha
login
or
join now
tjh.dev
/
kernel
1
fork
atom
Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1
fork
atom
overview
issues
pulls
pipelines
Merge branch 'irqchip/tango' into irqchip/core
Jason Cooper
10 years ago
55e10798
e4e1c0ea
+287
4 changed files
expand all
collapse all
unified
split
Documentation
devicetree
bindings
interrupt-controller
sigma,smp8642-intc.txt
drivers
irqchip
Kconfig
Makefile
irq-tango.c
+49
Documentation/devicetree/bindings/interrupt-controller/sigma,smp8642-intc.txt
reviewed
···
1
1
+
Sigma Designs SMP86xx/SMP87xx secondary interrupt controller
2
2
+
3
3
+
Required properties:
4
4
+
- compatible: should be "sigma,smp8642-intc"
5
5
+
- reg: physical address of MMIO region
6
6
+
- ranges: address space mapping of child nodes
7
7
+
- interrupt-parent: phandle of parent interrupt controller
8
8
+
- interrupt-controller: boolean
9
9
+
- #address-cells: should be <1>
10
10
+
- #size-cells: should be <1>
11
11
+
12
12
+
One child node per control block with properties:
13
13
+
- reg: address of registers for this control block
14
14
+
- interrupt-controller: boolean
15
15
+
- #interrupt-cells: should be <2>, interrupt index and flags per interrupts.txt
16
16
+
- interrupts: interrupt spec of primary interrupt controller
17
17
+
18
18
+
Example:
19
19
+
20
20
+
interrupt-controller@6e000 {
21
21
+
compatible = "sigma,smp8642-intc";
22
22
+
reg = <0x6e000 0x400>;
23
23
+
ranges = <0x0 0x6e000 0x400>;
24
24
+
interrupt-parent = <&gic>;
25
25
+
interrupt-controller;
26
26
+
#address-cells = <1>;
27
27
+
#size-cells = <1>;
28
28
+
29
29
+
irq0: interrupt-controller@0 {
30
30
+
reg = <0x000 0x100>;
31
31
+
interrupt-controller;
32
32
+
#interrupt-cells = <2>;
33
33
+
interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
34
34
+
};
35
35
+
36
36
+
irq1: interrupt-controller@100 {
37
37
+
reg = <0x100 0x100>;
38
38
+
interrupt-controller;
39
39
+
#interrupt-cells = <2>;
40
40
+
interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
41
41
+
};
42
42
+
43
43
+
irq2: interrupt-controller@300 {
44
44
+
reg = <0x300 0x100>;
45
45
+
interrupt-controller;
46
46
+
#interrupt-cells = <2>;
47
47
+
interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
48
48
+
};
49
49
+
};
+5
drivers/irqchip/Kconfig
reviewed
···
156
156
help
157
157
Enables SysCfg Controlled IRQs on STi based platforms.
158
158
159
159
+
config TANGO_IRQ
160
160
+
bool
161
161
+
select IRQ_DOMAIN
162
162
+
select GENERIC_IRQ_CHIP
163
163
+
159
164
config TB10X_IRQC
160
165
bool
161
166
select IRQ_DOMAIN
+1
drivers/irqchip/Makefile
reviewed
···
40
40
obj-$(CONFIG_ARCH_NSPIRE) += irq-zevio.o
41
41
obj-$(CONFIG_ARCH_VT8500) += irq-vt8500.o
42
42
obj-$(CONFIG_ST_IRQCHIP) += irq-st.o
43
43
+
obj-$(CONFIG_TANGO_IRQ) += irq-tango.o
43
44
obj-$(CONFIG_TB10X_IRQC) += irq-tb10x.o
44
45
obj-$(CONFIG_TS4800_IRQ) += irq-ts4800.o
45
46
obj-$(CONFIG_XTENSA) += irq-xtensa-pic.o
+232
drivers/irqchip/irq-tango.c
reviewed
···
1
1
+
/*
2
2
+
* Copyright (C) 2014 Mans Rullgard <mans@mansr.com>
3
3
+
*
4
4
+
* This program is free software; you can redistribute it and/or modify it
5
5
+
* under the terms of the GNU General Public License as published by the
6
6
+
* Free Software Foundation; either version 2 of the License, or (at your
7
7
+
* option) any later version.
8
8
+
*/
9
9
+
10
10
+
#include <linux/init.h>
11
11
+
#include <linux/irq.h>
12
12
+
#include <linux/irqchip.h>
13
13
+
#include <linux/irqchip/chained_irq.h>
14
14
+
#include <linux/ioport.h>
15
15
+
#include <linux/io.h>
16
16
+
#include <linux/of_address.h>
17
17
+
#include <linux/of_irq.h>
18
18
+
#include <linux/slab.h>
19
19
+
20
20
+
#define IRQ0_CTL_BASE 0x0000
21
21
+
#define IRQ1_CTL_BASE 0x0100
22
22
+
#define EDGE_CTL_BASE 0x0200
23
23
+
#define IRQ2_CTL_BASE 0x0300
24
24
+
25
25
+
#define IRQ_CTL_HI 0x18
26
26
+
#define EDGE_CTL_HI 0x20
27
27
+
28
28
+
#define IRQ_STATUS 0x00
29
29
+
#define IRQ_RAWSTAT 0x04
30
30
+
#define IRQ_EN_SET 0x08
31
31
+
#define IRQ_EN_CLR 0x0c
32
32
+
#define IRQ_SOFT_SET 0x10
33
33
+
#define IRQ_SOFT_CLR 0x14
34
34
+
35
35
+
#define EDGE_STATUS 0x00
36
36
+
#define EDGE_RAWSTAT 0x04
37
37
+
#define EDGE_CFG_RISE 0x08
38
38
+
#define EDGE_CFG_FALL 0x0c
39
39
+
#define EDGE_CFG_RISE_SET 0x10
40
40
+
#define EDGE_CFG_RISE_CLR 0x14
41
41
+
#define EDGE_CFG_FALL_SET 0x18
42
42
+
#define EDGE_CFG_FALL_CLR 0x1c
43
43
+
44
44
+
struct tangox_irq_chip {
45
45
+
void __iomem *base;
46
46
+
unsigned long ctl;
47
47
+
};
48
48
+
49
49
+
static inline u32 intc_readl(struct tangox_irq_chip *chip, int reg)
50
50
+
{
51
51
+
return readl_relaxed(chip->base + reg);
52
52
+
}
53
53
+
54
54
+
static inline void intc_writel(struct tangox_irq_chip *chip, int reg, u32 val)
55
55
+
{
56
56
+
writel_relaxed(val, chip->base + reg);
57
57
+
}
58
58
+
59
59
+
static void tangox_dispatch_irqs(struct irq_domain *dom, unsigned int status,
60
60
+
int base)
61
61
+
{
62
62
+
unsigned int hwirq;
63
63
+
unsigned int virq;
64
64
+
65
65
+
while (status) {
66
66
+
hwirq = __ffs(status);
67
67
+
virq = irq_find_mapping(dom, base + hwirq);
68
68
+
if (virq)
69
69
+
generic_handle_irq(virq);
70
70
+
status &= ~BIT(hwirq);
71
71
+
}
72
72
+
}
73
73
+
74
74
+
static void tangox_irq_handler(struct irq_desc *desc)
75
75
+
{
76
76
+
struct irq_domain *dom = irq_desc_get_handler_data(desc);
77
77
+
struct irq_chip *host_chip = irq_desc_get_chip(desc);
78
78
+
struct tangox_irq_chip *chip = dom->host_data;
79
79
+
unsigned int status_lo, status_hi;
80
80
+
81
81
+
chained_irq_enter(host_chip, desc);
82
82
+
83
83
+
status_lo = intc_readl(chip, chip->ctl + IRQ_STATUS);
84
84
+
status_hi = intc_readl(chip, chip->ctl + IRQ_CTL_HI + IRQ_STATUS);
85
85
+
86
86
+
tangox_dispatch_irqs(dom, status_lo, 0);
87
87
+
tangox_dispatch_irqs(dom, status_hi, 32);
88
88
+
89
89
+
chained_irq_exit(host_chip, desc);
90
90
+
}
91
91
+
92
92
+
static int tangox_irq_set_type(struct irq_data *d, unsigned int flow_type)
93
93
+
{
94
94
+
struct irq_chip_generic *gc = irq_data_get_irq_chip_data(d);
95
95
+
struct tangox_irq_chip *chip = gc->domain->host_data;
96
96
+
struct irq_chip_regs *regs = &gc->chip_types[0].regs;
97
97
+
98
98
+
switch (flow_type & IRQ_TYPE_SENSE_MASK) {
99
99
+
case IRQ_TYPE_EDGE_RISING:
100
100
+
intc_writel(chip, regs->type + EDGE_CFG_RISE_SET, d->mask);
101
101
+
intc_writel(chip, regs->type + EDGE_CFG_FALL_CLR, d->mask);
102
102
+
break;
103
103
+
104
104
+
case IRQ_TYPE_EDGE_FALLING:
105
105
+
intc_writel(chip, regs->type + EDGE_CFG_RISE_CLR, d->mask);
106
106
+
intc_writel(chip, regs->type + EDGE_CFG_FALL_SET, d->mask);
107
107
+
break;
108
108
+
109
109
+
case IRQ_TYPE_LEVEL_HIGH:
110
110
+
intc_writel(chip, regs->type + EDGE_CFG_RISE_CLR, d->mask);
111
111
+
intc_writel(chip, regs->type + EDGE_CFG_FALL_CLR, d->mask);
112
112
+
break;
113
113
+
114
114
+
case IRQ_TYPE_LEVEL_LOW:
115
115
+
intc_writel(chip, regs->type + EDGE_CFG_RISE_SET, d->mask);
116
116
+
intc_writel(chip, regs->type + EDGE_CFG_FALL_SET, d->mask);
117
117
+
break;
118
118
+
119
119
+
default:
120
120
+
pr_err("Invalid trigger mode %x for IRQ %d\n",
121
121
+
flow_type, d->irq);
122
122
+
return -EINVAL;
123
123
+
}
124
124
+
125
125
+
return irq_setup_alt_chip(d, flow_type);
126
126
+
}
127
127
+
128
128
+
static void __init tangox_irq_init_chip(struct irq_chip_generic *gc,
129
129
+
unsigned long ctl_offs,
130
130
+
unsigned long edge_offs)
131
131
+
{
132
132
+
struct tangox_irq_chip *chip = gc->domain->host_data;
133
133
+
struct irq_chip_type *ct = gc->chip_types;
134
134
+
unsigned long ctl_base = chip->ctl + ctl_offs;
135
135
+
unsigned long edge_base = EDGE_CTL_BASE + edge_offs;
136
136
+
int i;
137
137
+
138
138
+
gc->reg_base = chip->base;
139
139
+
gc->unused = 0;
140
140
+
141
141
+
for (i = 0; i < 2; i++) {
142
142
+
ct[i].chip.irq_ack = irq_gc_ack_set_bit;
143
143
+
ct[i].chip.irq_mask = irq_gc_mask_disable_reg;
144
144
+
ct[i].chip.irq_mask_ack = irq_gc_mask_disable_reg_and_ack;
145
145
+
ct[i].chip.irq_unmask = irq_gc_unmask_enable_reg;
146
146
+
ct[i].chip.irq_set_type = tangox_irq_set_type;
147
147
+
ct[i].chip.name = gc->domain->name;
148
148
+
149
149
+
ct[i].regs.enable = ctl_base + IRQ_EN_SET;
150
150
+
ct[i].regs.disable = ctl_base + IRQ_EN_CLR;
151
151
+
ct[i].regs.ack = edge_base + EDGE_RAWSTAT;
152
152
+
ct[i].regs.type = edge_base;
153
153
+
}
154
154
+
155
155
+
ct[0].type = IRQ_TYPE_LEVEL_MASK;
156
156
+
ct[0].handler = handle_level_irq;
157
157
+
158
158
+
ct[1].type = IRQ_TYPE_EDGE_BOTH;
159
159
+
ct[1].handler = handle_edge_irq;
160
160
+
161
161
+
intc_writel(chip, ct->regs.disable, 0xffffffff);
162
162
+
intc_writel(chip, ct->regs.ack, 0xffffffff);
163
163
+
}
164
164
+
165
165
+
static void __init tangox_irq_domain_init(struct irq_domain *dom)
166
166
+
{
167
167
+
struct irq_chip_generic *gc;
168
168
+
int i;
169
169
+
170
170
+
for (i = 0; i < 2; i++) {
171
171
+
gc = irq_get_domain_generic_chip(dom, i * 32);
172
172
+
tangox_irq_init_chip(gc, i * IRQ_CTL_HI, i * EDGE_CTL_HI);
173
173
+
}
174
174
+
}
175
175
+
176
176
+
static int __init tangox_irq_init(void __iomem *base, struct resource *baseres,
177
177
+
struct device_node *node)
178
178
+
{
179
179
+
struct tangox_irq_chip *chip;
180
180
+
struct irq_domain *dom;
181
181
+
struct resource res;
182
182
+
int irq;
183
183
+
int err;
184
184
+
185
185
+
irq = irq_of_parse_and_map(node, 0);
186
186
+
if (!irq)
187
187
+
panic("%s: failed to get IRQ", node->name);
188
188
+
189
189
+
err = of_address_to_resource(node, 0, &res);
190
190
+
if (err)
191
191
+
panic("%s: failed to get address", node->name);
192
192
+
193
193
+
chip = kzalloc(sizeof(*chip), GFP_KERNEL);
194
194
+
chip->ctl = res.start - baseres->start;
195
195
+
chip->base = base;
196
196
+
197
197
+
dom = irq_domain_add_linear(node, 64, &irq_generic_chip_ops, chip);
198
198
+
if (!dom)
199
199
+
panic("%s: failed to create irqdomain", node->name);
200
200
+
201
201
+
err = irq_alloc_domain_generic_chips(dom, 32, 2, node->name,
202
202
+
handle_level_irq, 0, 0, 0);
203
203
+
if (err)
204
204
+
panic("%s: failed to allocate irqchip", node->name);
205
205
+
206
206
+
tangox_irq_domain_init(dom);
207
207
+
208
208
+
irq_set_chained_handler(irq, tangox_irq_handler);
209
209
+
irq_set_handler_data(irq, dom);
210
210
+
211
211
+
return 0;
212
212
+
}
213
213
+
214
214
+
static int __init tangox_of_irq_init(struct device_node *node,
215
215
+
struct device_node *parent)
216
216
+
{
217
217
+
struct device_node *c;
218
218
+
struct resource res;
219
219
+
void __iomem *base;
220
220
+
221
221
+
base = of_iomap(node, 0);
222
222
+
if (!base)
223
223
+
panic("%s: of_iomap failed", node->name);
224
224
+
225
225
+
of_address_to_resource(node, 0, &res);
226
226
+
227
227
+
for_each_child_of_node(node, c)
228
228
+
tangox_irq_init(base, &res, c);
229
229
+
230
230
+
return 0;
231
231
+
}
232
232
+
IRQCHIP_DECLARE(tangox_intc, "sigma,smp8642-intc", tangox_of_irq_init);