1 | // SPDX-License-Identifier: GPL-2.0 |
2 | |
3 | /* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. |
4 | * Copyright (C) 2018-2022 Linaro Ltd. |
5 | */ |
6 | |
7 | /* DOC: IPA Interrupts |
8 | * |
9 | * The IPA has an interrupt line distinct from the interrupt used by the GSI |
10 | * code. Whereas GSI interrupts are generally related to channel events (like |
11 | * transfer completions), IPA interrupts are related to other events related |
12 | * to the IPA. Some of the IPA interrupts come from a microcontroller |
13 | * embedded in the IPA. Each IPA interrupt type can be both masked and |
14 | * acknowledged independent of the others. |
15 | * |
16 | * Two of the IPA interrupts are initiated by the microcontroller. A third |
17 | * can be generated to signal the need for a wakeup/resume when an IPA |
18 | * endpoint has been suspended. There are other IPA events, but at this |
19 | * time only these three are supported. |
20 | */ |
21 | |
22 | #include <linux/platform_device.h> |
23 | #include <linux/types.h> |
24 | #include <linux/interrupt.h> |
25 | #include <linux/pm_runtime.h> |
26 | #include <linux/pm_wakeirq.h> |
27 | |
28 | #include "ipa.h" |
29 | #include "ipa_reg.h" |
30 | #include "ipa_endpoint.h" |
31 | #include "ipa_power.h" |
32 | #include "ipa_uc.h" |
33 | #include "ipa_interrupt.h" |
34 | |
35 | /** |
36 | * struct ipa_interrupt - IPA interrupt information |
37 | * @ipa: IPA pointer |
38 | * @irq: Linux IRQ number used for IPA interrupts |
39 | * @enabled: Mask indicating which interrupts are enabled |
40 | */ |
41 | struct ipa_interrupt { |
42 | struct ipa *ipa; |
43 | u32 irq; |
44 | u32 enabled; |
45 | }; |
46 | |
47 | /* Clear the suspend interrupt for all endpoints that signaled it */ |
48 | static void ipa_interrupt_suspend_clear_all(struct ipa_interrupt *interrupt) |
49 | { |
50 | struct ipa *ipa = interrupt->ipa; |
51 | u32 unit_count; |
52 | u32 unit; |
53 | |
54 | unit_count = DIV_ROUND_UP(ipa->endpoint_count, 32); |
55 | for (unit = 0; unit < unit_count; unit++) { |
56 | const struct reg *reg; |
57 | u32 val; |
58 | |
59 | reg = ipa_reg(ipa, reg_id: IRQ_SUSPEND_INFO); |
60 | val = ioread32(ipa->reg_virt + reg_n_offset(reg, n: unit)); |
61 | |
62 | /* SUSPEND interrupt status isn't cleared on IPA version 3.0 */ |
63 | if (!val || ipa->version == IPA_VERSION_3_0) |
64 | continue; |
65 | |
66 | reg = ipa_reg(ipa, reg_id: IRQ_SUSPEND_CLR); |
67 | iowrite32(val, ipa->reg_virt + reg_n_offset(reg, n: unit)); |
68 | } |
69 | } |
70 | |
71 | /* Process a particular interrupt type that has been received */ |
72 | static void ipa_interrupt_process(struct ipa_interrupt *interrupt, u32 irq_id) |
73 | { |
74 | struct ipa *ipa = interrupt->ipa; |
75 | const struct reg *reg; |
76 | u32 mask = BIT(irq_id); |
77 | u32 offset; |
78 | |
79 | reg = ipa_reg(ipa, reg_id: IPA_IRQ_CLR); |
80 | offset = reg_offset(reg); |
81 | |
82 | switch (irq_id) { |
83 | case IPA_IRQ_UC_0: |
84 | case IPA_IRQ_UC_1: |
85 | /* For microcontroller interrupts, clear the interrupt right |
86 | * away, "to avoid clearing unhandled interrupts." |
87 | */ |
88 | iowrite32(mask, ipa->reg_virt + offset); |
89 | ipa_uc_interrupt_handler(ipa, irq_id); |
90 | break; |
91 | |
92 | case IPA_IRQ_TX_SUSPEND: |
93 | /* Clearing the SUSPEND_TX interrupt also clears the |
94 | * register that tells us which suspended endpoint(s) |
95 | * caused the interrupt, so defer clearing until after |
96 | * the handler has been called. |
97 | */ |
98 | ipa_interrupt_suspend_clear_all(interrupt); |
99 | fallthrough; |
100 | |
101 | default: /* Silently ignore (and clear) any other condition */ |
102 | iowrite32(mask, ipa->reg_virt + offset); |
103 | break; |
104 | } |
105 | } |
106 | |
107 | /* IPA IRQ handler is threaded */ |
108 | static irqreturn_t ipa_isr_thread(int irq, void *dev_id) |
109 | { |
110 | struct ipa_interrupt *interrupt = dev_id; |
111 | struct ipa *ipa = interrupt->ipa; |
112 | u32 enabled = interrupt->enabled; |
113 | struct device *dev = ipa->dev; |
114 | const struct reg *reg; |
115 | u32 pending; |
116 | u32 offset; |
117 | u32 mask; |
118 | int ret; |
119 | |
120 | ret = pm_runtime_get_sync(dev); |
121 | if (WARN_ON(ret < 0)) |
122 | goto out_power_put; |
123 | |
124 | /* The status register indicates which conditions are present, |
125 | * including conditions whose interrupt is not enabled. Handle |
126 | * only the enabled ones. |
127 | */ |
128 | reg = ipa_reg(ipa, reg_id: IPA_IRQ_STTS); |
129 | offset = reg_offset(reg); |
130 | pending = ioread32(ipa->reg_virt + offset); |
131 | while ((mask = pending & enabled)) { |
132 | do { |
133 | u32 irq_id = __ffs(mask); |
134 | |
135 | mask ^= BIT(irq_id); |
136 | |
137 | ipa_interrupt_process(interrupt, irq_id); |
138 | } while (mask); |
139 | pending = ioread32(ipa->reg_virt + offset); |
140 | } |
141 | |
142 | /* If any disabled interrupts are pending, clear them */ |
143 | if (pending) { |
144 | dev_dbg(dev, "clearing disabled IPA interrupts 0x%08x\n" , |
145 | pending); |
146 | reg = ipa_reg(ipa, reg_id: IPA_IRQ_CLR); |
147 | iowrite32(pending, ipa->reg_virt + reg_offset(reg)); |
148 | } |
149 | out_power_put: |
150 | pm_runtime_mark_last_busy(dev); |
151 | (void)pm_runtime_put_autosuspend(dev); |
152 | |
153 | return IRQ_HANDLED; |
154 | } |
155 | |
156 | static void ipa_interrupt_enabled_update(struct ipa *ipa) |
157 | { |
158 | const struct reg *reg = ipa_reg(ipa, reg_id: IPA_IRQ_EN); |
159 | |
160 | iowrite32(ipa->interrupt->enabled, ipa->reg_virt + reg_offset(reg)); |
161 | } |
162 | |
163 | /* Enable an IPA interrupt type */ |
164 | void ipa_interrupt_enable(struct ipa *ipa, enum ipa_irq_id ipa_irq) |
165 | { |
166 | /* Update the IPA interrupt mask to enable it */ |
167 | ipa->interrupt->enabled |= BIT(ipa_irq); |
168 | ipa_interrupt_enabled_update(ipa); |
169 | } |
170 | |
171 | /* Disable an IPA interrupt type */ |
172 | void ipa_interrupt_disable(struct ipa *ipa, enum ipa_irq_id ipa_irq) |
173 | { |
174 | /* Update the IPA interrupt mask to disable it */ |
175 | ipa->interrupt->enabled &= ~BIT(ipa_irq); |
176 | ipa_interrupt_enabled_update(ipa); |
177 | } |
178 | |
179 | void ipa_interrupt_irq_disable(struct ipa *ipa) |
180 | { |
181 | disable_irq(irq: ipa->interrupt->irq); |
182 | } |
183 | |
184 | void ipa_interrupt_irq_enable(struct ipa *ipa) |
185 | { |
186 | enable_irq(irq: ipa->interrupt->irq); |
187 | } |
188 | |
189 | /* Common function used to enable/disable TX_SUSPEND for an endpoint */ |
190 | static void ipa_interrupt_suspend_control(struct ipa_interrupt *interrupt, |
191 | u32 endpoint_id, bool enable) |
192 | { |
193 | struct ipa *ipa = interrupt->ipa; |
194 | u32 mask = BIT(endpoint_id % 32); |
195 | u32 unit = endpoint_id / 32; |
196 | const struct reg *reg; |
197 | u32 offset; |
198 | u32 val; |
199 | |
200 | WARN_ON(!test_bit(endpoint_id, ipa->available)); |
201 | |
202 | /* IPA version 3.0 does not support TX_SUSPEND interrupt control */ |
203 | if (ipa->version == IPA_VERSION_3_0) |
204 | return; |
205 | |
206 | reg = ipa_reg(ipa, reg_id: IRQ_SUSPEND_EN); |
207 | offset = reg_n_offset(reg, n: unit); |
208 | val = ioread32(ipa->reg_virt + offset); |
209 | |
210 | if (enable) |
211 | val |= mask; |
212 | else |
213 | val &= ~mask; |
214 | |
215 | iowrite32(val, ipa->reg_virt + offset); |
216 | } |
217 | |
218 | /* Enable TX_SUSPEND for an endpoint */ |
219 | void |
220 | ipa_interrupt_suspend_enable(struct ipa_interrupt *interrupt, u32 endpoint_id) |
221 | { |
222 | ipa_interrupt_suspend_control(interrupt, endpoint_id, enable: true); |
223 | } |
224 | |
225 | /* Disable TX_SUSPEND for an endpoint */ |
226 | void |
227 | ipa_interrupt_suspend_disable(struct ipa_interrupt *interrupt, u32 endpoint_id) |
228 | { |
229 | ipa_interrupt_suspend_control(interrupt, endpoint_id, enable: false); |
230 | } |
231 | |
232 | /* Simulate arrival of an IPA TX_SUSPEND interrupt */ |
233 | void ipa_interrupt_simulate_suspend(struct ipa_interrupt *interrupt) |
234 | { |
235 | ipa_interrupt_process(interrupt, irq_id: IPA_IRQ_TX_SUSPEND); |
236 | } |
237 | |
238 | /* Configure the IPA interrupt framework */ |
239 | int ipa_interrupt_config(struct ipa *ipa) |
240 | { |
241 | struct ipa_interrupt *interrupt = ipa->interrupt; |
242 | unsigned int irq = interrupt->irq; |
243 | struct device *dev = ipa->dev; |
244 | const struct reg *reg; |
245 | int ret; |
246 | |
247 | interrupt->ipa = ipa; |
248 | |
249 | /* Disable all IPA interrupt types */ |
250 | reg = ipa_reg(ipa, reg_id: IPA_IRQ_EN); |
251 | iowrite32(0, ipa->reg_virt + reg_offset(reg)); |
252 | |
253 | ret = request_threaded_irq(irq, NULL, thread_fn: ipa_isr_thread, IRQF_ONESHOT, |
254 | name: "ipa" , dev: interrupt); |
255 | if (ret) { |
256 | dev_err(dev, "error %d requesting \"ipa\" IRQ\n" , ret); |
257 | goto err_kfree; |
258 | } |
259 | |
260 | ret = dev_pm_set_wake_irq(dev, irq); |
261 | if (ret) { |
262 | dev_err(dev, "error %d registering \"ipa\" IRQ as wakeirq\n" , |
263 | ret); |
264 | goto err_free_irq; |
265 | } |
266 | |
267 | ipa->interrupt = interrupt; |
268 | |
269 | return 0; |
270 | |
271 | err_free_irq: |
272 | free_irq(interrupt->irq, interrupt); |
273 | err_kfree: |
274 | kfree(objp: interrupt); |
275 | |
276 | return ret; |
277 | } |
278 | |
279 | /* Inverse of ipa_interrupt_config() */ |
280 | void ipa_interrupt_deconfig(struct ipa *ipa) |
281 | { |
282 | struct ipa_interrupt *interrupt = ipa->interrupt; |
283 | struct device *dev = ipa->dev; |
284 | |
285 | ipa->interrupt = NULL; |
286 | |
287 | dev_pm_clear_wake_irq(dev); |
288 | free_irq(interrupt->irq, interrupt); |
289 | } |
290 | |
291 | /* Initialize the IPA interrupt structure */ |
292 | struct ipa_interrupt *ipa_interrupt_init(struct platform_device *pdev) |
293 | { |
294 | struct device *dev = &pdev->dev; |
295 | struct ipa_interrupt *interrupt; |
296 | int irq; |
297 | |
298 | irq = platform_get_irq_byname(pdev, "ipa" ); |
299 | if (irq <= 0) { |
300 | dev_err(dev, "DT error %d getting \"ipa\" IRQ property\n" , irq); |
301 | |
302 | return ERR_PTR(error: irq ? : -EINVAL); |
303 | } |
304 | |
305 | interrupt = kzalloc(size: sizeof(*interrupt), GFP_KERNEL); |
306 | if (!interrupt) |
307 | return ERR_PTR(error: -ENOMEM); |
308 | interrupt->irq = irq; |
309 | |
310 | return interrupt; |
311 | } |
312 | |
313 | /* Inverse of ipa_interrupt_init() */ |
314 | void ipa_interrupt_exit(struct ipa_interrupt *interrupt) |
315 | { |
316 | kfree(objp: interrupt); |
317 | } |
318 | |