]>
Commit | Line | Data |
---|---|---|
a646d6ec AE |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | ||
3 | /* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved. | |
4 | * Copyright (C) 2018-2020 Linaro Ltd. | |
5 | */ | |
6 | ||
7 | #include <linux/errno.h> | |
8 | #include <linux/if_arp.h> | |
9 | #include <linux/netdevice.h> | |
10 | #include <linux/skbuff.h> | |
11 | #include <linux/if_rmnet.h> | |
30eb3fbe | 12 | #include <linux/remoteproc/qcom_rproc.h> |
a646d6ec AE |
13 | |
14 | #include "ipa.h" | |
15 | #include "ipa_data.h" | |
16 | #include "ipa_endpoint.h" | |
17 | #include "ipa_table.h" | |
18 | #include "ipa_mem.h" | |
19 | #include "ipa_modem.h" | |
20 | #include "ipa_smp2p.h" | |
21 | #include "ipa_qmi.h" | |
22 | ||
23 | #define IPA_NETDEV_NAME "rmnet_ipa%d" | |
24 | #define IPA_NETDEV_TAILROOM 0 /* for padding by mux layer */ | |
25 | #define IPA_NETDEV_TIMEOUT 10 /* seconds */ | |
26 | ||
27 | enum ipa_modem_state { | |
28 | IPA_MODEM_STATE_STOPPED = 0, | |
29 | IPA_MODEM_STATE_STARTING, | |
30 | IPA_MODEM_STATE_RUNNING, | |
31 | IPA_MODEM_STATE_STOPPING, | |
32 | }; | |
33 | ||
34 | /** struct ipa_priv - IPA network device private data */ | |
35 | struct ipa_priv { | |
36 | struct ipa *ipa; | |
37 | }; | |
38 | ||
39 | /** ipa_open() - Opens the modem network interface */ | |
40 | static int ipa_open(struct net_device *netdev) | |
41 | { | |
42 | struct ipa_priv *priv = netdev_priv(netdev); | |
43 | struct ipa *ipa = priv->ipa; | |
44 | int ret; | |
45 | ||
46 | ret = ipa_endpoint_enable_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]); | |
47 | if (ret) | |
48 | return ret; | |
49 | ret = ipa_endpoint_enable_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]); | |
50 | if (ret) | |
51 | goto err_disable_tx; | |
52 | ||
53 | netif_start_queue(netdev); | |
54 | ||
55 | return 0; | |
56 | ||
57 | err_disable_tx: | |
58 | ipa_endpoint_disable_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]); | |
59 | ||
60 | return ret; | |
61 | } | |
62 | ||
63 | /** ipa_stop() - Stops the modem network interface. */ | |
64 | static int ipa_stop(struct net_device *netdev) | |
65 | { | |
66 | struct ipa_priv *priv = netdev_priv(netdev); | |
67 | struct ipa *ipa = priv->ipa; | |
68 | ||
69 | netif_stop_queue(netdev); | |
70 | ||
71 | ipa_endpoint_disable_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]); | |
72 | ipa_endpoint_disable_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]); | |
73 | ||
74 | return 0; | |
75 | } | |
76 | ||
77 | /** ipa_start_xmit() - Transmits an skb. | |
78 | * @skb: skb to be transmitted | |
79 | * @dev: network device | |
80 | * | |
81 | * Return codes: | |
82 | * NETDEV_TX_OK: Success | |
83 | * NETDEV_TX_BUSY: Error while transmitting the skb. Try again later | |
84 | */ | |
85 | static int ipa_start_xmit(struct sk_buff *skb, struct net_device *netdev) | |
86 | { | |
87 | struct net_device_stats *stats = &netdev->stats; | |
88 | struct ipa_priv *priv = netdev_priv(netdev); | |
89 | struct ipa_endpoint *endpoint; | |
90 | struct ipa *ipa = priv->ipa; | |
91 | u32 skb_len = skb->len; | |
92 | int ret; | |
93 | ||
94 | if (!skb_len) | |
95 | goto err_drop_skb; | |
96 | ||
97 | endpoint = ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]; | |
98 | if (endpoint->data->qmap && skb->protocol != htons(ETH_P_MAP)) | |
99 | goto err_drop_skb; | |
100 | ||
101 | ret = ipa_endpoint_skb_tx(endpoint, skb); | |
102 | if (ret) { | |
103 | if (ret != -E2BIG) | |
104 | return NETDEV_TX_BUSY; | |
105 | goto err_drop_skb; | |
106 | } | |
107 | ||
108 | stats->tx_packets++; | |
109 | stats->tx_bytes += skb_len; | |
110 | ||
111 | return NETDEV_TX_OK; | |
112 | ||
113 | err_drop_skb: | |
114 | dev_kfree_skb_any(skb); | |
115 | stats->tx_dropped++; | |
116 | ||
117 | return NETDEV_TX_OK; | |
118 | } | |
119 | ||
120 | void ipa_modem_skb_rx(struct net_device *netdev, struct sk_buff *skb) | |
121 | { | |
122 | struct net_device_stats *stats = &netdev->stats; | |
123 | ||
124 | if (skb) { | |
125 | skb->dev = netdev; | |
126 | skb->protocol = htons(ETH_P_MAP); | |
127 | stats->rx_packets++; | |
128 | stats->rx_bytes += skb->len; | |
129 | ||
130 | (void)netif_receive_skb(skb); | |
131 | } else { | |
132 | stats->rx_dropped++; | |
133 | } | |
134 | } | |
135 | ||
136 | static const struct net_device_ops ipa_modem_ops = { | |
137 | .ndo_open = ipa_open, | |
138 | .ndo_stop = ipa_stop, | |
139 | .ndo_start_xmit = ipa_start_xmit, | |
140 | }; | |
141 | ||
142 | /** ipa_modem_netdev_setup() - netdev setup function for the modem */ | |
143 | static void ipa_modem_netdev_setup(struct net_device *netdev) | |
144 | { | |
145 | netdev->netdev_ops = &ipa_modem_ops; | |
146 | ether_setup(netdev); | |
147 | /* No header ops (override value set by ether_setup()) */ | |
148 | netdev->header_ops = NULL; | |
149 | netdev->type = ARPHRD_RAWIP; | |
150 | netdev->hard_header_len = 0; | |
151 | netdev->max_mtu = IPA_MTU; | |
152 | netdev->mtu = netdev->max_mtu; | |
153 | netdev->addr_len = 0; | |
154 | netdev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST); | |
155 | /* The endpoint is configured for QMAP */ | |
156 | netdev->needed_headroom = sizeof(struct rmnet_map_header); | |
157 | netdev->needed_tailroom = IPA_NETDEV_TAILROOM; | |
158 | netdev->watchdog_timeo = IPA_NETDEV_TIMEOUT * HZ; | |
159 | netdev->hw_features = NETIF_F_SG; | |
160 | } | |
161 | ||
162 | /** ipa_modem_suspend() - suspend callback | |
163 | * @netdev: Network device | |
164 | * | |
165 | * Suspend the modem's endpoints. | |
166 | */ | |
167 | void ipa_modem_suspend(struct net_device *netdev) | |
168 | { | |
169 | struct ipa_priv *priv = netdev_priv(netdev); | |
170 | struct ipa *ipa = priv->ipa; | |
171 | ||
172 | netif_stop_queue(netdev); | |
173 | ||
174 | ipa_endpoint_suspend_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]); | |
175 | ipa_endpoint_suspend_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]); | |
176 | } | |
177 | ||
178 | /** ipa_modem_resume() - resume callback for runtime_pm | |
179 | * @dev: pointer to device | |
180 | * | |
181 | * Resume the modem's endpoints. | |
182 | */ | |
183 | void ipa_modem_resume(struct net_device *netdev) | |
184 | { | |
185 | struct ipa_priv *priv = netdev_priv(netdev); | |
186 | struct ipa *ipa = priv->ipa; | |
187 | ||
188 | ipa_endpoint_resume_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]); | |
189 | ipa_endpoint_resume_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]); | |
190 | ||
191 | netif_wake_queue(netdev); | |
192 | } | |
193 | ||
194 | int ipa_modem_start(struct ipa *ipa) | |
195 | { | |
196 | enum ipa_modem_state state; | |
197 | struct net_device *netdev; | |
198 | struct ipa_priv *priv; | |
199 | int ret; | |
200 | ||
201 | /* Only attempt to start the modem if it's stopped */ | |
202 | state = atomic_cmpxchg(&ipa->modem_state, IPA_MODEM_STATE_STOPPED, | |
203 | IPA_MODEM_STATE_STARTING); | |
204 | ||
205 | /* Silently ignore attempts when running, or when changing state */ | |
206 | if (state != IPA_MODEM_STATE_STOPPED) | |
207 | return 0; | |
208 | ||
209 | netdev = alloc_netdev(sizeof(struct ipa_priv), IPA_NETDEV_NAME, | |
210 | NET_NAME_UNKNOWN, ipa_modem_netdev_setup); | |
211 | if (!netdev) { | |
212 | ret = -ENOMEM; | |
213 | goto out_set_state; | |
214 | } | |
215 | ||
216 | ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]->netdev = netdev; | |
217 | ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]->netdev = netdev; | |
218 | ||
219 | priv = netdev_priv(netdev); | |
220 | priv->ipa = ipa; | |
221 | ||
222 | ret = register_netdev(netdev); | |
223 | if (ret) | |
224 | free_netdev(netdev); | |
225 | else | |
226 | ipa->modem_netdev = netdev; | |
227 | ||
228 | out_set_state: | |
229 | if (ret) | |
230 | atomic_set(&ipa->modem_state, IPA_MODEM_STATE_STOPPED); | |
231 | else | |
232 | atomic_set(&ipa->modem_state, IPA_MODEM_STATE_RUNNING); | |
233 | smp_mb__after_atomic(); | |
234 | ||
235 | return ret; | |
236 | } | |
237 | ||
238 | int ipa_modem_stop(struct ipa *ipa) | |
239 | { | |
240 | struct net_device *netdev = ipa->modem_netdev; | |
241 | enum ipa_modem_state state; | |
242 | int ret; | |
243 | ||
244 | /* Only attempt to stop the modem if it's running */ | |
245 | state = atomic_cmpxchg(&ipa->modem_state, IPA_MODEM_STATE_RUNNING, | |
246 | IPA_MODEM_STATE_STOPPING); | |
247 | ||
248 | /* Silently ignore attempts when already stopped */ | |
249 | if (state == IPA_MODEM_STATE_STOPPED) | |
250 | return 0; | |
251 | ||
252 | /* If we're somewhere between stopped and starting, we're busy */ | |
253 | if (state != IPA_MODEM_STATE_RUNNING) | |
254 | return -EBUSY; | |
255 | ||
256 | /* Prevent the modem from triggering a call to ipa_setup() */ | |
257 | ipa_smp2p_disable(ipa); | |
258 | ||
259 | if (netdev) { | |
260 | /* Stop the queue and disable the endpoints if it's open */ | |
261 | ret = ipa_stop(netdev); | |
262 | if (ret) | |
263 | goto out_set_state; | |
264 | ||
265 | ipa->modem_netdev = NULL; | |
266 | unregister_netdev(netdev); | |
267 | free_netdev(netdev); | |
268 | } else { | |
269 | ret = 0; | |
270 | } | |
271 | ||
272 | out_set_state: | |
273 | if (ret) | |
274 | atomic_set(&ipa->modem_state, IPA_MODEM_STATE_RUNNING); | |
275 | else | |
276 | atomic_set(&ipa->modem_state, IPA_MODEM_STATE_STOPPED); | |
277 | smp_mb__after_atomic(); | |
278 | ||
279 | return ret; | |
280 | } | |
281 | ||
282 | /* Treat a "clean" modem stop the same as a crash */ | |
283 | static void ipa_modem_crashed(struct ipa *ipa) | |
284 | { | |
285 | struct device *dev = &ipa->pdev->dev; | |
286 | int ret; | |
287 | ||
288 | ipa_endpoint_modem_pause_all(ipa, true); | |
289 | ||
290 | ipa_endpoint_modem_hol_block_clear_all(ipa); | |
291 | ||
292 | ipa_table_reset(ipa, true); | |
293 | ||
294 | ret = ipa_table_hash_flush(ipa); | |
295 | if (ret) | |
dc3e19f4 | 296 | dev_err(dev, "error %d flushing hash caches\n", ret); |
a646d6ec AE |
297 | |
298 | ret = ipa_endpoint_modem_exception_reset_all(ipa); | |
299 | if (ret) | |
2ba53898 | 300 | dev_err(dev, "error %d resetting exception endpoint\n", ret); |
a646d6ec AE |
301 | |
302 | ipa_endpoint_modem_pause_all(ipa, false); | |
303 | ||
304 | ret = ipa_modem_stop(ipa); | |
305 | if (ret) | |
2ba53898 | 306 | dev_err(dev, "error %d stopping modem\n", ret); |
a646d6ec AE |
307 | |
308 | /* Now prepare for the next modem boot */ | |
309 | ret = ipa_mem_zero_modem(ipa); | |
310 | if (ret) | |
311 | dev_err(dev, "error %d zeroing modem memory regions\n", ret); | |
312 | } | |
313 | ||
30eb3fbe AE |
314 | static int ipa_modem_notify(struct notifier_block *nb, unsigned long action, |
315 | void *data) | |
a646d6ec | 316 | { |
30eb3fbe AE |
317 | struct ipa *ipa = container_of(nb, struct ipa, nb); |
318 | struct qcom_ssr_notify_data *notify_data = data; | |
319 | struct device *dev = &ipa->pdev->dev; | |
a646d6ec | 320 | |
30eb3fbe AE |
321 | switch (action) { |
322 | case QCOM_SSR_BEFORE_POWERUP: | |
a646d6ec AE |
323 | dev_info(dev, "received modem starting event\n"); |
324 | ipa_smp2p_notify_reset(ipa); | |
325 | break; | |
326 | ||
30eb3fbe | 327 | case QCOM_SSR_AFTER_POWERUP: |
a646d6ec AE |
328 | dev_info(dev, "received modem running event\n"); |
329 | break; | |
330 | ||
30eb3fbe | 331 | case QCOM_SSR_BEFORE_SHUTDOWN: |
a646d6ec | 332 | dev_info(dev, "received modem %s event\n", |
30eb3fbe | 333 | notify_data->crashed ? "crashed" : "stopping"); |
a646d6ec AE |
334 | if (ipa->setup_complete) |
335 | ipa_modem_crashed(ipa); | |
336 | break; | |
337 | ||
30eb3fbe | 338 | case QCOM_SSR_AFTER_SHUTDOWN: |
a646d6ec AE |
339 | dev_info(dev, "received modem offline event\n"); |
340 | break; | |
341 | ||
a646d6ec | 342 | default: |
30eb3fbe | 343 | dev_err(dev, "received unrecognized event %lu\n", action); |
a646d6ec AE |
344 | break; |
345 | } | |
30eb3fbe AE |
346 | |
347 | return NOTIFY_OK; | |
a646d6ec AE |
348 | } |
349 | ||
350 | int ipa_modem_init(struct ipa *ipa, bool modem_init) | |
351 | { | |
352 | return ipa_smp2p_init(ipa, modem_init); | |
353 | } | |
354 | ||
355 | void ipa_modem_exit(struct ipa *ipa) | |
356 | { | |
357 | ipa_smp2p_exit(ipa); | |
358 | } | |
359 | ||
360 | int ipa_modem_config(struct ipa *ipa) | |
361 | { | |
30eb3fbe AE |
362 | void *notifier; |
363 | ||
364 | ipa->nb.notifier_call = ipa_modem_notify; | |
365 | ||
366 | notifier = qcom_register_ssr_notifier("mpss", &ipa->nb); | |
367 | if (IS_ERR(notifier)) | |
368 | return PTR_ERR(notifier); | |
369 | ||
370 | ipa->notifier = notifier; | |
371 | ||
372 | return 0; | |
a646d6ec AE |
373 | } |
374 | ||
375 | void ipa_modem_deconfig(struct ipa *ipa) | |
376 | { | |
30eb3fbe AE |
377 | struct device *dev = &ipa->pdev->dev; |
378 | int ret; | |
379 | ||
380 | ret = qcom_unregister_ssr_notifier(ipa->notifier, &ipa->nb); | |
381 | if (ret) | |
382 | dev_err(dev, "error %d unregistering notifier", ret); | |
383 | ||
384 | ipa->notifier = NULL; | |
385 | memset(&ipa->nb, 0, sizeof(ipa->nb)); | |
a646d6ec AE |
386 | } |
387 | ||
388 | int ipa_modem_setup(struct ipa *ipa) | |
389 | { | |
390 | return ipa_qmi_setup(ipa); | |
391 | } | |
392 | ||
393 | void ipa_modem_teardown(struct ipa *ipa) | |
394 | { | |
395 | ipa_qmi_teardown(ipa); | |
396 | } |