]>
Commit | Line | Data |
---|---|---|
e0720416 AT |
1 | /* |
2 | * Copyright (C) Maxime Coquelin 2015 | |
3 | * Author: Maxime Coquelin <[email protected]> | |
4 | * License terms: GNU General Public License (GPL), version 2 | |
5 | */ | |
6 | ||
7 | #include <linux/bitops.h> | |
8 | #include <linux/interrupt.h> | |
9 | #include <linux/io.h> | |
10 | #include <linux/irq.h> | |
11 | #include <linux/irqchip.h> | |
12 | #include <linux/irqchip/chained_irq.h> | |
13 | #include <linux/irqdomain.h> | |
14 | #include <linux/of_address.h> | |
15 | #include <linux/of_irq.h> | |
16 | ||
17 | #define EXTI_IMR 0x0 | |
18 | #define EXTI_EMR 0x4 | |
19 | #define EXTI_RTSR 0x8 | |
20 | #define EXTI_FTSR 0xc | |
21 | #define EXTI_SWIER 0x10 | |
22 | #define EXTI_PR 0x14 | |
23 | ||
24 | static void stm32_irq_handler(struct irq_desc *desc) | |
25 | { | |
26 | struct irq_domain *domain = irq_desc_get_handler_data(desc); | |
27 | struct irq_chip_generic *gc = domain->gc->gc[0]; | |
28 | struct irq_chip *chip = irq_desc_get_chip(desc); | |
29 | unsigned long pending; | |
30 | int n; | |
31 | ||
32 | chained_irq_enter(chip, desc); | |
33 | ||
34 | while ((pending = irq_reg_readl(gc, EXTI_PR))) { | |
35 | for_each_set_bit(n, &pending, BITS_PER_LONG) { | |
36 | generic_handle_irq(irq_find_mapping(domain, n)); | |
37 | irq_reg_writel(gc, BIT(n), EXTI_PR); | |
38 | } | |
39 | } | |
40 | ||
41 | chained_irq_exit(chip, desc); | |
42 | } | |
43 | ||
44 | static int stm32_irq_set_type(struct irq_data *data, unsigned int type) | |
45 | { | |
46 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); | |
47 | int pin = data->hwirq; | |
48 | u32 rtsr, ftsr; | |
49 | ||
50 | irq_gc_lock(gc); | |
51 | ||
52 | rtsr = irq_reg_readl(gc, EXTI_RTSR); | |
53 | ftsr = irq_reg_readl(gc, EXTI_FTSR); | |
54 | ||
55 | switch (type) { | |
56 | case IRQ_TYPE_EDGE_RISING: | |
57 | rtsr |= BIT(pin); | |
58 | ftsr &= ~BIT(pin); | |
59 | break; | |
60 | case IRQ_TYPE_EDGE_FALLING: | |
61 | rtsr &= ~BIT(pin); | |
62 | ftsr |= BIT(pin); | |
63 | break; | |
64 | case IRQ_TYPE_EDGE_BOTH: | |
65 | rtsr |= BIT(pin); | |
66 | ftsr |= BIT(pin); | |
67 | break; | |
68 | default: | |
69 | irq_gc_unlock(gc); | |
70 | return -EINVAL; | |
71 | } | |
72 | ||
73 | irq_reg_writel(gc, rtsr, EXTI_RTSR); | |
74 | irq_reg_writel(gc, ftsr, EXTI_FTSR); | |
75 | ||
76 | irq_gc_unlock(gc); | |
77 | ||
78 | return 0; | |
79 | } | |
80 | ||
81 | static int stm32_irq_set_wake(struct irq_data *data, unsigned int on) | |
82 | { | |
83 | struct irq_chip_generic *gc = irq_data_get_irq_chip_data(data); | |
84 | int pin = data->hwirq; | |
85 | u32 emr; | |
86 | ||
87 | irq_gc_lock(gc); | |
88 | ||
89 | emr = irq_reg_readl(gc, EXTI_EMR); | |
90 | if (on) | |
91 | emr |= BIT(pin); | |
92 | else | |
93 | emr &= ~BIT(pin); | |
94 | irq_reg_writel(gc, emr, EXTI_EMR); | |
95 | ||
96 | irq_gc_unlock(gc); | |
97 | ||
98 | return 0; | |
99 | } | |
100 | ||
101 | static int stm32_exti_alloc(struct irq_domain *d, unsigned int virq, | |
102 | unsigned int nr_irqs, void *data) | |
103 | { | |
104 | struct irq_chip_generic *gc = d->gc->gc[0]; | |
105 | struct irq_fwspec *fwspec = data; | |
106 | irq_hw_number_t hwirq; | |
107 | ||
108 | hwirq = fwspec->param[0]; | |
109 | ||
110 | irq_map_generic_chip(d, virq, hwirq); | |
111 | irq_domain_set_info(d, virq, hwirq, &gc->chip_types->chip, gc, | |
112 | handle_simple_irq, NULL, NULL); | |
113 | ||
114 | return 0; | |
115 | } | |
116 | ||
117 | static void stm32_exti_free(struct irq_domain *d, unsigned int virq, | |
118 | unsigned int nr_irqs) | |
119 | { | |
120 | struct irq_data *data = irq_domain_get_irq_data(d, virq); | |
121 | ||
122 | irq_domain_reset_irq_data(data); | |
123 | } | |
124 | ||
125 | struct irq_domain_ops irq_exti_domain_ops = { | |
126 | .map = irq_map_generic_chip, | |
127 | .xlate = irq_domain_xlate_onetwocell, | |
128 | .alloc = stm32_exti_alloc, | |
129 | .free = stm32_exti_free, | |
130 | }; | |
131 | ||
132 | static int __init stm32_exti_init(struct device_node *node, | |
133 | struct device_node *parent) | |
134 | { | |
135 | unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN; | |
136 | int nr_irqs, nr_exti, ret, i; | |
137 | struct irq_chip_generic *gc; | |
138 | struct irq_domain *domain; | |
139 | void *base; | |
140 | ||
141 | base = of_iomap(node, 0); | |
142 | if (!base) { | |
143 | pr_err("%s: Unable to map registers\n", node->full_name); | |
144 | return -ENOMEM; | |
145 | } | |
146 | ||
147 | /* Determine number of irqs supported */ | |
148 | writel_relaxed(~0UL, base + EXTI_RTSR); | |
149 | nr_exti = fls(readl_relaxed(base + EXTI_RTSR)); | |
150 | writel_relaxed(0, base + EXTI_RTSR); | |
151 | ||
152 | pr_info("%s: %d External IRQs detected\n", node->full_name, nr_exti); | |
153 | ||
154 | domain = irq_domain_add_linear(node, nr_exti, | |
155 | &irq_exti_domain_ops, NULL); | |
156 | if (!domain) { | |
157 | pr_err("%s: Could not register interrupt domain.\n", | |
158 | node->name); | |
159 | ret = -ENOMEM; | |
160 | goto out_unmap; | |
161 | } | |
162 | ||
163 | ret = irq_alloc_domain_generic_chips(domain, nr_exti, 1, "exti", | |
164 | handle_edge_irq, clr, 0, 0); | |
165 | if (ret) { | |
166 | pr_err("%s: Could not allocate generic interrupt chip.\n", | |
167 | node->full_name); | |
168 | goto out_free_domain; | |
169 | } | |
170 | ||
171 | gc = domain->gc->gc[0]; | |
172 | gc->reg_base = base; | |
173 | gc->chip_types->type = IRQ_TYPE_EDGE_BOTH; | |
174 | gc->chip_types->chip.name = gc->chip_types[0].chip.name; | |
175 | gc->chip_types->chip.irq_ack = irq_gc_ack_set_bit; | |
176 | gc->chip_types->chip.irq_mask = irq_gc_mask_clr_bit; | |
177 | gc->chip_types->chip.irq_unmask = irq_gc_mask_set_bit; | |
178 | gc->chip_types->chip.irq_set_type = stm32_irq_set_type; | |
179 | gc->chip_types->chip.irq_set_wake = stm32_irq_set_wake; | |
180 | gc->chip_types->regs.ack = EXTI_PR; | |
181 | gc->chip_types->regs.mask = EXTI_IMR; | |
182 | gc->chip_types->handler = handle_edge_irq; | |
183 | ||
184 | nr_irqs = of_irq_count(node); | |
185 | for (i = 0; i < nr_irqs; i++) { | |
186 | unsigned int irq = irq_of_parse_and_map(node, i); | |
187 | ||
188 | irq_set_handler_data(irq, domain); | |
189 | irq_set_chained_handler(irq, stm32_irq_handler); | |
190 | } | |
191 | ||
192 | return 0; | |
193 | ||
194 | out_free_domain: | |
195 | irq_domain_remove(domain); | |
196 | out_unmap: | |
197 | iounmap(base); | |
198 | return ret; | |
199 | } | |
200 | ||
201 | IRQCHIP_DECLARE(stm32_exti, "st,stm32-exti", stm32_exti_init); |