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