xref: /openbmc/linux/drivers/net/ipa/ipa_modem.c (revision a4388da5)
1a646d6ecSAlex Elder // SPDX-License-Identifier: GPL-2.0
2a646d6ecSAlex Elder 
3a646d6ecSAlex Elder /* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
4*a4388da5SAlex Elder  * Copyright (C) 2018-2022 Linaro Ltd.
5a646d6ecSAlex Elder  */
6a646d6ecSAlex Elder 
7a646d6ecSAlex Elder #include <linux/errno.h>
8a646d6ecSAlex Elder #include <linux/if_arp.h>
9a646d6ecSAlex Elder #include <linux/netdevice.h>
10a646d6ecSAlex Elder #include <linux/skbuff.h>
11a646d6ecSAlex Elder #include <linux/if_rmnet.h>
1275944b04SAlex Elder #include <linux/etherdevice.h>
1375944b04SAlex Elder #include <net/pkt_sched.h>
14a96e73faSAlex Elder #include <linux/pm_runtime.h>
1530eb3fbeSAlex Elder #include <linux/remoteproc/qcom_rproc.h>
16a646d6ecSAlex Elder 
17a646d6ecSAlex Elder #include "ipa.h"
18a646d6ecSAlex Elder #include "ipa_data.h"
19a646d6ecSAlex Elder #include "ipa_endpoint.h"
20a646d6ecSAlex Elder #include "ipa_table.h"
21a646d6ecSAlex Elder #include "ipa_mem.h"
22a646d6ecSAlex Elder #include "ipa_modem.h"
23a646d6ecSAlex Elder #include "ipa_smp2p.h"
24a646d6ecSAlex Elder #include "ipa_qmi.h"
25e2f154e6SAlex Elder #include "ipa_uc.h"
262775cbc5SAlex Elder #include "ipa_power.h"
27a646d6ecSAlex Elder 
28a646d6ecSAlex Elder #define IPA_NETDEV_NAME		"rmnet_ipa%d"
29a646d6ecSAlex Elder #define IPA_NETDEV_TAILROOM	0	/* for padding by mux layer */
30a646d6ecSAlex Elder #define IPA_NETDEV_TIMEOUT	10	/* seconds */
31a646d6ecSAlex Elder 
32a646d6ecSAlex Elder enum ipa_modem_state {
33a646d6ecSAlex Elder 	IPA_MODEM_STATE_STOPPED	= 0,
34a646d6ecSAlex Elder 	IPA_MODEM_STATE_STARTING,
35a646d6ecSAlex Elder 	IPA_MODEM_STATE_RUNNING,
36a646d6ecSAlex Elder 	IPA_MODEM_STATE_STOPPING,
37a646d6ecSAlex Elder };
38a646d6ecSAlex Elder 
39a96e73faSAlex Elder /**
40a96e73faSAlex Elder  * struct ipa_priv - IPA network device private data
41a96e73faSAlex Elder  * @ipa:	IPA pointer
42a96e73faSAlex Elder  * @work:	Work structure used to wake the modem netdev TX queue
43a96e73faSAlex Elder  */
44a646d6ecSAlex Elder struct ipa_priv {
45a646d6ecSAlex Elder 	struct ipa *ipa;
46a96e73faSAlex Elder 	struct work_struct work;
47a646d6ecSAlex Elder };
48a646d6ecSAlex Elder 
49a646d6ecSAlex Elder /** ipa_open() - Opens the modem network interface */
ipa_open(struct net_device * netdev)50a646d6ecSAlex Elder static int ipa_open(struct net_device *netdev)
51a646d6ecSAlex Elder {
52a646d6ecSAlex Elder 	struct ipa_priv *priv = netdev_priv(netdev);
53a646d6ecSAlex Elder 	struct ipa *ipa = priv->ipa;
54724c2d74SAlex Elder 	struct device *dev;
55a646d6ecSAlex Elder 	int ret;
56a646d6ecSAlex Elder 
57724c2d74SAlex Elder 	dev = &ipa->pdev->dev;
58724c2d74SAlex Elder 	ret = pm_runtime_get_sync(dev);
59724c2d74SAlex Elder 	if (ret < 0)
60724c2d74SAlex Elder 		goto err_power_put;
61f2b03553SAlex Elder 
62a646d6ecSAlex Elder 	ret = ipa_endpoint_enable_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]);
63a646d6ecSAlex Elder 	if (ret)
64724c2d74SAlex Elder 		goto err_power_put;
65f2b03553SAlex Elder 
66a646d6ecSAlex Elder 	ret = ipa_endpoint_enable_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]);
67a646d6ecSAlex Elder 	if (ret)
68a646d6ecSAlex Elder 		goto err_disable_tx;
69a646d6ecSAlex Elder 
70a646d6ecSAlex Elder 	netif_start_queue(netdev);
71a646d6ecSAlex Elder 
721aac309dSAlex Elder 	pm_runtime_mark_last_busy(dev);
731aac309dSAlex Elder 	(void)pm_runtime_put_autosuspend(dev);
748dc181f2SAlex Elder 
75a646d6ecSAlex Elder 	return 0;
76a646d6ecSAlex Elder 
77a646d6ecSAlex Elder err_disable_tx:
78a646d6ecSAlex Elder 	ipa_endpoint_disable_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]);
79724c2d74SAlex Elder err_power_put:
801aac309dSAlex Elder 	pm_runtime_put_noidle(dev);
81a646d6ecSAlex Elder 
82a646d6ecSAlex Elder 	return ret;
83a646d6ecSAlex Elder }
84a646d6ecSAlex Elder 
85a646d6ecSAlex Elder /** ipa_stop() - Stops the modem network interface. */
ipa_stop(struct net_device * netdev)86a646d6ecSAlex Elder static int ipa_stop(struct net_device *netdev)
87a646d6ecSAlex Elder {
88a646d6ecSAlex Elder 	struct ipa_priv *priv = netdev_priv(netdev);
89a646d6ecSAlex Elder 	struct ipa *ipa = priv->ipa;
90724c2d74SAlex Elder 	struct device *dev;
918dc181f2SAlex Elder 	int ret;
928dc181f2SAlex Elder 
93724c2d74SAlex Elder 	dev = &ipa->pdev->dev;
94724c2d74SAlex Elder 	ret = pm_runtime_get_sync(dev);
95724c2d74SAlex Elder 	if (ret < 0)
96724c2d74SAlex Elder 		goto out_power_put;
97a646d6ecSAlex Elder 
98a646d6ecSAlex Elder 	netif_stop_queue(netdev);
99a646d6ecSAlex Elder 
100a646d6ecSAlex Elder 	ipa_endpoint_disable_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]);
101a646d6ecSAlex Elder 	ipa_endpoint_disable_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]);
102724c2d74SAlex Elder out_power_put:
1031aac309dSAlex Elder 	pm_runtime_mark_last_busy(dev);
1041aac309dSAlex Elder 	(void)pm_runtime_put_autosuspend(dev);
105f2b03553SAlex Elder 
106a646d6ecSAlex Elder 	return 0;
107a646d6ecSAlex Elder }
108a646d6ecSAlex Elder 
109a646d6ecSAlex Elder /** ipa_start_xmit() - Transmits an skb.
110a646d6ecSAlex Elder  * @skb: skb to be transmitted
111a646d6ecSAlex Elder  * @dev: network device
112a646d6ecSAlex Elder  *
113a646d6ecSAlex Elder  * Return codes:
114a646d6ecSAlex Elder  * NETDEV_TX_OK: Success
115a646d6ecSAlex Elder  * NETDEV_TX_BUSY: Error while transmitting the skb. Try again later
116a646d6ecSAlex Elder  */
1178dc181f2SAlex Elder static netdev_tx_t
ipa_start_xmit(struct sk_buff * skb,struct net_device * netdev)1188dc181f2SAlex Elder ipa_start_xmit(struct sk_buff *skb, struct net_device *netdev)
119a646d6ecSAlex Elder {
120a646d6ecSAlex Elder 	struct net_device_stats *stats = &netdev->stats;
121a646d6ecSAlex Elder 	struct ipa_priv *priv = netdev_priv(netdev);
122a646d6ecSAlex Elder 	struct ipa_endpoint *endpoint;
123a646d6ecSAlex Elder 	struct ipa *ipa = priv->ipa;
124a646d6ecSAlex Elder 	u32 skb_len = skb->len;
1256b51f802SAlex Elder 	struct device *dev;
126a646d6ecSAlex Elder 	int ret;
127a646d6ecSAlex Elder 
128a646d6ecSAlex Elder 	if (!skb_len)
129a646d6ecSAlex Elder 		goto err_drop_skb;
130a646d6ecSAlex Elder 
131a646d6ecSAlex Elder 	endpoint = ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX];
132660e52d6SAlex Elder 	if (endpoint->config.qmap && skb->protocol != htons(ETH_P_MAP))
133a646d6ecSAlex Elder 		goto err_drop_skb;
134a646d6ecSAlex Elder 
1356b51f802SAlex Elder 	/* The hardware must be powered for us to transmit */
1366b51f802SAlex Elder 	dev = &ipa->pdev->dev;
1376b51f802SAlex Elder 	ret = pm_runtime_get(dev);
1386b51f802SAlex Elder 	if (ret < 1) {
1396b51f802SAlex Elder 		/* If a resume won't happen, just drop the packet */
1406b51f802SAlex Elder 		if (ret < 0 && ret != -EINPROGRESS) {
141b8e36e13SAlex Elder 			ipa_power_modem_queue_active(ipa);
1426b51f802SAlex Elder 			pm_runtime_put_noidle(dev);
1436b51f802SAlex Elder 			goto err_drop_skb;
1446b51f802SAlex Elder 		}
1456b51f802SAlex Elder 
1466b51f802SAlex Elder 		/* No power (yet).  Stop the network stack from transmitting
1476b51f802SAlex Elder 		 * until we're resumed; ipa_modem_resume() arranges for the
1486b51f802SAlex Elder 		 * TX queue to be started again.
1496b51f802SAlex Elder 		 */
150b8e36e13SAlex Elder 		ipa_power_modem_queue_stop(ipa);
1516b51f802SAlex Elder 
1521aac309dSAlex Elder 		pm_runtime_put_noidle(dev);
1536b51f802SAlex Elder 
1546b51f802SAlex Elder 		return NETDEV_TX_BUSY;
1556b51f802SAlex Elder 	}
1566b51f802SAlex Elder 
157b8e36e13SAlex Elder 	ipa_power_modem_queue_active(ipa);
158b8e36e13SAlex Elder 
159a646d6ecSAlex Elder 	ret = ipa_endpoint_skb_tx(endpoint, skb);
1606b51f802SAlex Elder 
1611aac309dSAlex Elder 	pm_runtime_mark_last_busy(dev);
1621aac309dSAlex Elder 	(void)pm_runtime_put_autosuspend(dev);
1636b51f802SAlex Elder 
164a646d6ecSAlex Elder 	if (ret) {
165a646d6ecSAlex Elder 		if (ret != -E2BIG)
166a646d6ecSAlex Elder 			return NETDEV_TX_BUSY;
167a646d6ecSAlex Elder 		goto err_drop_skb;
168a646d6ecSAlex Elder 	}
169a646d6ecSAlex Elder 
170a646d6ecSAlex Elder 	stats->tx_packets++;
171a646d6ecSAlex Elder 	stats->tx_bytes += skb_len;
172a646d6ecSAlex Elder 
173a646d6ecSAlex Elder 	return NETDEV_TX_OK;
174a646d6ecSAlex Elder 
175a646d6ecSAlex Elder err_drop_skb:
176a646d6ecSAlex Elder 	dev_kfree_skb_any(skb);
177a646d6ecSAlex Elder 	stats->tx_dropped++;
178a646d6ecSAlex Elder 
179a646d6ecSAlex Elder 	return NETDEV_TX_OK;
180a646d6ecSAlex Elder }
181a646d6ecSAlex Elder 
ipa_modem_skb_rx(struct net_device * netdev,struct sk_buff * skb)182a646d6ecSAlex Elder void ipa_modem_skb_rx(struct net_device *netdev, struct sk_buff *skb)
183a646d6ecSAlex Elder {
184a646d6ecSAlex Elder 	struct net_device_stats *stats = &netdev->stats;
185a646d6ecSAlex Elder 
186a646d6ecSAlex Elder 	if (skb) {
187a646d6ecSAlex Elder 		skb->dev = netdev;
188a646d6ecSAlex Elder 		skb->protocol = htons(ETH_P_MAP);
189a646d6ecSAlex Elder 		stats->rx_packets++;
190a646d6ecSAlex Elder 		stats->rx_bytes += skb->len;
191a646d6ecSAlex Elder 
192a646d6ecSAlex Elder 		(void)netif_receive_skb(skb);
193a646d6ecSAlex Elder 	} else {
194a646d6ecSAlex Elder 		stats->rx_dropped++;
195a646d6ecSAlex Elder 	}
196a646d6ecSAlex Elder }
197a646d6ecSAlex Elder 
198a646d6ecSAlex Elder static const struct net_device_ops ipa_modem_ops = {
199a646d6ecSAlex Elder 	.ndo_open	= ipa_open,
200a646d6ecSAlex Elder 	.ndo_stop	= ipa_stop,
201a646d6ecSAlex Elder 	.ndo_start_xmit	= ipa_start_xmit,
202a646d6ecSAlex Elder };
203a646d6ecSAlex Elder 
204a646d6ecSAlex Elder /** ipa_modem_netdev_setup() - netdev setup function for the modem */
ipa_modem_netdev_setup(struct net_device * netdev)205a646d6ecSAlex Elder static void ipa_modem_netdev_setup(struct net_device *netdev)
206a646d6ecSAlex Elder {
207a646d6ecSAlex Elder 	netdev->netdev_ops = &ipa_modem_ops;
20875944b04SAlex Elder 
209a646d6ecSAlex Elder 	netdev->header_ops = NULL;
210a646d6ecSAlex Elder 	netdev->type = ARPHRD_RAWIP;
211a646d6ecSAlex Elder 	netdev->hard_header_len = 0;
21275944b04SAlex Elder 	netdev->min_header_len = ETH_HLEN;
21375944b04SAlex Elder 	netdev->min_mtu = ETH_MIN_MTU;
214a646d6ecSAlex Elder 	netdev->max_mtu = IPA_MTU;
215a646d6ecSAlex Elder 	netdev->mtu = netdev->max_mtu;
216a646d6ecSAlex Elder 	netdev->addr_len = 0;
21775944b04SAlex Elder 	netdev->tx_queue_len = DEFAULT_TX_QUEUE_LEN;
218a646d6ecSAlex Elder 	netdev->flags &= ~(IFF_BROADCAST | IFF_MULTICAST);
21975944b04SAlex Elder 	netdev->priv_flags |= IFF_TX_SKB_SHARING;
22075944b04SAlex Elder 	eth_broadcast_addr(netdev->broadcast);
22175944b04SAlex Elder 
222a646d6ecSAlex Elder 	/* The endpoint is configured for QMAP */
223a646d6ecSAlex Elder 	netdev->needed_headroom = sizeof(struct rmnet_map_header);
224a646d6ecSAlex Elder 	netdev->needed_tailroom = IPA_NETDEV_TAILROOM;
225a646d6ecSAlex Elder 	netdev->watchdog_timeo = IPA_NETDEV_TIMEOUT * HZ;
226a646d6ecSAlex Elder 	netdev->hw_features = NETIF_F_SG;
227a646d6ecSAlex Elder }
228a646d6ecSAlex Elder 
229a646d6ecSAlex Elder /** ipa_modem_suspend() - suspend callback
230a646d6ecSAlex Elder  * @netdev:	Network device
231a646d6ecSAlex Elder  *
232a646d6ecSAlex Elder  * Suspend the modem's endpoints.
233a646d6ecSAlex Elder  */
ipa_modem_suspend(struct net_device * netdev)234a646d6ecSAlex Elder void ipa_modem_suspend(struct net_device *netdev)
235a646d6ecSAlex Elder {
236a646d6ecSAlex Elder 	struct ipa_priv *priv = netdev_priv(netdev);
237a646d6ecSAlex Elder 	struct ipa *ipa = priv->ipa;
238a646d6ecSAlex Elder 
23930c2515bSAlex Elder 	if (!(netdev->flags & IFF_UP))
24030c2515bSAlex Elder 		return;
24130c2515bSAlex Elder 
242a646d6ecSAlex Elder 	ipa_endpoint_suspend_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]);
243a646d6ecSAlex Elder 	ipa_endpoint_suspend_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]);
244a646d6ecSAlex Elder }
245a646d6ecSAlex Elder 
246a96e73faSAlex Elder /**
247a96e73faSAlex Elder  * ipa_modem_wake_queue_work() - enable modem netdev queue
248a96e73faSAlex Elder  * @work:	Work structure
249a96e73faSAlex Elder  *
250a96e73faSAlex Elder  * Re-enable transmit on the modem network device.  This is called
251a96e73faSAlex Elder  * in (power management) work queue context, scheduled when resuming
2526b51f802SAlex Elder  * the modem.  We can't enable the queue directly in ipa_modem_resume()
2536b51f802SAlex Elder  * because transmits restart the instant the queue is awakened; but the
2546b51f802SAlex Elder  * device power state won't be ACTIVE until *after* ipa_modem_resume()
2556b51f802SAlex Elder  * returns.
256a96e73faSAlex Elder  */
ipa_modem_wake_queue_work(struct work_struct * work)257a96e73faSAlex Elder static void ipa_modem_wake_queue_work(struct work_struct *work)
258a96e73faSAlex Elder {
259a96e73faSAlex Elder 	struct ipa_priv *priv = container_of(work, struct ipa_priv, work);
260a96e73faSAlex Elder 
261b8e36e13SAlex Elder 	ipa_power_modem_queue_wake(priv->ipa);
262a96e73faSAlex Elder }
263a96e73faSAlex Elder 
264a646d6ecSAlex Elder /** ipa_modem_resume() - resume callback for runtime_pm
265a646d6ecSAlex Elder  * @dev: pointer to device
266a646d6ecSAlex Elder  *
267a646d6ecSAlex Elder  * Resume the modem's endpoints.
268a646d6ecSAlex Elder  */
ipa_modem_resume(struct net_device * netdev)269a646d6ecSAlex Elder void ipa_modem_resume(struct net_device *netdev)
270a646d6ecSAlex Elder {
271a646d6ecSAlex Elder 	struct ipa_priv *priv = netdev_priv(netdev);
272a646d6ecSAlex Elder 	struct ipa *ipa = priv->ipa;
273a646d6ecSAlex Elder 
27430c2515bSAlex Elder 	if (!(netdev->flags & IFF_UP))
27530c2515bSAlex Elder 		return;
27630c2515bSAlex Elder 
277a646d6ecSAlex Elder 	ipa_endpoint_resume_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]);
278a646d6ecSAlex Elder 	ipa_endpoint_resume_one(ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]);
279a646d6ecSAlex Elder 
280a96e73faSAlex Elder 	/* Arrange for the TX queue to be restarted */
281a96e73faSAlex Elder 	(void)queue_pm_work(&priv->work);
282a646d6ecSAlex Elder }
283a646d6ecSAlex Elder 
ipa_modem_start(struct ipa * ipa)284a646d6ecSAlex Elder int ipa_modem_start(struct ipa *ipa)
285a646d6ecSAlex Elder {
286a646d6ecSAlex Elder 	enum ipa_modem_state state;
287a646d6ecSAlex Elder 	struct net_device *netdev;
288a646d6ecSAlex Elder 	struct ipa_priv *priv;
289a646d6ecSAlex Elder 	int ret;
290a646d6ecSAlex Elder 
291a646d6ecSAlex Elder 	/* Only attempt to start the modem if it's stopped */
292a646d6ecSAlex Elder 	state = atomic_cmpxchg(&ipa->modem_state, IPA_MODEM_STATE_STOPPED,
293a646d6ecSAlex Elder 			       IPA_MODEM_STATE_STARTING);
294a646d6ecSAlex Elder 
295a646d6ecSAlex Elder 	/* Silently ignore attempts when running, or when changing state */
296a646d6ecSAlex Elder 	if (state != IPA_MODEM_STATE_STOPPED)
297a646d6ecSAlex Elder 		return 0;
298a646d6ecSAlex Elder 
299a646d6ecSAlex Elder 	netdev = alloc_netdev(sizeof(struct ipa_priv), IPA_NETDEV_NAME,
300a646d6ecSAlex Elder 			      NET_NAME_UNKNOWN, ipa_modem_netdev_setup);
301a646d6ecSAlex Elder 	if (!netdev) {
302a646d6ecSAlex Elder 		ret = -ENOMEM;
303a646d6ecSAlex Elder 		goto out_set_state;
304a646d6ecSAlex Elder 	}
305a646d6ecSAlex Elder 
306afba9dc1SStephan Gerhold 	SET_NETDEV_DEV(netdev, &ipa->pdev->dev);
307a646d6ecSAlex Elder 	priv = netdev_priv(netdev);
308a646d6ecSAlex Elder 	priv->ipa = ipa;
309a96e73faSAlex Elder 	INIT_WORK(&priv->work, ipa_modem_wake_queue_work);
31057f63fafSAlex Elder 	ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]->netdev = netdev;
31157f63fafSAlex Elder 	ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]->netdev = netdev;
31210cc73c4SAlex Elder 	ipa->modem_netdev = netdev;
31310cc73c4SAlex Elder 
31410cc73c4SAlex Elder 	ret = register_netdev(netdev);
31510cc73c4SAlex Elder 	if (ret) {
31610cc73c4SAlex Elder 		ipa->modem_netdev = NULL;
31710cc73c4SAlex Elder 		ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]->netdev = NULL;
31810cc73c4SAlex Elder 		ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]->netdev = NULL;
31957f63fafSAlex Elder 		free_netdev(netdev);
32057f63fafSAlex Elder 	}
321a646d6ecSAlex Elder 
322a646d6ecSAlex Elder out_set_state:
323a646d6ecSAlex Elder 	if (ret)
324a646d6ecSAlex Elder 		atomic_set(&ipa->modem_state, IPA_MODEM_STATE_STOPPED);
325a646d6ecSAlex Elder 	else
326a646d6ecSAlex Elder 		atomic_set(&ipa->modem_state, IPA_MODEM_STATE_RUNNING);
327a646d6ecSAlex Elder 	smp_mb__after_atomic();
328a646d6ecSAlex Elder 
329a646d6ecSAlex Elder 	return ret;
330a646d6ecSAlex Elder }
331a646d6ecSAlex Elder 
ipa_modem_stop(struct ipa * ipa)332a646d6ecSAlex Elder int ipa_modem_stop(struct ipa *ipa)
333a646d6ecSAlex Elder {
334a646d6ecSAlex Elder 	struct net_device *netdev = ipa->modem_netdev;
335a646d6ecSAlex Elder 	enum ipa_modem_state state;
336a646d6ecSAlex Elder 
337a646d6ecSAlex Elder 	/* Only attempt to stop the modem if it's running */
338a646d6ecSAlex Elder 	state = atomic_cmpxchg(&ipa->modem_state, IPA_MODEM_STATE_RUNNING,
339a646d6ecSAlex Elder 			       IPA_MODEM_STATE_STOPPING);
340a646d6ecSAlex Elder 
341a646d6ecSAlex Elder 	/* Silently ignore attempts when already stopped */
342a646d6ecSAlex Elder 	if (state == IPA_MODEM_STATE_STOPPED)
343a646d6ecSAlex Elder 		return 0;
344a646d6ecSAlex Elder 
345a646d6ecSAlex Elder 	/* If we're somewhere between stopped and starting, we're busy */
346a646d6ecSAlex Elder 	if (state != IPA_MODEM_STATE_RUNNING)
347a646d6ecSAlex Elder 		return -EBUSY;
348a646d6ecSAlex Elder 
34930c2515bSAlex Elder 	/* Clean up the netdev and endpoints if it was started */
350077e770fSAlex Elder 	if (netdev) {
351a96e73faSAlex Elder 		struct ipa_priv *priv = netdev_priv(netdev);
352a96e73faSAlex Elder 
353a96e73faSAlex Elder 		cancel_work_sync(&priv->work);
35430c2515bSAlex Elder 		/* If it was opened, stop it first */
35530c2515bSAlex Elder 		if (netdev->flags & IFF_UP)
356077e770fSAlex Elder 			(void)ipa_stop(netdev);
35710cc73c4SAlex Elder 		unregister_netdev(netdev);
35810cc73c4SAlex Elder 		ipa->modem_netdev = NULL;
35957f63fafSAlex Elder 		ipa->name_map[IPA_ENDPOINT_AP_MODEM_RX]->netdev = NULL;
36057f63fafSAlex Elder 		ipa->name_map[IPA_ENDPOINT_AP_MODEM_TX]->netdev = NULL;
361a646d6ecSAlex Elder 		free_netdev(netdev);
362a646d6ecSAlex Elder 	}
363a646d6ecSAlex Elder 
364a646d6ecSAlex Elder 	atomic_set(&ipa->modem_state, IPA_MODEM_STATE_STOPPED);
365a646d6ecSAlex Elder 	smp_mb__after_atomic();
366a646d6ecSAlex Elder 
367077e770fSAlex Elder 	return 0;
368a646d6ecSAlex Elder }
369a646d6ecSAlex Elder 
370a646d6ecSAlex Elder /* Treat a "clean" modem stop the same as a crash */
ipa_modem_crashed(struct ipa * ipa)371a646d6ecSAlex Elder static void ipa_modem_crashed(struct ipa *ipa)
372a646d6ecSAlex Elder {
373a646d6ecSAlex Elder 	struct device *dev = &ipa->pdev->dev;
374a646d6ecSAlex Elder 	int ret;
375a646d6ecSAlex Elder 
3768afc7e47SAlex Elder 	/* Prevent the modem from triggering a call to ipa_setup() */
3778afc7e47SAlex Elder 	ipa_smp2p_irq_disable_setup(ipa);
3788afc7e47SAlex Elder 
379724c2d74SAlex Elder 	ret = pm_runtime_get_sync(dev);
380724c2d74SAlex Elder 	if (ret < 0) {
381724c2d74SAlex Elder 		dev_err(dev, "error %d getting power to handle crash\n", ret);
382724c2d74SAlex Elder 		goto out_power_put;
383724c2d74SAlex Elder 	}
38434c6034bSAlex Elder 
385a646d6ecSAlex Elder 	ipa_endpoint_modem_pause_all(ipa, true);
386a646d6ecSAlex Elder 
387a646d6ecSAlex Elder 	ipa_endpoint_modem_hol_block_clear_all(ipa);
388a646d6ecSAlex Elder 
389a646d6ecSAlex Elder 	ipa_table_reset(ipa, true);
390a646d6ecSAlex Elder 
391a646d6ecSAlex Elder 	ret = ipa_table_hash_flush(ipa);
392a646d6ecSAlex Elder 	if (ret)
393dc3e19f4SColin Ian King 		dev_err(dev, "error %d flushing hash caches\n", ret);
394a646d6ecSAlex Elder 
395a646d6ecSAlex Elder 	ret = ipa_endpoint_modem_exception_reset_all(ipa);
396a646d6ecSAlex Elder 	if (ret)
3972ba53898SChristophe JAILLET 		dev_err(dev, "error %d resetting exception endpoint\n", ret);
398a646d6ecSAlex Elder 
399a646d6ecSAlex Elder 	ipa_endpoint_modem_pause_all(ipa, false);
400a646d6ecSAlex Elder 
401a646d6ecSAlex Elder 	ret = ipa_modem_stop(ipa);
402a646d6ecSAlex Elder 	if (ret)
4032ba53898SChristophe JAILLET 		dev_err(dev, "error %d stopping modem\n", ret);
404a646d6ecSAlex Elder 
405a646d6ecSAlex Elder 	/* Now prepare for the next modem boot */
406a646d6ecSAlex Elder 	ret = ipa_mem_zero_modem(ipa);
407a646d6ecSAlex Elder 	if (ret)
408a646d6ecSAlex Elder 		dev_err(dev, "error %d zeroing modem memory regions\n", ret);
40934c6034bSAlex Elder 
410724c2d74SAlex Elder out_power_put:
4111aac309dSAlex Elder 	pm_runtime_mark_last_busy(dev);
4121aac309dSAlex Elder 	(void)pm_runtime_put_autosuspend(dev);
413a646d6ecSAlex Elder }
414a646d6ecSAlex Elder 
ipa_modem_notify(struct notifier_block * nb,unsigned long action,void * data)41530eb3fbeSAlex Elder static int ipa_modem_notify(struct notifier_block *nb, unsigned long action,
41630eb3fbeSAlex Elder 			    void *data)
417a646d6ecSAlex Elder {
41830eb3fbeSAlex Elder 	struct ipa *ipa = container_of(nb, struct ipa, nb);
41930eb3fbeSAlex Elder 	struct qcom_ssr_notify_data *notify_data = data;
42030eb3fbeSAlex Elder 	struct device *dev = &ipa->pdev->dev;
421a646d6ecSAlex Elder 
42230eb3fbeSAlex Elder 	switch (action) {
42330eb3fbeSAlex Elder 	case QCOM_SSR_BEFORE_POWERUP:
424a646d6ecSAlex Elder 		dev_info(dev, "received modem starting event\n");
4257aa0e8b8SAlex Elder 		ipa_uc_power(ipa);
426a646d6ecSAlex Elder 		ipa_smp2p_notify_reset(ipa);
427a646d6ecSAlex Elder 		break;
428a646d6ecSAlex Elder 
42930eb3fbeSAlex Elder 	case QCOM_SSR_AFTER_POWERUP:
430a646d6ecSAlex Elder 		dev_info(dev, "received modem running event\n");
431a646d6ecSAlex Elder 		break;
432a646d6ecSAlex Elder 
43330eb3fbeSAlex Elder 	case QCOM_SSR_BEFORE_SHUTDOWN:
434a646d6ecSAlex Elder 		dev_info(dev, "received modem %s event\n",
43530eb3fbeSAlex Elder 			 notify_data->crashed ? "crashed" : "stopping");
436a646d6ecSAlex Elder 		if (ipa->setup_complete)
437a646d6ecSAlex Elder 			ipa_modem_crashed(ipa);
438a646d6ecSAlex Elder 		break;
439a646d6ecSAlex Elder 
44030eb3fbeSAlex Elder 	case QCOM_SSR_AFTER_SHUTDOWN:
441a646d6ecSAlex Elder 		dev_info(dev, "received modem offline event\n");
442a646d6ecSAlex Elder 		break;
443a646d6ecSAlex Elder 
444a646d6ecSAlex Elder 	default:
44530eb3fbeSAlex Elder 		dev_err(dev, "received unrecognized event %lu\n", action);
446a646d6ecSAlex Elder 		break;
447a646d6ecSAlex Elder 	}
44830eb3fbeSAlex Elder 
44930eb3fbeSAlex Elder 	return NOTIFY_OK;
450a646d6ecSAlex Elder }
451a646d6ecSAlex Elder 
ipa_modem_config(struct ipa * ipa)452a646d6ecSAlex Elder int ipa_modem_config(struct ipa *ipa)
453a646d6ecSAlex Elder {
45430eb3fbeSAlex Elder 	void *notifier;
45530eb3fbeSAlex Elder 
45630eb3fbeSAlex Elder 	ipa->nb.notifier_call = ipa_modem_notify;
45730eb3fbeSAlex Elder 
45830eb3fbeSAlex Elder 	notifier = qcom_register_ssr_notifier("mpss", &ipa->nb);
45930eb3fbeSAlex Elder 	if (IS_ERR(notifier))
46030eb3fbeSAlex Elder 		return PTR_ERR(notifier);
46130eb3fbeSAlex Elder 
46230eb3fbeSAlex Elder 	ipa->notifier = notifier;
46330eb3fbeSAlex Elder 
46430eb3fbeSAlex Elder 	return 0;
465a646d6ecSAlex Elder }
466a646d6ecSAlex Elder 
ipa_modem_deconfig(struct ipa * ipa)467a646d6ecSAlex Elder void ipa_modem_deconfig(struct ipa *ipa)
468a646d6ecSAlex Elder {
46930eb3fbeSAlex Elder 	struct device *dev = &ipa->pdev->dev;
47030eb3fbeSAlex Elder 	int ret;
47130eb3fbeSAlex Elder 
47230eb3fbeSAlex Elder 	ret = qcom_unregister_ssr_notifier(ipa->notifier, &ipa->nb);
47330eb3fbeSAlex Elder 	if (ret)
47430eb3fbeSAlex Elder 		dev_err(dev, "error %d unregistering notifier", ret);
47530eb3fbeSAlex Elder 
47630eb3fbeSAlex Elder 	ipa->notifier = NULL;
47730eb3fbeSAlex Elder 	memset(&ipa->nb, 0, sizeof(ipa->nb));
478a646d6ecSAlex Elder }
479