1 | // SPDX-License-Identifier: GPL-2.0 |
2 | |
3 | /* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. |
4 | * Copyright (C) 2019-2022 Linaro Ltd. |
5 | */ |
6 | |
7 | #include <linux/types.h> |
8 | #include <linux/platform_device.h> |
9 | #include <linux/interrupt.h> |
10 | #include <linux/notifier.h> |
11 | #include <linux/panic_notifier.h> |
12 | #include <linux/pm_runtime.h> |
13 | #include <linux/soc/qcom/smem.h> |
14 | #include <linux/soc/qcom/smem_state.h> |
15 | |
16 | #include "ipa_smp2p.h" |
17 | #include "ipa.h" |
18 | #include "ipa_uc.h" |
19 | |
20 | /** |
21 | * DOC: IPA SMP2P communication with the modem |
22 | * |
23 | * SMP2P is a primitive communication mechanism available between the AP and |
24 | * the modem. The IPA driver uses this for two purposes: to enable the modem |
25 | * to state that the GSI hardware is ready to use; and to communicate the |
26 | * state of IPA power in the event of a crash. |
27 | * |
28 | * GSI needs to have early initialization completed before it can be used. |
29 | * This initialization is done either by Trust Zone or by the modem. In the |
30 | * latter case, the modem uses an SMP2P interrupt to tell the AP IPA driver |
31 | * when the GSI is ready to use. |
32 | * |
33 | * The modem is also able to inquire about the current state of IPA |
34 | * power by trigging another SMP2P interrupt to the AP. We communicate |
35 | * whether power is enabled using two SMP2P state bits--one to indicate |
36 | * the power state (on or off), and a second to indicate the power state |
37 | * bit is valid. The modem will poll the valid bit until it is set, and |
38 | * at that time records whether the AP has IPA power enabled. |
39 | * |
40 | * Finally, if the AP kernel panics, we update the SMP2P state bits even if |
41 | * we never receive an interrupt from the modem requesting this. |
42 | */ |
43 | |
44 | /** |
45 | * struct ipa_smp2p - IPA SMP2P information |
46 | * @ipa: IPA pointer |
47 | * @valid_state: SMEM state indicating enabled state is valid |
48 | * @enabled_state: SMEM state to indicate power is enabled |
49 | * @valid_bit: Valid bit in 32-bit SMEM state mask |
50 | * @enabled_bit: Enabled bit in 32-bit SMEM state mask |
51 | * @enabled_bit: Enabled bit in 32-bit SMEM state mask |
52 | * @clock_query_irq: IPA interrupt triggered by modem for power query |
53 | * @setup_ready_irq: IPA interrupt triggered by modem to signal GSI ready |
54 | * @power_on: Whether IPA power is on |
55 | * @notified: Whether modem has been notified of power state |
56 | * @setup_disabled: Whether setup ready interrupt handler is disabled |
57 | * @mutex: Mutex protecting ready-interrupt/shutdown interlock |
58 | * @panic_notifier: Panic notifier structure |
59 | */ |
60 | struct ipa_smp2p { |
61 | struct ipa *ipa; |
62 | struct qcom_smem_state *valid_state; |
63 | struct qcom_smem_state *enabled_state; |
64 | u32 valid_bit; |
65 | u32 enabled_bit; |
66 | u32 clock_query_irq; |
67 | u32 setup_ready_irq; |
68 | bool power_on; |
69 | bool notified; |
70 | bool setup_disabled; |
71 | struct mutex mutex; |
72 | struct notifier_block panic_notifier; |
73 | }; |
74 | |
75 | /** |
76 | * ipa_smp2p_notify() - use SMP2P to tell modem about IPA power state |
77 | * @smp2p: SMP2P information |
78 | * |
79 | * This is called either when the modem has requested it (by triggering |
80 | * the modem power query IPA interrupt) or whenever the AP is shutting down |
81 | * (via a panic notifier). It sets the two SMP2P state bits--one saying |
82 | * whether the IPA power is on, and the other indicating the first bit |
83 | * is valid. |
84 | */ |
85 | static void ipa_smp2p_notify(struct ipa_smp2p *smp2p) |
86 | { |
87 | u32 value; |
88 | u32 mask; |
89 | |
90 | if (smp2p->notified) |
91 | return; |
92 | |
93 | smp2p->power_on = pm_runtime_get_if_active(dev: smp2p->ipa->dev) > 0; |
94 | |
95 | /* Signal whether the IPA power is enabled */ |
96 | mask = BIT(smp2p->enabled_bit); |
97 | value = smp2p->power_on ? mask : 0; |
98 | qcom_smem_state_update_bits(state: smp2p->enabled_state, mask, value); |
99 | |
100 | /* Now indicate that the enabled flag is valid */ |
101 | mask = BIT(smp2p->valid_bit); |
102 | value = mask; |
103 | qcom_smem_state_update_bits(state: smp2p->valid_state, mask, value); |
104 | |
105 | smp2p->notified = true; |
106 | } |
107 | |
108 | /* Threaded IRQ handler for modem "ipa-clock-query" SMP2P interrupt */ |
109 | static irqreturn_t ipa_smp2p_modem_clk_query_isr(int irq, void *dev_id) |
110 | { |
111 | struct ipa_smp2p *smp2p = dev_id; |
112 | |
113 | ipa_smp2p_notify(smp2p); |
114 | |
115 | return IRQ_HANDLED; |
116 | } |
117 | |
118 | static int ipa_smp2p_panic_notifier(struct notifier_block *nb, |
119 | unsigned long action, void *data) |
120 | { |
121 | struct ipa_smp2p *smp2p; |
122 | |
123 | smp2p = container_of(nb, struct ipa_smp2p, panic_notifier); |
124 | |
125 | ipa_smp2p_notify(smp2p); |
126 | |
127 | if (smp2p->power_on) |
128 | ipa_uc_panic_notifier(ipa: smp2p->ipa); |
129 | |
130 | return NOTIFY_DONE; |
131 | } |
132 | |
133 | static int ipa_smp2p_panic_notifier_register(struct ipa_smp2p *smp2p) |
134 | { |
135 | /* IPA panic handler needs to run before modem shuts down */ |
136 | smp2p->panic_notifier.notifier_call = ipa_smp2p_panic_notifier; |
137 | smp2p->panic_notifier.priority = INT_MAX; /* Do it early */ |
138 | |
139 | return atomic_notifier_chain_register(nh: &panic_notifier_list, |
140 | nb: &smp2p->panic_notifier); |
141 | } |
142 | |
143 | static void ipa_smp2p_panic_notifier_unregister(struct ipa_smp2p *smp2p) |
144 | { |
145 | atomic_notifier_chain_unregister(nh: &panic_notifier_list, |
146 | nb: &smp2p->panic_notifier); |
147 | } |
148 | |
149 | /* Threaded IRQ handler for modem "ipa-setup-ready" SMP2P interrupt */ |
150 | static irqreturn_t ipa_smp2p_modem_setup_ready_isr(int irq, void *dev_id) |
151 | { |
152 | struct ipa_smp2p *smp2p = dev_id; |
153 | struct ipa *ipa = smp2p->ipa; |
154 | struct device *dev; |
155 | int ret; |
156 | |
157 | /* Ignore any (spurious) interrupts received after the first */ |
158 | if (ipa->setup_complete) |
159 | return IRQ_HANDLED; |
160 | |
161 | /* Power needs to be active for setup */ |
162 | dev = ipa->dev; |
163 | ret = pm_runtime_get_sync(dev); |
164 | if (ret < 0) { |
165 | dev_err(dev, "error %d getting power for setup\n" , ret); |
166 | goto out_power_put; |
167 | } |
168 | |
169 | /* An error here won't cause driver shutdown, so warn if one occurs */ |
170 | ret = ipa_setup(ipa); |
171 | WARN(ret != 0, "error %d from ipa_setup()\n" , ret); |
172 | |
173 | out_power_put: |
174 | pm_runtime_mark_last_busy(dev); |
175 | (void)pm_runtime_put_autosuspend(dev); |
176 | |
177 | return IRQ_HANDLED; |
178 | } |
179 | |
180 | /* Initialize SMP2P interrupts */ |
181 | static int ipa_smp2p_irq_init(struct ipa_smp2p *smp2p, |
182 | struct platform_device *pdev, |
183 | const char *name, irq_handler_t handler) |
184 | { |
185 | struct device *dev = &pdev->dev; |
186 | unsigned int irq; |
187 | int ret; |
188 | |
189 | ret = platform_get_irq_byname(pdev, name); |
190 | if (ret <= 0) |
191 | return ret ? : -EINVAL; |
192 | irq = ret; |
193 | |
194 | ret = request_threaded_irq(irq, NULL, thread_fn: handler, flags: 0, name, dev: smp2p); |
195 | if (ret) { |
196 | dev_err(dev, "error %d requesting \"%s\" IRQ\n" , ret, name); |
197 | return ret; |
198 | } |
199 | |
200 | return irq; |
201 | } |
202 | |
203 | static void ipa_smp2p_irq_exit(struct ipa_smp2p *smp2p, u32 irq) |
204 | { |
205 | free_irq(irq, smp2p); |
206 | } |
207 | |
208 | /* Drop the power reference if it was taken in ipa_smp2p_notify() */ |
209 | static void ipa_smp2p_power_release(struct ipa *ipa) |
210 | { |
211 | struct device *dev = ipa->dev; |
212 | |
213 | if (!ipa->smp2p->power_on) |
214 | return; |
215 | |
216 | pm_runtime_mark_last_busy(dev); |
217 | (void)pm_runtime_put_autosuspend(dev); |
218 | ipa->smp2p->power_on = false; |
219 | } |
220 | |
221 | /* Initialize the IPA SMP2P subsystem */ |
222 | int |
223 | ipa_smp2p_init(struct ipa *ipa, struct platform_device *pdev, bool modem_init) |
224 | { |
225 | struct qcom_smem_state *enabled_state; |
226 | struct device *dev = &pdev->dev; |
227 | struct qcom_smem_state *valid_state; |
228 | struct ipa_smp2p *smp2p; |
229 | u32 enabled_bit; |
230 | u32 valid_bit; |
231 | int ret; |
232 | |
233 | valid_state = qcom_smem_state_get(dev, con_id: "ipa-clock-enabled-valid" , |
234 | bit: &valid_bit); |
235 | if (IS_ERR(ptr: valid_state)) |
236 | return PTR_ERR(ptr: valid_state); |
237 | if (valid_bit >= 32) /* BITS_PER_U32 */ |
238 | return -EINVAL; |
239 | |
240 | enabled_state = qcom_smem_state_get(dev, con_id: "ipa-clock-enabled" , |
241 | bit: &enabled_bit); |
242 | if (IS_ERR(ptr: enabled_state)) |
243 | return PTR_ERR(ptr: enabled_state); |
244 | if (enabled_bit >= 32) /* BITS_PER_U32 */ |
245 | return -EINVAL; |
246 | |
247 | smp2p = kzalloc(size: sizeof(*smp2p), GFP_KERNEL); |
248 | if (!smp2p) |
249 | return -ENOMEM; |
250 | |
251 | smp2p->ipa = ipa; |
252 | |
253 | /* These fields are needed by the power query interrupt |
254 | * handler, so initialize them now. |
255 | */ |
256 | mutex_init(&smp2p->mutex); |
257 | smp2p->valid_state = valid_state; |
258 | smp2p->valid_bit = valid_bit; |
259 | smp2p->enabled_state = enabled_state; |
260 | smp2p->enabled_bit = enabled_bit; |
261 | |
262 | /* We have enough information saved to handle notifications */ |
263 | ipa->smp2p = smp2p; |
264 | |
265 | ret = ipa_smp2p_irq_init(smp2p, pdev, name: "ipa-clock-query" , |
266 | handler: ipa_smp2p_modem_clk_query_isr); |
267 | if (ret < 0) |
268 | goto err_null_smp2p; |
269 | smp2p->clock_query_irq = ret; |
270 | |
271 | ret = ipa_smp2p_panic_notifier_register(smp2p); |
272 | if (ret) |
273 | goto err_irq_exit; |
274 | |
275 | if (modem_init) { |
276 | /* Result will be non-zero (negative for error) */ |
277 | ret = ipa_smp2p_irq_init(smp2p, pdev, name: "ipa-setup-ready" , |
278 | handler: ipa_smp2p_modem_setup_ready_isr); |
279 | if (ret < 0) |
280 | goto err_notifier_unregister; |
281 | smp2p->setup_ready_irq = ret; |
282 | } |
283 | |
284 | return 0; |
285 | |
286 | err_notifier_unregister: |
287 | ipa_smp2p_panic_notifier_unregister(smp2p); |
288 | err_irq_exit: |
289 | ipa_smp2p_irq_exit(smp2p, irq: smp2p->clock_query_irq); |
290 | err_null_smp2p: |
291 | ipa->smp2p = NULL; |
292 | mutex_destroy(lock: &smp2p->mutex); |
293 | kfree(objp: smp2p); |
294 | |
295 | return ret; |
296 | } |
297 | |
298 | void ipa_smp2p_exit(struct ipa *ipa) |
299 | { |
300 | struct ipa_smp2p *smp2p = ipa->smp2p; |
301 | |
302 | if (smp2p->setup_ready_irq) |
303 | ipa_smp2p_irq_exit(smp2p, irq: smp2p->setup_ready_irq); |
304 | ipa_smp2p_panic_notifier_unregister(smp2p); |
305 | ipa_smp2p_irq_exit(smp2p, irq: smp2p->clock_query_irq); |
306 | /* We won't get notified any more; drop power reference (if any) */ |
307 | ipa_smp2p_power_release(ipa); |
308 | ipa->smp2p = NULL; |
309 | mutex_destroy(lock: &smp2p->mutex); |
310 | kfree(objp: smp2p); |
311 | } |
312 | |
313 | void ipa_smp2p_irq_disable_setup(struct ipa *ipa) |
314 | { |
315 | struct ipa_smp2p *smp2p = ipa->smp2p; |
316 | |
317 | if (!smp2p->setup_ready_irq) |
318 | return; |
319 | |
320 | mutex_lock(&smp2p->mutex); |
321 | |
322 | if (!smp2p->setup_disabled) { |
323 | disable_irq(irq: smp2p->setup_ready_irq); |
324 | smp2p->setup_disabled = true; |
325 | } |
326 | |
327 | mutex_unlock(lock: &smp2p->mutex); |
328 | } |
329 | |
330 | /* Reset state tracking whether we have notified the modem */ |
331 | void ipa_smp2p_notify_reset(struct ipa *ipa) |
332 | { |
333 | struct ipa_smp2p *smp2p = ipa->smp2p; |
334 | u32 mask; |
335 | |
336 | if (!smp2p->notified) |
337 | return; |
338 | |
339 | ipa_smp2p_power_release(ipa); |
340 | |
341 | /* Reset the power enabled valid flag */ |
342 | mask = BIT(smp2p->valid_bit); |
343 | qcom_smem_state_update_bits(state: smp2p->valid_state, mask, value: 0); |
344 | |
345 | /* Mark the power disabled for good measure... */ |
346 | mask = BIT(smp2p->enabled_bit); |
347 | qcom_smem_state_update_bits(state: smp2p->enabled_state, mask, value: 0); |
348 | |
349 | smp2p->notified = false; |
350 | } |
351 | |