Commit | Line | Data |
---|---|---|
cdf2e941 AE |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | ||
3 | /* Copyright (c) 2012-2018, The Linux Foundation. All rights reserved. | |
8e7c89d8 | 4 | * Copyright (C) 2018-2023 Linaro Ltd. |
cdf2e941 AE |
5 | */ |
6 | ||
7 | #include <linux/types.h> | |
8 | #include <linux/atomic.h> | |
9 | #include <linux/bitfield.h> | |
10 | #include <linux/device.h> | |
11 | #include <linux/bug.h> | |
12 | #include <linux/io.h> | |
13 | #include <linux/firmware.h> | |
14 | #include <linux/module.h> | |
15 | #include <linux/of.h> | |
cdf2e941 | 16 | #include <linux/of_address.h> |
3d40aed8 | 17 | #include <linux/platform_device.h> |
4c6a4da8 | 18 | #include <linux/pm_runtime.h> |
3bf90eca | 19 | #include <linux/firmware/qcom/qcom_scm.h> |
cdf2e941 AE |
20 | #include <linux/soc/qcom/mdt_loader.h> |
21 | ||
22 | #include "ipa.h" | |
2775cbc5 | 23 | #include "ipa_power.h" |
cdf2e941 AE |
24 | #include "ipa_data.h" |
25 | #include "ipa_endpoint.h" | |
ee3e6bea | 26 | #include "ipa_resource.h" |
cdf2e941 AE |
27 | #include "ipa_cmd.h" |
28 | #include "ipa_reg.h" | |
29 | #include "ipa_mem.h" | |
30 | #include "ipa_table.h" | |
76b5fbcd | 31 | #include "ipa_smp2p.h" |
cdf2e941 AE |
32 | #include "ipa_modem.h" |
33 | #include "ipa_uc.h" | |
34 | #include "ipa_interrupt.h" | |
35 | #include "gsi_trans.h" | |
2e3cf97f | 36 | #include "ipa_sysfs.h" |
cdf2e941 AE |
37 | |
38 | /** | |
39 | * DOC: The IP Accelerator | |
40 | * | |
41 | * This driver supports the Qualcomm IP Accelerator (IPA), which is a | |
42 | * networking component found in many Qualcomm SoCs. The IPA is connected | |
43 | * to the application processor (AP), but is also connected (and partially | |
44 | * controlled by) other "execution environments" (EEs), such as a modem. | |
45 | * | |
46 | * The IPA is the conduit between the AP and the modem that carries network | |
47 | * traffic. This driver presents a network interface representing the | |
48 | * connection of the modem to external (e.g. LTE) networks. | |
49 | * | |
50 | * The IPA provides protocol checksum calculation, offloading this work | |
51 | * from the AP. The IPA offers additional functionality, including routing, | |
52 | * filtering, and NAT support, but that more advanced functionality is not | |
53 | * currently supported. Despite that, some resources--including routing | |
54 | * tables and filter tables--are defined in this driver because they must | |
55 | * be initialized even when the advanced hardware features are not used. | |
56 | * | |
57 | * There are two distinct layers that implement the IPA hardware, and this | |
58 | * is reflected in the organization of the driver. The generic software | |
59 | * interface (GSI) is an integral component of the IPA, providing a | |
60 | * well-defined communication layer between the AP subsystem and the IPA | |
61 | * core. The GSI implements a set of "channels" used for communication | |
62 | * between the AP and the IPA. | |
63 | * | |
64 | * The IPA layer uses GSI channels to implement its "endpoints". And while | |
65 | * a GSI channel carries data between the AP and the IPA, a pair of IPA | |
66 | * endpoints is used to carry traffic between two EEs. Specifically, the main | |
67 | * modem network interface is implemented by two pairs of endpoints: a TX | |
68 | * endpoint on the AP coupled with an RX endpoint on the modem; and another | |
69 | * RX endpoint on the AP receiving data from a TX endpoint on the modem. | |
70 | */ | |
71 | ||
72 | /* The name of the GSI firmware file relative to /lib/firmware */ | |
9ce062ba | 73 | #define IPA_FW_PATH_DEFAULT "ipa_fws.mdt" |
cdf2e941 AE |
74 | #define IPA_PAS_ID 15 |
75 | ||
36426411 | 76 | /* Shift of 19.2 MHz timestamp to achieve lower resolution timestamps */ |
b00e190c | 77 | /* IPA v5.5+ does not specify Qtime timestamp config for DPL */ |
36426411 AE |
78 | #define DPL_TIMESTAMP_SHIFT 14 /* ~1.172 kHz, ~853 usec per tick */ |
79 | #define TAG_TIMESTAMP_SHIFT 14 | |
80 | #define NAT_TIMESTAMP_SHIFT 24 /* ~1.144 Hz, ~874 msec per tick */ | |
81 | ||
82 | /* Divider for 19.2 MHz crystal oscillator clock to get common timer clock */ | |
83 | #define IPA_XO_CLOCK_DIVIDER 192 /* 1 is subtracted where used */ | |
84 | ||
50f803d4 AE |
85 | /** |
86 | * enum ipa_firmware_loader: How GSI firmware gets loaded | |
87 | * | |
88 | * @IPA_LOADER_DEFER: System not ready; try again later | |
89 | * @IPA_LOADER_SELF: AP loads GSI firmware | |
90 | * @IPA_LOADER_MODEM: Modem loads GSI firmware, signals when done | |
7569805e | 91 | * @IPA_LOADER_SKIP: Neither AP nor modem need to load GSI firmware |
07f2f8e1 | 92 | * @IPA_LOADER_INVALID: GSI firmware loader specification is invalid |
50f803d4 AE |
93 | */ |
94 | enum ipa_firmware_loader { | |
95 | IPA_LOADER_DEFER, | |
96 | IPA_LOADER_SELF, | |
97 | IPA_LOADER_MODEM, | |
7569805e | 98 | IPA_LOADER_SKIP, |
07f2f8e1 | 99 | IPA_LOADER_INVALID, |
50f803d4 AE |
100 | }; |
101 | ||
cdf2e941 AE |
102 | /** |
103 | * ipa_setup() - Set up IPA hardware | |
104 | * @ipa: IPA pointer | |
105 | * | |
106 | * Perform initialization that requires issuing immediate commands on | |
107 | * the command TX endpoint. If the modem is doing GSI firmware load | |
108 | * and initialization, this function will be called when an SMP2P | |
109 | * interrupt has been signaled by the modem. Otherwise it will be | |
110 | * called from ipa_probe() after GSI firmware has been successfully | |
111 | * loaded, authenticated, and started by Trust Zone. | |
112 | */ | |
113 | int ipa_setup(struct ipa *ipa) | |
114 | { | |
115 | struct ipa_endpoint *exception_endpoint; | |
116 | struct ipa_endpoint *command_endpoint; | |
8529b4b0 | 117 | struct device *dev = &ipa->pdev->dev; |
cdf2e941 AE |
118 | int ret; |
119 | ||
d387c761 | 120 | ret = gsi_setup(&ipa->gsi); |
cdf2e941 AE |
121 | if (ret) |
122 | return ret; | |
123 | ||
d430fe4b | 124 | ret = ipa_power_setup(ipa); |
8529b4b0 | 125 | if (ret) |
afe1baa8 | 126 | goto err_gsi_teardown; |
8529b4b0 | 127 | |
cdf2e941 AE |
128 | ipa_endpoint_setup(ipa); |
129 | ||
130 | /* We need to use the AP command TX endpoint to perform other | |
131 | * initialization, so we enable first. | |
132 | */ | |
133 | command_endpoint = ipa->name_map[IPA_ENDPOINT_AP_COMMAND_TX]; | |
134 | ret = ipa_endpoint_enable_one(command_endpoint); | |
135 | if (ret) | |
136 | goto err_endpoint_teardown; | |
137 | ||
74858b63 | 138 | ret = ipa_mem_setup(ipa); /* No matching teardown required */ |
cdf2e941 AE |
139 | if (ret) |
140 | goto err_command_disable; | |
141 | ||
74858b63 | 142 | ret = ipa_table_setup(ipa); /* No matching teardown required */ |
cdf2e941 | 143 | if (ret) |
74858b63 | 144 | goto err_command_disable; |
cdf2e941 AE |
145 | |
146 | /* Enable the exception handling endpoint, and tell the hardware | |
147 | * to use it by default. | |
148 | */ | |
149 | exception_endpoint = ipa->name_map[IPA_ENDPOINT_AP_LAN_RX]; | |
150 | ret = ipa_endpoint_enable_one(exception_endpoint); | |
151 | if (ret) | |
74858b63 | 152 | goto err_command_disable; |
cdf2e941 AE |
153 | |
154 | ipa_endpoint_default_route_set(ipa, exception_endpoint->endpoint_id); | |
155 | ||
156 | /* We're all set. Now prepare for communication with the modem */ | |
63961f54 | 157 | ret = ipa_qmi_setup(ipa); |
cdf2e941 AE |
158 | if (ret) |
159 | goto err_default_route_clear; | |
160 | ||
161 | ipa->setup_complete = true; | |
162 | ||
8529b4b0 | 163 | dev_info(dev, "IPA driver setup completed successfully\n"); |
cdf2e941 AE |
164 | |
165 | return 0; | |
166 | ||
167 | err_default_route_clear: | |
168 | ipa_endpoint_default_route_clear(ipa); | |
169 | ipa_endpoint_disable_one(exception_endpoint); | |
cdf2e941 AE |
170 | err_command_disable: |
171 | ipa_endpoint_disable_one(command_endpoint); | |
172 | err_endpoint_teardown: | |
173 | ipa_endpoint_teardown(ipa); | |
afe1baa8 | 174 | ipa_power_teardown(ipa); |
afe1baa8 | 175 | err_gsi_teardown: |
cdf2e941 AE |
176 | gsi_teardown(&ipa->gsi); |
177 | ||
178 | return ret; | |
179 | } | |
180 | ||
181 | /** | |
182 | * ipa_teardown() - Inverse of ipa_setup() | |
183 | * @ipa: IPA pointer | |
184 | */ | |
185 | static void ipa_teardown(struct ipa *ipa) | |
186 | { | |
187 | struct ipa_endpoint *exception_endpoint; | |
188 | struct ipa_endpoint *command_endpoint; | |
189 | ||
2c257248 AE |
190 | /* We're going to tear everything down, as if setup never completed */ |
191 | ipa->setup_complete = false; | |
192 | ||
63961f54 | 193 | ipa_qmi_teardown(ipa); |
cdf2e941 AE |
194 | ipa_endpoint_default_route_clear(ipa); |
195 | exception_endpoint = ipa->name_map[IPA_ENDPOINT_AP_LAN_RX]; | |
196 | ipa_endpoint_disable_one(exception_endpoint); | |
cdf2e941 AE |
197 | command_endpoint = ipa->name_map[IPA_ENDPOINT_AP_COMMAND_TX]; |
198 | ipa_endpoint_disable_one(command_endpoint); | |
199 | ipa_endpoint_teardown(ipa); | |
afe1baa8 | 200 | ipa_power_teardown(ipa); |
cdf2e941 AE |
201 | gsi_teardown(&ipa->gsi); |
202 | } | |
203 | ||
92073b16 AE |
204 | static void |
205 | ipa_hardware_config_bcr(struct ipa *ipa, const struct ipa_data *data) | |
206 | { | |
81772e44 | 207 | const struct reg *reg; |
92073b16 AE |
208 | u32 val; |
209 | ||
210 | /* IPA v4.5+ has no backward compatibility register */ | |
211 | if (ipa->version >= IPA_VERSION_4_5) | |
212 | return; | |
213 | ||
6a244b75 | 214 | reg = ipa_reg(ipa, IPA_BCR); |
92073b16 | 215 | val = data->backward_compat; |
fc4cecf7 | 216 | iowrite32(val, ipa->reg_virt + reg_offset(reg)); |
92073b16 AE |
217 | } |
218 | ||
219 | static void ipa_hardware_config_tx(struct ipa *ipa) | |
220 | { | |
221 | enum ipa_version version = ipa->version; | |
81772e44 | 222 | const struct reg *reg; |
6bfb7538 | 223 | u32 offset; |
92073b16 AE |
224 | u32 val; |
225 | ||
226 | if (version <= IPA_VERSION_4_0 || version >= IPA_VERSION_4_5) | |
227 | return; | |
228 | ||
229 | /* Disable PA mask to allow HOLB drop */ | |
6a244b75 | 230 | reg = ipa_reg(ipa, IPA_TX_CFG); |
fc4cecf7 | 231 | offset = reg_offset(reg); |
6a244b75 | 232 | |
6bfb7538 | 233 | val = ioread32(ipa->reg_virt + offset); |
92073b16 | 234 | |
f1470fd7 | 235 | val &= ~reg_bit(reg, PA_MASK_EN); |
92073b16 | 236 | |
6bfb7538 | 237 | iowrite32(val, ipa->reg_virt + offset); |
92073b16 AE |
238 | } |
239 | ||
240 | static void ipa_hardware_config_clkon(struct ipa *ipa) | |
241 | { | |
242 | enum ipa_version version = ipa->version; | |
81772e44 | 243 | const struct reg *reg; |
92073b16 AE |
244 | u32 val; |
245 | ||
6a244b75 | 246 | if (version >= IPA_VERSION_4_5) |
92073b16 AE |
247 | return; |
248 | ||
6a244b75 AE |
249 | if (version < IPA_VERSION_4_0 && version != IPA_VERSION_3_1) |
250 | return; | |
6bfb7538 | 251 | |
6a244b75 AE |
252 | /* Implement some hardware workarounds */ |
253 | reg = ipa_reg(ipa, CLKON_CFG); | |
479deb32 AE |
254 | if (version == IPA_VERSION_3_1) { |
255 | /* Disable MISC clock gating */ | |
f1470fd7 | 256 | val = reg_bit(reg, CLKON_MISC); |
479deb32 AE |
257 | } else { /* IPA v4.0+ */ |
258 | /* Enable open global clocks in the CLKON configuration */ | |
f1470fd7 AE |
259 | val = reg_bit(reg, CLKON_GLOBAL); |
260 | val |= reg_bit(reg, GLOBAL_2X_CLK); | |
479deb32 | 261 | } |
92073b16 | 262 | |
fc4cecf7 | 263 | iowrite32(val, ipa->reg_virt + reg_offset(reg)); |
92073b16 AE |
264 | } |
265 | ||
cc5199ed | 266 | /* Configure bus access behavior for IPA components */ |
cdf2e941 AE |
267 | static void ipa_hardware_config_comp(struct ipa *ipa) |
268 | { | |
81772e44 | 269 | const struct reg *reg; |
6bfb7538 | 270 | u32 offset; |
cdf2e941 AE |
271 | u32 val; |
272 | ||
d7f3087b AE |
273 | /* Nothing to configure prior to IPA v4.0 */ |
274 | if (ipa->version < IPA_VERSION_4_0) | |
cdf2e941 AE |
275 | return; |
276 | ||
6a244b75 | 277 | reg = ipa_reg(ipa, COMP_CFG); |
fc4cecf7 AE |
278 | offset = reg_offset(reg); |
279 | ||
6bfb7538 | 280 | val = ioread32(ipa->reg_virt + offset); |
cdf2e941 AE |
281 | |
282 | if (ipa->version == IPA_VERSION_4_0) { | |
f1470fd7 AE |
283 | val &= ~reg_bit(reg, IPA_QMB_SELECT_CONS_EN); |
284 | val &= ~reg_bit(reg, IPA_QMB_SELECT_PROD_EN); | |
285 | val &= ~reg_bit(reg, IPA_QMB_SELECT_GLOBAL_EN); | |
8bfc4e21 | 286 | } else if (ipa->version < IPA_VERSION_4_5) { |
f1470fd7 | 287 | val |= reg_bit(reg, GSI_MULTI_AXI_MASTERS_DIS); |
8bfc4e21 | 288 | } else { |
cb7550b4 | 289 | /* For IPA v4.5+ FULL_FLUSH_WAIT_RS_CLOSURE_EN is 0 */ |
cdf2e941 AE |
290 | } |
291 | ||
f1470fd7 AE |
292 | val |= reg_bit(reg, GSI_MULTI_INORDER_RD_DIS); |
293 | val |= reg_bit(reg, GSI_MULTI_INORDER_WR_DIS); | |
cdf2e941 | 294 | |
6bfb7538 | 295 | iowrite32(val, ipa->reg_virt + offset); |
cdf2e941 AE |
296 | } |
297 | ||
8a81efac AE |
298 | /* Configure DDR and (possibly) PCIe max read/write QSB values */ |
299 | static void | |
300 | ipa_hardware_config_qsb(struct ipa *ipa, const struct ipa_data *data) | |
cdf2e941 | 301 | { |
8a81efac AE |
302 | const struct ipa_qsb_data *data0; |
303 | const struct ipa_qsb_data *data1; | |
81772e44 | 304 | const struct reg *reg; |
cdf2e941 AE |
305 | u32 val; |
306 | ||
8a81efac AE |
307 | /* QMB 0 represents DDR; QMB 1 (if present) represents PCIe */ |
308 | data0 = &data->qsb_data[IPA_QSB_MASTER_DDR]; | |
309 | if (data->qsb_count > 1) | |
310 | data1 = &data->qsb_data[IPA_QSB_MASTER_PCIE]; | |
311 | ||
312 | /* Max outstanding write accesses for QSB masters */ | |
6a244b75 AE |
313 | reg = ipa_reg(ipa, QSB_MAX_WRITES); |
314 | ||
f1470fd7 | 315 | val = reg_encode(reg, GEN_QMB_0_MAX_WRITES, data0->max_writes); |
8a81efac | 316 | if (data->qsb_count > 1) |
f1470fd7 | 317 | val |= reg_encode(reg, GEN_QMB_1_MAX_WRITES, data1->max_writes); |
6a244b75 | 318 | |
fc4cecf7 | 319 | iowrite32(val, ipa->reg_virt + reg_offset(reg)); |
cdf2e941 | 320 | |
8a81efac | 321 | /* Max outstanding read accesses for QSB masters */ |
6a244b75 AE |
322 | reg = ipa_reg(ipa, QSB_MAX_READS); |
323 | ||
f1470fd7 | 324 | val = reg_encode(reg, GEN_QMB_0_MAX_READS, data0->max_reads); |
b9aa0805 | 325 | if (ipa->version >= IPA_VERSION_4_0) |
f1470fd7 AE |
326 | val |= reg_encode(reg, GEN_QMB_0_MAX_READS_BEATS, |
327 | data0->max_reads_beats); | |
b9aa0805 | 328 | if (data->qsb_count > 1) { |
f1470fd7 | 329 | val = reg_encode(reg, GEN_QMB_1_MAX_READS, data1->max_reads); |
b9aa0805 | 330 | if (ipa->version >= IPA_VERSION_4_0) |
f1470fd7 AE |
331 | val |= reg_encode(reg, GEN_QMB_1_MAX_READS_BEATS, |
332 | data1->max_reads_beats); | |
b9aa0805 | 333 | } |
6a244b75 | 334 | |
fc4cecf7 | 335 | iowrite32(val, ipa->reg_virt + reg_offset(reg)); |
cdf2e941 AE |
336 | } |
337 | ||
1910494e AE |
338 | /* The internal inactivity timer clock is used for the aggregation timer */ |
339 | #define TIMER_FREQUENCY 32000 /* 32 KHz inactivity timer clock */ | |
340 | ||
341 | /* Compute the value to use in the COUNTER_CFG register AGGR_GRANULARITY | |
342 | * field to represent the given number of microseconds. The value is one | |
343 | * less than the number of timer ticks in the requested period. 0 is not | |
676eec8e AE |
344 | * a valid granularity value (so for example @usec must be at least 16 for |
345 | * a TIMER_FREQUENCY of 32000). | |
1910494e | 346 | */ |
676eec8e | 347 | static __always_inline u32 ipa_aggr_granularity_val(u32 usec) |
1910494e | 348 | { |
1910494e AE |
349 | return DIV_ROUND_CLOSEST(usec * TIMER_FREQUENCY, USEC_PER_SEC) - 1; |
350 | } | |
351 | ||
36426411 AE |
352 | /* IPA uses unified Qtime starting at IPA v4.5, implementing various |
353 | * timestamps and timers independent of the IPA core clock rate. The | |
354 | * Qtimer is based on a 56-bit timestamp incremented at each tick of | |
355 | * a 19.2 MHz SoC crystal oscillator (XO clock). | |
356 | * | |
357 | * For IPA timestamps (tag, NAT, data path logging) a lower resolution | |
358 | * timestamp is achieved by shifting the Qtimer timestamp value right | |
359 | * some number of bits to produce the low-order bits of the coarser | |
360 | * granularity timestamp. | |
361 | * | |
362 | * For timers, a common timer clock is derived from the XO clock using | |
363 | * a divider (we use 192, to produce a 100kHz timer clock). From | |
364 | * this common clock, three "pulse generators" are used to produce | |
365 | * timer ticks at a configurable frequency. IPA timers (such as | |
366 | * those used for aggregation or head-of-line block handling) now | |
367 | * define their period based on one of these pulse generators. | |
368 | */ | |
369 | static void ipa_qtime_config(struct ipa *ipa) | |
370 | { | |
81772e44 | 371 | const struct reg *reg; |
6bfb7538 | 372 | u32 offset; |
36426411 AE |
373 | u32 val; |
374 | ||
375 | /* Timer clock divider must be disabled when we change the rate */ | |
6a244b75 | 376 | reg = ipa_reg(ipa, TIMERS_XO_CLK_DIV_CFG); |
fc4cecf7 | 377 | iowrite32(0, ipa->reg_virt + reg_offset(reg)); |
36426411 | 378 | |
6a244b75 | 379 | reg = ipa_reg(ipa, QTIME_TIMESTAMP_CFG); |
b00e190c AE |
380 | if (ipa->version < IPA_VERSION_5_5) { |
381 | /* Set DPL time stamp resolution to use Qtime (not 1 msec) */ | |
382 | val = reg_encode(reg, DPL_TIMESTAMP_LSB, DPL_TIMESTAMP_SHIFT); | |
383 | val |= reg_bit(reg, DPL_TIMESTAMP_SEL); | |
384 | } | |
36426411 | 385 | /* Configure tag and NAT Qtime timestamp resolution as well */ |
f1470fd7 AE |
386 | val = reg_encode(reg, TAG_TIMESTAMP_LSB, TAG_TIMESTAMP_SHIFT); |
387 | val = reg_encode(reg, NAT_TIMESTAMP_LSB, NAT_TIMESTAMP_SHIFT); | |
6bfb7538 | 388 | |
fc4cecf7 | 389 | iowrite32(val, ipa->reg_virt + reg_offset(reg)); |
36426411 AE |
390 | |
391 | /* Set granularity of pulse generators used for other timers */ | |
6a244b75 | 392 | reg = ipa_reg(ipa, TIMERS_PULSE_GRAN_CFG); |
f1470fd7 AE |
393 | val = reg_encode(reg, PULSE_GRAN_0, IPA_GRAN_100_US); |
394 | val |= reg_encode(reg, PULSE_GRAN_1, IPA_GRAN_1_MS); | |
2cdbcbfd | 395 | if (ipa->version >= IPA_VERSION_5_0) { |
f1470fd7 AE |
396 | val |= reg_encode(reg, PULSE_GRAN_2, IPA_GRAN_10_MS); |
397 | val |= reg_encode(reg, PULSE_GRAN_3, IPA_GRAN_10_MS); | |
2cdbcbfd | 398 | } else { |
f1470fd7 | 399 | val |= reg_encode(reg, PULSE_GRAN_2, IPA_GRAN_1_MS); |
2cdbcbfd | 400 | } |
6bfb7538 | 401 | |
fc4cecf7 | 402 | iowrite32(val, ipa->reg_virt + reg_offset(reg)); |
36426411 AE |
403 | |
404 | /* Actual divider is 1 more than value supplied here */ | |
6a244b75 | 405 | reg = ipa_reg(ipa, TIMERS_XO_CLK_DIV_CFG); |
fc4cecf7 AE |
406 | offset = reg_offset(reg); |
407 | ||
f1470fd7 | 408 | val = reg_encode(reg, DIV_VALUE, IPA_XO_CLOCK_DIVIDER - 1); |
6bfb7538 | 409 | |
6bfb7538 | 410 | iowrite32(val, ipa->reg_virt + offset); |
36426411 AE |
411 | |
412 | /* Divider value is set; re-enable the common timer clock divider */ | |
f1470fd7 | 413 | val |= reg_bit(reg, DIV_ENABLE); |
6bfb7538 AE |
414 | |
415 | iowrite32(val, ipa->reg_virt + offset); | |
36426411 AE |
416 | } |
417 | ||
1e5db096 AE |
418 | /* Before IPA v4.5 timing is controlled by a counter register */ |
419 | static void ipa_hardware_config_counter(struct ipa *ipa) | |
420 | { | |
6a244b75 | 421 | u32 granularity = ipa_aggr_granularity_val(IPA_AGGR_GRANULARITY); |
81772e44 | 422 | const struct reg *reg; |
1e5db096 AE |
423 | u32 val; |
424 | ||
6a244b75 | 425 | reg = ipa_reg(ipa, COUNTER_CFG); |
b5c35fa4 | 426 | /* If defined, EOT_COAL_GRANULARITY is 0 */ |
f1470fd7 | 427 | val = reg_encode(reg, AGGR_GRANULARITY, granularity); |
fc4cecf7 | 428 | iowrite32(val, ipa->reg_virt + reg_offset(reg)); |
1e5db096 AE |
429 | } |
430 | ||
431 | static void ipa_hardware_config_timing(struct ipa *ipa) | |
432 | { | |
433 | if (ipa->version < IPA_VERSION_4_5) | |
434 | ipa_hardware_config_counter(ipa); | |
435 | else | |
436 | ipa_qtime_config(ipa); | |
437 | } | |
438 | ||
b24627b1 AE |
439 | static void ipa_hardware_config_hashing(struct ipa *ipa) |
440 | { | |
81772e44 | 441 | const struct reg *reg; |
b24627b1 | 442 | |
8e7c89d8 AE |
443 | /* Other than IPA v4.2, all versions enable "hashing". Starting |
444 | * with IPA v5.0, the filter and router tables are implemented | |
445 | * differently, but the default configuration enables this feature | |
446 | * (now referred to as "cacheing"), so there's nothing to do here. | |
447 | */ | |
b24627b1 AE |
448 | if (ipa->version != IPA_VERSION_4_2) |
449 | return; | |
450 | ||
451 | /* IPA v4.2 does not support hashed tables, so disable them */ | |
6a244b75 | 452 | reg = ipa_reg(ipa, FILT_ROUT_HASH_EN); |
62b9c009 AE |
453 | |
454 | /* IPV6_ROUTER_HASH, IPV6_FILTER_HASH, IPV4_ROUTER_HASH, | |
455 | * IPV4_FILTER_HASH are all zero. | |
456 | */ | |
fc4cecf7 | 457 | iowrite32(0, ipa->reg_virt + reg_offset(reg)); |
b24627b1 AE |
458 | } |
459 | ||
cdf2e941 AE |
460 | static void ipa_idle_indication_cfg(struct ipa *ipa, |
461 | u32 enter_idle_debounce_thresh, | |
462 | bool const_non_idle_enable) | |
463 | { | |
81772e44 | 464 | const struct reg *reg; |
cdf2e941 AE |
465 | u32 val; |
466 | ||
95a0396a CC |
467 | if (ipa->version < IPA_VERSION_3_5_1) |
468 | return; | |
469 | ||
6a244b75 | 470 | reg = ipa_reg(ipa, IDLE_INDICATION_CFG); |
f1470fd7 AE |
471 | val = reg_encode(reg, ENTER_IDLE_DEBOUNCE_THRESH, |
472 | enter_idle_debounce_thresh); | |
cdf2e941 | 473 | if (const_non_idle_enable) |
f1470fd7 | 474 | val |= reg_bit(reg, CONST_NON_IDLE_ENABLE); |
cdf2e941 | 475 | |
fc4cecf7 | 476 | iowrite32(val, ipa->reg_virt + reg_offset(reg)); |
cdf2e941 AE |
477 | } |
478 | ||
479 | /** | |
480 | * ipa_hardware_dcd_config() - Enable dynamic clock division on IPA | |
e3eea08e | 481 | * @ipa: IPA pointer |
cdf2e941 AE |
482 | * |
483 | * Configures when the IPA signals it is idle to the global clock | |
7aa0e8b8 AE |
484 | * controller, which can respond by scaling down the clock to save |
485 | * power. | |
cdf2e941 AE |
486 | */ |
487 | static void ipa_hardware_dcd_config(struct ipa *ipa) | |
488 | { | |
8bfc4e21 | 489 | /* Recommended values for IPA 3.5 and later according to IPA HPG */ |
cdf2e941 AE |
490 | ipa_idle_indication_cfg(ipa, 256, false); |
491 | } | |
492 | ||
493 | static void ipa_hardware_dcd_deconfig(struct ipa *ipa) | |
494 | { | |
495 | /* Power-on reset values */ | |
496 | ipa_idle_indication_cfg(ipa, 0, true); | |
497 | } | |
498 | ||
499 | /** | |
500 | * ipa_hardware_config() - Primitive hardware initialization | |
501 | * @ipa: IPA pointer | |
8a81efac | 502 | * @data: IPA configuration data |
cdf2e941 | 503 | */ |
8a81efac | 504 | static void ipa_hardware_config(struct ipa *ipa, const struct ipa_data *data) |
cdf2e941 | 505 | { |
92073b16 AE |
506 | ipa_hardware_config_bcr(ipa, data); |
507 | ipa_hardware_config_tx(ipa); | |
508 | ipa_hardware_config_clkon(ipa); | |
cdf2e941 | 509 | ipa_hardware_config_comp(ipa); |
8a81efac | 510 | ipa_hardware_config_qsb(ipa, data); |
1e5db096 | 511 | ipa_hardware_config_timing(ipa); |
b24627b1 | 512 | ipa_hardware_config_hashing(ipa); |
cdf2e941 AE |
513 | ipa_hardware_dcd_config(ipa); |
514 | } | |
515 | ||
516 | /** | |
517 | * ipa_hardware_deconfig() - Inverse of ipa_hardware_config() | |
518 | * @ipa: IPA pointer | |
519 | * | |
520 | * This restores the power-on reset values (even if they aren't different) | |
521 | */ | |
522 | static void ipa_hardware_deconfig(struct ipa *ipa) | |
523 | { | |
524 | /* Mostly we just leave things as we set them. */ | |
525 | ipa_hardware_dcd_deconfig(ipa); | |
526 | } | |
527 | ||
cdf2e941 AE |
528 | /** |
529 | * ipa_config() - Configure IPA hardware | |
530 | * @ipa: IPA pointer | |
e3eea08e | 531 | * @data: IPA configuration data |
cdf2e941 | 532 | * |
7aa0e8b8 | 533 | * Perform initialization requiring IPA power to be enabled. |
cdf2e941 AE |
534 | */ |
535 | static int ipa_config(struct ipa *ipa, const struct ipa_data *data) | |
536 | { | |
537 | int ret; | |
538 | ||
8a81efac | 539 | ipa_hardware_config(ipa, data); |
cdf2e941 | 540 | |
07e1f689 | 541 | ret = ipa_mem_config(ipa); |
cdf2e941 AE |
542 | if (ret) |
543 | goto err_hardware_deconfig; | |
544 | ||
1118a147 AE |
545 | ipa->interrupt = ipa_interrupt_config(ipa); |
546 | if (IS_ERR(ipa->interrupt)) { | |
547 | ret = PTR_ERR(ipa->interrupt); | |
548 | ipa->interrupt = NULL; | |
549 | goto err_mem_deconfig; | |
550 | } | |
551 | ||
dc8f7e39 AE |
552 | ipa_uc_config(ipa); |
553 | ||
07e1f689 | 554 | ret = ipa_endpoint_config(ipa); |
cdf2e941 | 555 | if (ret) |
afe1baa8 | 556 | goto err_uc_deconfig; |
cdf2e941 | 557 | |
74858b63 | 558 | ipa_table_config(ipa); /* No deconfig required */ |
cdf2e941 | 559 | |
74858b63 | 560 | /* Assign resource limitation to each group; no deconfig required */ |
cdf2e941 AE |
561 | ret = ipa_resource_config(ipa, data->resource_data); |
562 | if (ret) | |
07e1f689 | 563 | goto err_endpoint_deconfig; |
cdf2e941 AE |
564 | |
565 | ret = ipa_modem_config(ipa); | |
566 | if (ret) | |
07e1f689 | 567 | goto err_endpoint_deconfig; |
cdf2e941 AE |
568 | |
569 | return 0; | |
570 | ||
cdf2e941 AE |
571 | err_endpoint_deconfig: |
572 | ipa_endpoint_deconfig(ipa); | |
afe1baa8 | 573 | err_uc_deconfig: |
dc8f7e39 | 574 | ipa_uc_deconfig(ipa); |
1118a147 AE |
575 | ipa_interrupt_deconfig(ipa->interrupt); |
576 | ipa->interrupt = NULL; | |
07e1f689 AE |
577 | err_mem_deconfig: |
578 | ipa_mem_deconfig(ipa); | |
cdf2e941 AE |
579 | err_hardware_deconfig: |
580 | ipa_hardware_deconfig(ipa); | |
cdf2e941 AE |
581 | |
582 | return ret; | |
583 | } | |
584 | ||
585 | /** | |
586 | * ipa_deconfig() - Inverse of ipa_config() | |
587 | * @ipa: IPA pointer | |
588 | */ | |
589 | static void ipa_deconfig(struct ipa *ipa) | |
590 | { | |
591 | ipa_modem_deconfig(ipa); | |
cdf2e941 | 592 | ipa_endpoint_deconfig(ipa); |
dc8f7e39 | 593 | ipa_uc_deconfig(ipa); |
1118a147 AE |
594 | ipa_interrupt_deconfig(ipa->interrupt); |
595 | ipa->interrupt = NULL; | |
07e1f689 | 596 | ipa_mem_deconfig(ipa); |
cdf2e941 | 597 | ipa_hardware_deconfig(ipa); |
cdf2e941 AE |
598 | } |
599 | ||
600 | static int ipa_firmware_load(struct device *dev) | |
601 | { | |
602 | const struct firmware *fw; | |
603 | struct device_node *node; | |
604 | struct resource res; | |
605 | phys_addr_t phys; | |
9ce062ba | 606 | const char *path; |
cdf2e941 AE |
607 | ssize_t size; |
608 | void *virt; | |
609 | int ret; | |
610 | ||
611 | node = of_parse_phandle(dev->of_node, "memory-region", 0); | |
612 | if (!node) { | |
613 | dev_err(dev, "DT error getting \"memory-region\" property\n"); | |
614 | return -EINVAL; | |
615 | } | |
616 | ||
617 | ret = of_address_to_resource(node, 0, &res); | |
b244163f | 618 | of_node_put(node); |
cdf2e941 AE |
619 | if (ret) { |
620 | dev_err(dev, "error %d getting \"memory-region\" resource\n", | |
621 | ret); | |
622 | return ret; | |
623 | } | |
624 | ||
9ce062ba AE |
625 | /* Use name from DTB if specified; use default for *any* error */ |
626 | ret = of_property_read_string(dev->of_node, "firmware-name", &path); | |
cdf2e941 | 627 | if (ret) { |
9ce062ba AE |
628 | dev_dbg(dev, "error %d getting \"firmware-name\" resource\n", |
629 | ret); | |
630 | path = IPA_FW_PATH_DEFAULT; | |
631 | } | |
632 | ||
633 | ret = request_firmware(&fw, path, dev); | |
634 | if (ret) { | |
635 | dev_err(dev, "error %d requesting \"%s\"\n", ret, path); | |
cdf2e941 AE |
636 | return ret; |
637 | } | |
638 | ||
639 | phys = res.start; | |
640 | size = (size_t)resource_size(&res); | |
641 | virt = memremap(phys, size, MEMREMAP_WC); | |
642 | if (!virt) { | |
643 | dev_err(dev, "unable to remap firmware memory\n"); | |
644 | ret = -ENOMEM; | |
645 | goto out_release_firmware; | |
646 | } | |
647 | ||
9ce062ba | 648 | ret = qcom_mdt_load(dev, fw, path, IPA_PAS_ID, virt, phys, size, NULL); |
cdf2e941 | 649 | if (ret) |
9ce062ba | 650 | dev_err(dev, "error %d loading \"%s\"\n", ret, path); |
cdf2e941 | 651 | else if ((ret = qcom_scm_pas_auth_and_reset(IPA_PAS_ID))) |
9ce062ba | 652 | dev_err(dev, "error %d authenticating \"%s\"\n", ret, path); |
cdf2e941 AE |
653 | |
654 | memunmap(virt); | |
655 | out_release_firmware: | |
656 | release_firmware(fw); | |
657 | ||
658 | return ret; | |
659 | } | |
660 | ||
661 | static const struct of_device_id ipa_match[] = { | |
1bb1a117 AE |
662 | { |
663 | .compatible = "qcom,msm8998-ipa", | |
664 | .data = &ipa_data_v3_1, | |
665 | }, | |
cdf2e941 AE |
666 | { |
667 | .compatible = "qcom,sdm845-ipa", | |
fc566dab | 668 | .data = &ipa_data_v3_5_1, |
cdf2e941 AE |
669 | }, |
670 | { | |
671 | .compatible = "qcom,sc7180-ipa", | |
782d767a | 672 | .data = &ipa_data_v4_2, |
cdf2e941 | 673 | }, |
fbb763e7 AE |
674 | { |
675 | .compatible = "qcom,sdx55-ipa", | |
676 | .data = &ipa_data_v4_5, | |
677 | }, | |
b310de78 AE |
678 | { |
679 | .compatible = "qcom,sm6350-ipa", | |
680 | .data = &ipa_data_v4_7, | |
681 | }, | |
e557dc82 AE |
682 | { |
683 | .compatible = "qcom,sm8350-ipa", | |
684 | .data = &ipa_data_v4_9, | |
685 | }, | |
927c5043 AE |
686 | { |
687 | .compatible = "qcom,sc7280-ipa", | |
688 | .data = &ipa_data_v4_11, | |
689 | }, | |
cb7550b4 AE |
690 | { |
691 | .compatible = "qcom,sdx65-ipa", | |
692 | .data = &ipa_data_v5_0, | |
693 | }, | |
7c592940 AE |
694 | { |
695 | .compatible = "qcom,sm8550-ipa", | |
696 | .data = &ipa_data_v5_5, | |
697 | }, | |
cdf2e941 AE |
698 | { }, |
699 | }; | |
700 | MODULE_DEVICE_TABLE(of, ipa_match); | |
701 | ||
cdf2e941 AE |
702 | /* Check things that can be validated at build time. This just |
703 | * groups these things BUILD_BUG_ON() calls don't clutter the rest | |
704 | * of the code. | |
705 | * */ | |
706 | static void ipa_validate_build(void) | |
707 | { | |
99e75a37 AE |
708 | /* At one time we assumed a 64-bit build, allowing some do_div() |
709 | * calls to be replaced by simple division or modulo operations. | |
710 | * We currently only perform divide and modulo operations on u32, | |
711 | * u16, or size_t objects, and of those only size_t has any chance | |
712 | * of being a 64-bit value. (It should be guaranteed 32 bits wide | |
713 | * on a 32-bit build, but there is no harm in verifying that.) | |
714 | */ | |
715 | BUILD_BUG_ON(!IS_ENABLED(CONFIG_64BIT) && sizeof(size_t) != 4); | |
cdf2e941 AE |
716 | |
717 | /* Code assumes the EE ID for the AP is 0 (zeroed structure field) */ | |
718 | BUILD_BUG_ON(GSI_EE_AP != 0); | |
719 | ||
720 | /* There's no point if we have no channels or event rings */ | |
721 | BUILD_BUG_ON(!GSI_CHANNEL_COUNT_MAX); | |
722 | BUILD_BUG_ON(!GSI_EVT_RING_COUNT_MAX); | |
723 | ||
724 | /* GSI hardware design limits */ | |
725 | BUILD_BUG_ON(GSI_CHANNEL_COUNT_MAX > 32); | |
726 | BUILD_BUG_ON(GSI_EVT_RING_COUNT_MAX > 31); | |
727 | ||
728 | /* The number of TREs in a transaction is limited by the channel's | |
729 | * TLV FIFO size. A transaction structure uses 8-bit fields | |
730 | * to represents the number of TREs it has allocated and used. | |
731 | */ | |
732 | BUILD_BUG_ON(GSI_TLV_MAX > U8_MAX); | |
733 | ||
cdf2e941 AE |
734 | /* This is used as a divisor */ |
735 | BUILD_BUG_ON(!IPA_AGGR_GRANULARITY); | |
317a5740 AE |
736 | |
737 | /* Aggregation granularity value can't be 0, and must fit */ | |
738 | BUILD_BUG_ON(!ipa_aggr_granularity_val(IPA_AGGR_GRANULARITY)); | |
cdf2e941 AE |
739 | } |
740 | ||
50f803d4 AE |
741 | static enum ipa_firmware_loader ipa_firmware_loader(struct device *dev) |
742 | { | |
07f2f8e1 AE |
743 | bool modem_init; |
744 | const char *str; | |
745 | int ret; | |
746 | ||
747 | /* Look up the old and new properties by name */ | |
748 | modem_init = of_property_read_bool(dev->of_node, "modem-init"); | |
749 | ret = of_property_read_string(dev->of_node, "qcom,gsi-loader", &str); | |
750 | ||
751 | /* If the new property doesn't exist, it's legacy behavior */ | |
752 | if (ret == -EINVAL) { | |
753 | if (modem_init) | |
754 | return IPA_LOADER_MODEM; | |
755 | goto out_self; | |
756 | } | |
757 | ||
758 | /* Any other error on the new property means it's poorly defined */ | |
759 | if (ret) | |
760 | return IPA_LOADER_INVALID; | |
761 | ||
762 | /* New property value exists; if old one does too, that's invalid */ | |
763 | if (modem_init) | |
764 | return IPA_LOADER_INVALID; | |
765 | ||
766 | /* Modem loads GSI firmware for "modem" */ | |
767 | if (!strcmp(str, "modem")) | |
50f803d4 AE |
768 | return IPA_LOADER_MODEM; |
769 | ||
7569805e AE |
770 | /* No GSI firmware load is needed for "skip" */ |
771 | if (!strcmp(str, "skip")) | |
772 | return IPA_LOADER_SKIP; | |
773 | ||
07f2f8e1 AE |
774 | /* Any value other than "self" is an error */ |
775 | if (strcmp(str, "self")) | |
776 | return IPA_LOADER_INVALID; | |
777 | out_self: | |
50f803d4 AE |
778 | /* We need Trust Zone to load firmware; make sure it's available */ |
779 | if (qcom_scm_is_available()) | |
780 | return IPA_LOADER_SELF; | |
781 | ||
782 | return IPA_LOADER_DEFER; | |
783 | } | |
784 | ||
cdf2e941 AE |
785 | /** |
786 | * ipa_probe() - IPA platform driver probe function | |
787 | * @pdev: Platform device pointer | |
788 | * | |
e3eea08e | 789 | * Return: 0 if successful, or a negative error code (possibly |
cdf2e941 AE |
790 | * EPROBE_DEFER) |
791 | * | |
792 | * This is the main entry point for the IPA driver. Initialization proceeds | |
793 | * in several stages: | |
794 | * - The "init" stage involves activities that can be initialized without | |
795 | * access to the IPA hardware. | |
7aa0e8b8 | 796 | * - The "config" stage requires IPA power to be active so IPA registers |
cdf2e941 AE |
797 | * can be accessed, but does not require the use of IPA immediate commands. |
798 | * - The "setup" stage uses IPA immediate commands, and so requires the GSI | |
799 | * layer to be initialized. | |
800 | * | |
801 | * A Boolean Device Tree "modem-init" property determines whether GSI | |
802 | * initialization will be performed by the AP (Trust Zone) or the modem. | |
803 | * If the AP does GSI initialization, the setup phase is entered after | |
804 | * this has completed successfully. Otherwise the modem initializes | |
805 | * the GSI layer and signals it has finished by sending an SMP2P interrupt | |
806 | * to the AP; this triggers the start if IPA setup. | |
807 | */ | |
808 | static int ipa_probe(struct platform_device *pdev) | |
809 | { | |
cdf2e941 | 810 | struct device *dev = &pdev->dev; |
50f803d4 | 811 | enum ipa_firmware_loader loader; |
cdf2e941 | 812 | const struct ipa_data *data; |
7aa0e8b8 | 813 | struct ipa_power *power; |
cdf2e941 | 814 | struct ipa *ipa; |
cdf2e941 AE |
815 | int ret; |
816 | ||
817 | ipa_validate_build(); | |
818 | ||
7aa0e8b8 | 819 | /* Get configuration data early; needed for power initialization */ |
dfccb8b1 AE |
820 | data = of_device_get_match_data(dev); |
821 | if (!data) { | |
dfccb8b1 AE |
822 | dev_err(dev, "matched hardware not supported\n"); |
823 | return -ENODEV; | |
824 | } | |
825 | ||
8b3cb084 AE |
826 | if (!ipa_version_supported(data->version)) { |
827 | dev_err(dev, "unsupported IPA version %u\n", data->version); | |
e22e8e2f AE |
828 | return -EINVAL; |
829 | } | |
830 | ||
8defab8b AE |
831 | if (!data->modem_route_count) { |
832 | dev_err(dev, "modem_route_count cannot be zero\n"); | |
833 | return -EINVAL; | |
834 | } | |
835 | ||
50f803d4 | 836 | loader = ipa_firmware_loader(dev); |
07f2f8e1 AE |
837 | if (loader == IPA_LOADER_INVALID) |
838 | return -EINVAL; | |
50f803d4 AE |
839 | if (loader == IPA_LOADER_DEFER) |
840 | return -EPROBE_DEFER; | |
cdf2e941 | 841 | |
cdf2e941 AE |
842 | /* The clock and interconnects might not be ready when we're |
843 | * probed, so might return -EPROBE_DEFER. | |
844 | */ | |
7aa0e8b8 AE |
845 | power = ipa_power_init(dev, data->power_data); |
846 | if (IS_ERR(power)) | |
847 | return PTR_ERR(power); | |
cdf2e941 | 848 | |
dfccb8b1 | 849 | /* No more EPROBE_DEFER. Allocate and initialize the IPA structure */ |
cdf2e941 AE |
850 | ipa = kzalloc(sizeof(*ipa), GFP_KERNEL); |
851 | if (!ipa) { | |
852 | ret = -ENOMEM; | |
7aa0e8b8 | 853 | goto err_power_exit; |
cdf2e941 AE |
854 | } |
855 | ||
856 | ipa->pdev = pdev; | |
857 | dev_set_drvdata(dev, ipa); | |
7aa0e8b8 | 858 | ipa->power = power; |
cdf2e941 | 859 | ipa->version = data->version; |
8defab8b | 860 | ipa->modem_route_count = data->modem_route_count; |
51c48ce2 | 861 | init_completion(&ipa->completion); |
cdf2e941 AE |
862 | |
863 | ret = ipa_reg_init(ipa); | |
864 | if (ret) | |
865 | goto err_kfree_ipa; | |
866 | ||
3128aae8 | 867 | ret = ipa_mem_init(ipa, data->mem_data); |
cdf2e941 AE |
868 | if (ret) |
869 | goto err_reg_exit; | |
870 | ||
1d0c09de AE |
871 | ret = gsi_init(&ipa->gsi, pdev, ipa->version, data->endpoint_count, |
872 | data->endpoint_data); | |
cdf2e941 AE |
873 | if (ret) |
874 | goto err_mem_exit; | |
875 | ||
49e3aeeb | 876 | /* Result is a non-zero mask of endpoints that support filtering */ |
0f97fbd4 AE |
877 | ret = ipa_endpoint_init(ipa, data->endpoint_count, data->endpoint_data); |
878 | if (ret) | |
cdf2e941 | 879 | goto err_gsi_exit; |
cdf2e941 AE |
880 | |
881 | ret = ipa_table_init(ipa); | |
882 | if (ret) | |
883 | goto err_endpoint_exit; | |
884 | ||
50f803d4 | 885 | ret = ipa_smp2p_init(ipa, loader == IPA_LOADER_MODEM); |
cdf2e941 AE |
886 | if (ret) |
887 | goto err_table_exit; | |
888 | ||
7aa0e8b8 | 889 | /* Power needs to be active for config and setup */ |
4c6a4da8 | 890 | ret = pm_runtime_get_sync(dev); |
7ebd168c | 891 | if (WARN_ON(ret < 0)) |
4c6a4da8 | 892 | goto err_power_put; |
923a6b69 | 893 | |
cdf2e941 AE |
894 | ret = ipa_config(ipa, data); |
895 | if (ret) | |
4c6a4da8 | 896 | goto err_power_put; |
cdf2e941 AE |
897 | |
898 | dev_info(dev, "IPA driver initialized"); | |
899 | ||
07f2f8e1 AE |
900 | /* If the modem is loading GSI firmware, it will trigger a call to |
901 | * ipa_setup() when it has finished. In that case we're done here. | |
cdf2e941 | 902 | */ |
50f803d4 | 903 | if (loader == IPA_LOADER_MODEM) |
7ebd168c | 904 | goto done; |
cdf2e941 | 905 | |
7569805e AE |
906 | if (loader == IPA_LOADER_SELF) { |
907 | /* The AP is loading GSI firmware; do so now */ | |
908 | ret = ipa_firmware_load(dev); | |
909 | if (ret) | |
910 | goto err_deconfig; | |
911 | } /* Otherwise loader == IPA_LOADER_SKIP */ | |
cdf2e941 | 912 | |
07f2f8e1 | 913 | /* GSI firmware is loaded; proceed to setup */ |
cdf2e941 AE |
914 | ret = ipa_setup(ipa); |
915 | if (ret) | |
916 | goto err_deconfig; | |
7ebd168c | 917 | done: |
1aac309d AE |
918 | pm_runtime_mark_last_busy(dev); |
919 | (void)pm_runtime_put_autosuspend(dev); | |
923a6b69 | 920 | |
cdf2e941 AE |
921 | return 0; |
922 | ||
923 | err_deconfig: | |
924 | ipa_deconfig(ipa); | |
4c6a4da8 | 925 | err_power_put: |
1aac309d | 926 | pm_runtime_put_noidle(dev); |
76b5fbcd | 927 | ipa_smp2p_exit(ipa); |
cdf2e941 AE |
928 | err_table_exit: |
929 | ipa_table_exit(ipa); | |
930 | err_endpoint_exit: | |
931 | ipa_endpoint_exit(ipa); | |
932 | err_gsi_exit: | |
933 | gsi_exit(&ipa->gsi); | |
934 | err_mem_exit: | |
935 | ipa_mem_exit(ipa); | |
936 | err_reg_exit: | |
937 | ipa_reg_exit(ipa); | |
938 | err_kfree_ipa: | |
939 | kfree(ipa); | |
7aa0e8b8 AE |
940 | err_power_exit: |
941 | ipa_power_exit(power); | |
cdf2e941 AE |
942 | |
943 | return ret; | |
944 | } | |
945 | ||
a92dbb9c | 946 | static void ipa_remove(struct platform_device *pdev) |
cdf2e941 AE |
947 | { |
948 | struct ipa *ipa = dev_get_drvdata(&pdev->dev); | |
7aa0e8b8 | 949 | struct ipa_power *power = ipa->power; |
1aac309d | 950 | struct device *dev = &pdev->dev; |
cdf2e941 AE |
951 | int ret; |
952 | ||
8afc7e47 AE |
953 | /* Prevent the modem from triggering a call to ipa_setup(). This |
954 | * also ensures a modem-initiated setup that's underway completes. | |
955 | */ | |
956 | ipa_smp2p_irq_disable_setup(ipa); | |
957 | ||
1aac309d | 958 | ret = pm_runtime_get_sync(dev); |
7ebd168c | 959 | if (WARN_ON(ret < 0)) |
4c6a4da8 | 960 | goto out_power_put; |
923a6b69 | 961 | |
cdf2e941 AE |
962 | if (ipa->setup_complete) { |
963 | ret = ipa_modem_stop(ipa); | |
7c80e838 AE |
964 | /* If starting or stopping is in progress, try once more */ |
965 | if (ret == -EBUSY) { | |
966 | usleep_range(USEC_PER_MSEC, 2 * USEC_PER_MSEC); | |
967 | ret = ipa_modem_stop(ipa); | |
968 | } | |
a92dbb9c UKK |
969 | if (ret) { |
970 | /* | |
971 | * Not cleaning up here properly might also yield a | |
972 | * crash later on. As the device is still unregistered | |
973 | * in this case, this might even yield a crash later on. | |
974 | */ | |
975 | dev_err(dev, "Failed to stop modem (%pe), leaking resources\n", | |
976 | ERR_PTR(ret)); | |
977 | return; | |
978 | } | |
cdf2e941 AE |
979 | |
980 | ipa_teardown(ipa); | |
981 | } | |
982 | ||
983 | ipa_deconfig(ipa); | |
4c6a4da8 | 984 | out_power_put: |
1aac309d | 985 | pm_runtime_put_noidle(dev); |
76b5fbcd | 986 | ipa_smp2p_exit(ipa); |
cdf2e941 AE |
987 | ipa_table_exit(ipa); |
988 | ipa_endpoint_exit(ipa); | |
989 | gsi_exit(&ipa->gsi); | |
990 | ipa_mem_exit(ipa); | |
991 | ipa_reg_exit(ipa); | |
992 | kfree(ipa); | |
7aa0e8b8 | 993 | ipa_power_exit(power); |
cdf2e941 | 994 | |
3c91c86d | 995 | dev_info(dev, "IPA driver removed"); |
ae1d72f9 AE |
996 | } |
997 | ||
2e3cf97f AE |
998 | static const struct attribute_group *ipa_attribute_groups[] = { |
999 | &ipa_attribute_group, | |
1000 | &ipa_feature_attribute_group, | |
d79e4164 | 1001 | &ipa_endpoint_id_attribute_group, |
2e3cf97f AE |
1002 | &ipa_modem_attribute_group, |
1003 | NULL, | |
1004 | }; | |
1005 | ||
cdf2e941 | 1006 | static struct platform_driver ipa_driver = { |
ae1d72f9 | 1007 | .probe = ipa_probe, |
a92dbb9c UKK |
1008 | .remove_new = ipa_remove, |
1009 | .shutdown = ipa_remove, | |
cdf2e941 AE |
1010 | .driver = { |
1011 | .name = "ipa", | |
cdf2e941 AE |
1012 | .pm = &ipa_pm_ops, |
1013 | .of_match_table = ipa_match, | |
2e3cf97f | 1014 | .dev_groups = ipa_attribute_groups, |
cdf2e941 AE |
1015 | }, |
1016 | }; | |
1017 | ||
1018 | module_platform_driver(ipa_driver); | |
1019 | ||
1020 | MODULE_LICENSE("GPL v2"); | |
1021 | MODULE_DESCRIPTION("Qualcomm IP Accelerator device driver"); |