]>
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> | |
12 | #include <linux/remoteproc/qcom_q6v5_ipa_notify.h> | |
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) | |
296 | dev_err(dev, "error %d flushing hash cahces\n", ret); | |
297 | ||
298 | ret = ipa_endpoint_modem_exception_reset_all(ipa); | |
299 | if (ret) | |
300 | dev_err(dev, "error %d resetting exception endpoint", | |
301 | ret); | |
302 | ||
303 | ipa_endpoint_modem_pause_all(ipa, false); | |
304 | ||
305 | ret = ipa_modem_stop(ipa); | |
306 | if (ret) | |
307 | dev_err(dev, "error %d stopping modem", ret); | |
308 | ||
309 | /* Now prepare for the next modem boot */ | |
310 | ret = ipa_mem_zero_modem(ipa); | |
311 | if (ret) | |
312 | dev_err(dev, "error %d zeroing modem memory regions\n", ret); | |
313 | } | |
314 | ||
315 | static void ipa_modem_notify(void *data, enum qcom_rproc_event event) | |
316 | { | |
317 | struct ipa *ipa = data; | |
318 | struct device *dev; | |
319 | ||
320 | dev = &ipa->pdev->dev; | |
321 | switch (event) { | |
322 | case MODEM_STARTING: | |
323 | dev_info(dev, "received modem starting event\n"); | |
324 | ipa_smp2p_notify_reset(ipa); | |
325 | break; | |
326 | ||
327 | case MODEM_RUNNING: | |
328 | dev_info(dev, "received modem running event\n"); | |
329 | break; | |
330 | ||
331 | case MODEM_STOPPING: | |
332 | case MODEM_CRASHED: | |
333 | dev_info(dev, "received modem %s event\n", | |
334 | event == MODEM_STOPPING ? "stopping" | |
335 | : "crashed"); | |
336 | if (ipa->setup_complete) | |
337 | ipa_modem_crashed(ipa); | |
338 | break; | |
339 | ||
340 | case MODEM_OFFLINE: | |
341 | dev_info(dev, "received modem offline event\n"); | |
342 | break; | |
343 | ||
344 | case MODEM_REMOVING: | |
345 | dev_info(dev, "received modem stopping event\n"); | |
346 | break; | |
347 | ||
348 | default: | |
349 | dev_err(&ipa->pdev->dev, "unrecognized event %u\n", event); | |
350 | break; | |
351 | } | |
352 | } | |
353 | ||
354 | int ipa_modem_init(struct ipa *ipa, bool modem_init) | |
355 | { | |
356 | return ipa_smp2p_init(ipa, modem_init); | |
357 | } | |
358 | ||
359 | void ipa_modem_exit(struct ipa *ipa) | |
360 | { | |
361 | ipa_smp2p_exit(ipa); | |
362 | } | |
363 | ||
364 | int ipa_modem_config(struct ipa *ipa) | |
365 | { | |
366 | return qcom_register_ipa_notify(ipa->modem_rproc, ipa_modem_notify, | |
367 | ipa); | |
368 | } | |
369 | ||
370 | void ipa_modem_deconfig(struct ipa *ipa) | |
371 | { | |
372 | qcom_deregister_ipa_notify(ipa->modem_rproc); | |
373 | } | |
374 | ||
375 | int ipa_modem_setup(struct ipa *ipa) | |
376 | { | |
377 | return ipa_qmi_setup(ipa); | |
378 | } | |
379 | ||
380 | void ipa_modem_teardown(struct ipa *ipa) | |
381 | { | |
382 | ipa_qmi_teardown(ipa); | |
383 | } |