Commit | Line | Data |
---|---|---|
ba764c4d AE |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | ||
3 | /* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. | |
a4388da5 | 4 | * Copyright (C) 2018-2022 Linaro Ltd. |
ba764c4d AE |
5 | */ |
6 | ||
ba764c4d AE |
7 | #include <linux/clk.h> |
8 | #include <linux/device.h> | |
9 | #include <linux/interconnect.h> | |
73ff316d | 10 | #include <linux/pm.h> |
2abb0c7f | 11 | #include <linux/pm_runtime.h> |
73ff316d | 12 | #include <linux/bitops.h> |
ba764c4d | 13 | |
34a08176 AE |
14 | #include "linux/soc/qcom/qcom_aoss.h" |
15 | ||
ba764c4d | 16 | #include "ipa.h" |
2775cbc5 | 17 | #include "ipa_power.h" |
73ff316d | 18 | #include "ipa_endpoint.h" |
ba764c4d | 19 | #include "ipa_modem.h" |
dfccb8b1 | 20 | #include "ipa_data.h" |
ba764c4d AE |
21 | |
22 | /** | |
7aa0e8b8 | 23 | * DOC: IPA Power Management |
ba764c4d | 24 | * |
7aa0e8b8 AE |
25 | * The IPA hardware is enabled when the IPA core clock and all the |
26 | * interconnects (buses) it depends on are enabled. Runtime power | |
27 | * management is used to determine whether the core clock and | |
28 | * interconnects are enabled, and if not in use to be suspended | |
29 | * automatically. | |
ba764c4d | 30 | * |
7aa0e8b8 AE |
31 | * The core clock currently runs at a fixed clock rate when enabled, |
32 | * an all interconnects use a fixed average and peak bandwidth. | |
ba764c4d AE |
33 | */ |
34 | ||
1aac309d AE |
35 | #define IPA_AUTOSUSPEND_DELAY 500 /* milliseconds */ |
36 | ||
afb08b7e AE |
37 | /** |
38 | * enum ipa_power_flag - IPA power flags | |
39 | * @IPA_POWER_FLAG_RESUMED: Whether resume from suspend has been signaled | |
b9c532c1 | 40 | * @IPA_POWER_FLAG_SYSTEM: Hardware is system (not runtime) suspended |
b8e36e13 AE |
41 | * @IPA_POWER_FLAG_STOPPED: Modem TX is disabled by ipa_start_xmit() |
42 | * @IPA_POWER_FLAG_STARTED: Modem TX was enabled by ipa_runtime_resume() | |
afb08b7e AE |
43 | * @IPA_POWER_FLAG_COUNT: Number of defined power flags |
44 | */ | |
45 | enum ipa_power_flag { | |
46 | IPA_POWER_FLAG_RESUMED, | |
b9c532c1 | 47 | IPA_POWER_FLAG_SYSTEM, |
b8e36e13 AE |
48 | IPA_POWER_FLAG_STOPPED, |
49 | IPA_POWER_FLAG_STARTED, | |
afb08b7e AE |
50 | IPA_POWER_FLAG_COUNT, /* Last; not a flag */ |
51 | }; | |
52 | ||
ba764c4d | 53 | /** |
7aa0e8b8 | 54 | * struct ipa_power - IPA power management information |
63de79f0 | 55 | * @dev: IPA device pointer |
ba764c4d | 56 | * @core: IPA core clock |
34a08176 | 57 | * @qmp: QMP handle for AOSS communication |
b8e36e13 | 58 | * @spinlock: Protects modem TX queue enable/disable |
afb08b7e | 59 | * @flags: Boolean state flags |
ea151e19 | 60 | * @interconnect_count: Number of elements in interconnect[] |
5b40810b | 61 | * @interconnect: Interconnect array |
ba764c4d | 62 | */ |
7aa0e8b8 | 63 | struct ipa_power { |
63de79f0 | 64 | struct device *dev; |
ba764c4d | 65 | struct clk *core; |
34a08176 | 66 | struct qmp *qmp; |
b8e36e13 | 67 | spinlock_t spinlock; /* used with STOPPED/STARTED power flags */ |
afb08b7e | 68 | DECLARE_BITMAP(flags, IPA_POWER_FLAG_COUNT); |
ea151e19 | 69 | u32 interconnect_count; |
20551ee4 | 70 | struct icc_bulk_data interconnect[] __counted_by(interconnect_count); |
ba764c4d AE |
71 | }; |
72 | ||
ba764c4d | 73 | /* Initialize interconnects required for IPA operation */ |
37e0cf33 | 74 | static int ipa_interconnect_init(struct ipa_power *power, |
db6cd514 | 75 | const struct ipa_interconnect_data *data) |
ba764c4d | 76 | { |
c7be12fa | 77 | struct icc_bulk_data *interconnect; |
10d0d397 | 78 | int ret; |
8ee7ec48 | 79 | u32 i; |
ea151e19 | 80 | |
63ac8cce | 81 | /* Initialize our interconnect data array for bulk operations */ |
8ee7ec48 AE |
82 | interconnect = &power->interconnect[0]; |
83 | for (i = 0; i < power->interconnect_count; i++) { | |
63ac8cce AE |
84 | /* interconnect->path is filled in by of_icc_bulk_get() */ |
85 | interconnect->name = data->name; | |
86 | interconnect->avg_bw = data->average_bandwidth; | |
87 | interconnect->peak_bw = data->peak_bandwidth; | |
88 | data++; | |
ea151e19 AE |
89 | interconnect++; |
90 | } | |
ba764c4d | 91 | |
37e0cf33 | 92 | ret = of_icc_bulk_get(power->dev, power->interconnect_count, |
63ac8cce AE |
93 | power->interconnect); |
94 | if (ret) | |
8ee7ec48 | 95 | return ret; |
63ac8cce AE |
96 | |
97 | /* All interconnects are initially disabled */ | |
98 | icc_bulk_disable(power->interconnect_count, power->interconnect); | |
99 | ||
100 | /* Set the bandwidth values to be used when enabled */ | |
101 | ret = icc_bulk_set_bw(power->interconnect_count, power->interconnect); | |
102 | if (ret) | |
8ee7ec48 | 103 | icc_bulk_put(power->interconnect_count, power->interconnect); |
10d0d397 AE |
104 | |
105 | return ret; | |
ba764c4d AE |
106 | } |
107 | ||
108 | /* Inverse of ipa_interconnect_init() */ | |
7aa0e8b8 | 109 | static void ipa_interconnect_exit(struct ipa_power *power) |
ba764c4d | 110 | { |
63ac8cce | 111 | icc_bulk_put(power->interconnect_count, power->interconnect); |
ba764c4d AE |
112 | } |
113 | ||
7aa0e8b8 AE |
114 | /* Enable IPA power, enabling interconnects and the core clock */ |
115 | static int ipa_power_enable(struct ipa *ipa) | |
ba764c4d | 116 | { |
90078e63 | 117 | struct ipa_power *power = ipa->power; |
ba764c4d AE |
118 | int ret; |
119 | ||
90078e63 | 120 | ret = icc_bulk_enable(power->interconnect_count, power->interconnect); |
ba764c4d AE |
121 | if (ret) |
122 | return ret; | |
123 | ||
90078e63 | 124 | ret = clk_prepare_enable(power->core); |
8ee7c40a | 125 | if (ret) { |
37e0cf33 | 126 | dev_err(power->dev, "error %d enabling core clock\n", ret); |
90078e63 AE |
127 | icc_bulk_disable(power->interconnect_count, |
128 | power->interconnect); | |
8ee7c40a | 129 | } |
ba764c4d AE |
130 | |
131 | return ret; | |
132 | } | |
133 | ||
7aa0e8b8 | 134 | /* Inverse of ipa_power_enable() */ |
90078e63 | 135 | static void ipa_power_disable(struct ipa *ipa) |
ba764c4d | 136 | { |
90078e63 AE |
137 | struct ipa_power *power = ipa->power; |
138 | ||
139 | clk_disable_unprepare(power->core); | |
7ebd168c | 140 | |
90078e63 | 141 | icc_bulk_disable(power->interconnect_count, power->interconnect); |
ba764c4d AE |
142 | } |
143 | ||
2abb0c7f AE |
144 | static int ipa_runtime_suspend(struct device *dev) |
145 | { | |
146 | struct ipa *ipa = dev_get_drvdata(dev); | |
147 | ||
148 | /* Endpoints aren't usable until setup is complete */ | |
149 | if (ipa->setup_complete) { | |
7aa0e8b8 | 150 | __clear_bit(IPA_POWER_FLAG_RESUMED, ipa->power->flags); |
2abb0c7f AE |
151 | ipa_endpoint_suspend(ipa); |
152 | gsi_suspend(&ipa->gsi); | |
153 | } | |
154 | ||
90078e63 AE |
155 | ipa_power_disable(ipa); |
156 | ||
157 | return 0; | |
2abb0c7f AE |
158 | } |
159 | ||
160 | static int ipa_runtime_resume(struct device *dev) | |
161 | { | |
162 | struct ipa *ipa = dev_get_drvdata(dev); | |
163 | int ret; | |
164 | ||
7aa0e8b8 | 165 | ret = ipa_power_enable(ipa); |
2abb0c7f AE |
166 | if (WARN_ON(ret < 0)) |
167 | return ret; | |
168 | ||
169 | /* Endpoints aren't usable until setup is complete */ | |
170 | if (ipa->setup_complete) { | |
171 | gsi_resume(&ipa->gsi); | |
172 | ipa_endpoint_resume(ipa); | |
173 | } | |
174 | ||
175 | return 0; | |
176 | } | |
177 | ||
b9c532c1 AE |
178 | static int ipa_suspend(struct device *dev) |
179 | { | |
180 | struct ipa *ipa = dev_get_drvdata(dev); | |
181 | ||
7aa0e8b8 | 182 | __set_bit(IPA_POWER_FLAG_SYSTEM, ipa->power->flags); |
b9c532c1 | 183 | |
9ec9b2a3 CC |
184 | /* Increment the disable depth to ensure that the IRQ won't |
185 | * be re-enabled until the matching _enable call in | |
186 | * ipa_resume(). We do this to ensure that the interrupt | |
187 | * handler won't run whilst PM runtime is disabled. | |
188 | * | |
189 | * Note that disabling the IRQ is NOT the same as disabling | |
190 | * irq wake. If wakeup is enabled for the IPA then the IRQ | |
191 | * will still cause the system to wake up, see irq_set_irq_wake(). | |
192 | */ | |
193 | ipa_interrupt_irq_disable(ipa); | |
194 | ||
b9c532c1 AE |
195 | return pm_runtime_force_suspend(dev); |
196 | } | |
197 | ||
198 | static int ipa_resume(struct device *dev) | |
199 | { | |
200 | struct ipa *ipa = dev_get_drvdata(dev); | |
201 | int ret; | |
202 | ||
203 | ret = pm_runtime_force_resume(dev); | |
204 | ||
7aa0e8b8 | 205 | __clear_bit(IPA_POWER_FLAG_SYSTEM, ipa->power->flags); |
b9c532c1 | 206 | |
9ec9b2a3 CC |
207 | /* Now that PM runtime is enabled again it's safe |
208 | * to turn the IRQ back on and process any data | |
209 | * that was received during suspend. | |
210 | */ | |
211 | ipa_interrupt_irq_enable(ipa); | |
212 | ||
b9c532c1 AE |
213 | return ret; |
214 | } | |
215 | ||
78b348f3 | 216 | /* Return the current IPA core clock rate */ |
7aa0e8b8 | 217 | u32 ipa_core_clock_rate(struct ipa *ipa) |
78b348f3 | 218 | { |
7aa0e8b8 | 219 | return ipa->power ? (u32)clk_get_rate(ipa->power->core) : 0; |
78b348f3 AE |
220 | } |
221 | ||
482ae3a9 | 222 | void ipa_power_suspend_handler(struct ipa *ipa, enum ipa_irq_id irq_id) |
afe1baa8 | 223 | { |
b9c532c1 AE |
224 | /* To handle an IPA interrupt we will have resumed the hardware |
225 | * just to handle the interrupt, so we're done. If we are in a | |
226 | * system suspend, trigger a system resume. | |
afe1baa8 | 227 | */ |
7aa0e8b8 AE |
228 | if (!__test_and_set_bit(IPA_POWER_FLAG_RESUMED, ipa->power->flags)) |
229 | if (test_bit(IPA_POWER_FLAG_SYSTEM, ipa->power->flags)) | |
b9c532c1 | 230 | pm_wakeup_dev_event(&ipa->pdev->dev, 0, true); |
afe1baa8 AE |
231 | |
232 | /* Acknowledge/clear the suspend interrupt on all endpoints */ | |
233 | ipa_interrupt_suspend_clear_all(ipa->interrupt); | |
234 | } | |
235 | ||
b8e36e13 AE |
236 | /* The next few functions coordinate stopping and starting the modem |
237 | * network device transmit queue. | |
238 | * | |
239 | * Transmit can be running concurrent with power resume, and there's a | |
240 | * chance the resume completes before the transmit path stops the queue, | |
241 | * leaving the queue in a stopped state. The next two functions are used | |
242 | * to avoid this: ipa_power_modem_queue_stop() is used by ipa_start_xmit() | |
243 | * to conditionally stop the TX queue; and ipa_power_modem_queue_start() | |
244 | * is used by ipa_runtime_resume() to conditionally restart it. | |
245 | * | |
246 | * Two flags and a spinlock are used. If the queue is stopped, the STOPPED | |
247 | * power flag is set. And if the queue is started, the STARTED flag is set. | |
248 | * The queue is only started on resume if the STOPPED flag is set. And the | |
249 | * queue is only started in ipa_start_xmit() if the STARTED flag is *not* | |
250 | * set. As a result, the queue remains operational if the two activites | |
251 | * happen concurrently regardless of the order they complete. The spinlock | |
252 | * ensures the flag and TX queue operations are done atomically. | |
253 | * | |
254 | * The first function stops the modem netdev transmit queue, but only if | |
255 | * the STARTED flag is *not* set. That flag is cleared if it was set. | |
256 | * If the queue is stopped, the STOPPED flag is set. This is called only | |
257 | * from the power ->runtime_resume operation. | |
258 | */ | |
259 | void ipa_power_modem_queue_stop(struct ipa *ipa) | |
260 | { | |
7aa0e8b8 | 261 | struct ipa_power *power = ipa->power; |
b8e36e13 AE |
262 | unsigned long flags; |
263 | ||
7aa0e8b8 | 264 | spin_lock_irqsave(&power->spinlock, flags); |
b8e36e13 | 265 | |
7aa0e8b8 | 266 | if (!__test_and_clear_bit(IPA_POWER_FLAG_STARTED, power->flags)) { |
b8e36e13 | 267 | netif_stop_queue(ipa->modem_netdev); |
7aa0e8b8 | 268 | __set_bit(IPA_POWER_FLAG_STOPPED, power->flags); |
b8e36e13 AE |
269 | } |
270 | ||
7aa0e8b8 | 271 | spin_unlock_irqrestore(&power->spinlock, flags); |
b8e36e13 AE |
272 | } |
273 | ||
274 | /* This function starts the modem netdev transmit queue, but only if the | |
275 | * STOPPED flag is set. That flag is cleared if it was set. If the queue | |
276 | * was restarted, the STARTED flag is set; this allows ipa_start_xmit() | |
277 | * to skip stopping the queue in the event of a race. | |
278 | */ | |
279 | void ipa_power_modem_queue_wake(struct ipa *ipa) | |
280 | { | |
7aa0e8b8 | 281 | struct ipa_power *power = ipa->power; |
b8e36e13 AE |
282 | unsigned long flags; |
283 | ||
7aa0e8b8 | 284 | spin_lock_irqsave(&power->spinlock, flags); |
b8e36e13 | 285 | |
7aa0e8b8 AE |
286 | if (__test_and_clear_bit(IPA_POWER_FLAG_STOPPED, power->flags)) { |
287 | __set_bit(IPA_POWER_FLAG_STARTED, power->flags); | |
b8e36e13 AE |
288 | netif_wake_queue(ipa->modem_netdev); |
289 | } | |
290 | ||
7aa0e8b8 | 291 | spin_unlock_irqrestore(&power->spinlock, flags); |
b8e36e13 AE |
292 | } |
293 | ||
294 | /* This function clears the STARTED flag once the TX queue is operating */ | |
295 | void ipa_power_modem_queue_active(struct ipa *ipa) | |
296 | { | |
7aa0e8b8 | 297 | clear_bit(IPA_POWER_FLAG_STARTED, ipa->power->flags); |
b8e36e13 AE |
298 | } |
299 | ||
34a08176 AE |
300 | static int ipa_power_retention_init(struct ipa_power *power) |
301 | { | |
302 | struct qmp *qmp = qmp_get(power->dev); | |
303 | ||
304 | if (IS_ERR(qmp)) { | |
305 | if (PTR_ERR(qmp) == -EPROBE_DEFER) | |
306 | return -EPROBE_DEFER; | |
307 | ||
308 | /* We assume any other error means it's not defined/needed */ | |
309 | qmp = NULL; | |
310 | } | |
311 | power->qmp = qmp; | |
312 | ||
313 | return 0; | |
314 | } | |
315 | ||
316 | static void ipa_power_retention_exit(struct ipa_power *power) | |
317 | { | |
318 | qmp_put(power->qmp); | |
319 | power->qmp = NULL; | |
320 | } | |
321 | ||
322 | /* Control register retention on power collapse */ | |
323 | void ipa_power_retention(struct ipa *ipa, bool enable) | |
324 | { | |
325 | static const char fmt[] = "{ class: bcm, res: ipa_pc, val: %c }"; | |
326 | struct ipa_power *power = ipa->power; | |
34a08176 AE |
327 | int ret; |
328 | ||
329 | if (!power->qmp) | |
330 | return; /* Not needed on this platform */ | |
331 | ||
b4f63bbf | 332 | ret = qmp_send(power->qmp, fmt, enable ? '1' : '0'); |
34a08176 AE |
333 | if (ret) |
334 | dev_err(power->dev, "error %d sending QMP %sable request\n", | |
335 | ret, enable ? "en" : "dis"); | |
336 | } | |
337 | ||
d430fe4b | 338 | int ipa_power_setup(struct ipa *ipa) |
afe1baa8 | 339 | { |
d430fe4b AE |
340 | int ret; |
341 | ||
d50ed355 | 342 | ipa_interrupt_enable(ipa, IPA_IRQ_TX_SUSPEND); |
d430fe4b AE |
343 | |
344 | ret = device_init_wakeup(&ipa->pdev->dev, true); | |
8d8d3f1a | 345 | if (ret) |
d50ed355 | 346 | ipa_interrupt_disable(ipa, IPA_IRQ_TX_SUSPEND); |
d430fe4b AE |
347 | |
348 | return ret; | |
afe1baa8 AE |
349 | } |
350 | ||
351 | void ipa_power_teardown(struct ipa *ipa) | |
352 | { | |
d430fe4b | 353 | (void)device_init_wakeup(&ipa->pdev->dev, false); |
d50ed355 | 354 | ipa_interrupt_disable(ipa, IPA_IRQ_TX_SUSPEND); |
afe1baa8 AE |
355 | } |
356 | ||
7aa0e8b8 AE |
357 | /* Initialize IPA power management */ |
358 | struct ipa_power * | |
359 | ipa_power_init(struct device *dev, const struct ipa_power_data *data) | |
ba764c4d | 360 | { |
7aa0e8b8 | 361 | struct ipa_power *power; |
ba764c4d | 362 | struct clk *clk; |
8ee7ec48 | 363 | size_t size; |
ba764c4d AE |
364 | int ret; |
365 | ||
366 | clk = clk_get(dev, "core"); | |
367 | if (IS_ERR(clk)) { | |
4c7ccfcd AE |
368 | dev_err_probe(dev, PTR_ERR(clk), "error getting core clock\n"); |
369 | ||
ba764c4d AE |
370 | return ERR_CAST(clk); |
371 | } | |
372 | ||
91d02f95 | 373 | ret = clk_set_rate(clk, data->core_clock_rate); |
ba764c4d | 374 | if (ret) { |
91d02f95 AE |
375 | dev_err(dev, "error %d setting core clock rate to %u\n", |
376 | ret, data->core_clock_rate); | |
ba764c4d AE |
377 | goto err_clk_put; |
378 | } | |
379 | ||
cb631a63 AE |
380 | size = struct_size(power, interconnect, data->interconnect_count); |
381 | power = kzalloc(size, GFP_KERNEL); | |
7aa0e8b8 | 382 | if (!power) { |
ba764c4d AE |
383 | ret = -ENOMEM; |
384 | goto err_clk_put; | |
385 | } | |
7aa0e8b8 AE |
386 | power->dev = dev; |
387 | power->core = clk; | |
388 | spin_lock_init(&power->spinlock); | |
389 | power->interconnect_count = data->interconnect_count; | |
ba764c4d | 390 | |
37e0cf33 | 391 | ret = ipa_interconnect_init(power, data->interconnect_data); |
ba764c4d AE |
392 | if (ret) |
393 | goto err_kfree; | |
394 | ||
34a08176 AE |
395 | ret = ipa_power_retention_init(power); |
396 | if (ret) | |
397 | goto err_interconnect_exit; | |
398 | ||
1aac309d AE |
399 | pm_runtime_set_autosuspend_delay(dev, IPA_AUTOSUSPEND_DELAY); |
400 | pm_runtime_use_autosuspend(dev); | |
63de79f0 AE |
401 | pm_runtime_enable(dev); |
402 | ||
7aa0e8b8 | 403 | return power; |
ba764c4d | 404 | |
34a08176 AE |
405 | err_interconnect_exit: |
406 | ipa_interconnect_exit(power); | |
ba764c4d | 407 | err_kfree: |
7aa0e8b8 | 408 | kfree(power); |
ba764c4d AE |
409 | err_clk_put: |
410 | clk_put(clk); | |
411 | ||
412 | return ERR_PTR(ret); | |
413 | } | |
414 | ||
7aa0e8b8 AE |
415 | /* Inverse of ipa_power_init() */ |
416 | void ipa_power_exit(struct ipa_power *power) | |
ba764c4d | 417 | { |
7aa0e8b8 AE |
418 | struct device *dev = power->dev; |
419 | struct clk *clk = power->core; | |
ba764c4d | 420 | |
1aac309d AE |
421 | pm_runtime_disable(dev); |
422 | pm_runtime_dont_use_autosuspend(dev); | |
34a08176 | 423 | ipa_power_retention_exit(power); |
7aa0e8b8 AE |
424 | ipa_interconnect_exit(power); |
425 | kfree(power); | |
ba764c4d AE |
426 | clk_put(clk); |
427 | } | |
73ff316d | 428 | |
73ff316d | 429 | const struct dev_pm_ops ipa_pm_ops = { |
b9c532c1 AE |
430 | .suspend = ipa_suspend, |
431 | .resume = ipa_resume, | |
63de79f0 AE |
432 | .runtime_suspend = ipa_runtime_suspend, |
433 | .runtime_resume = ipa_runtime_resume, | |
73ff316d | 434 | }; |