GNU Linux-libre 6.8.9-gnu
[releases.git] / drivers / net / wwan / t7xx / t7xx_port_wwan.c
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (c) 2021, MediaTek Inc.
4  * Copyright (c) 2021-2022, Intel Corporation.
5  *
6  * Authors:
7  *  Amir Hanania <amir.hanania@intel.com>
8  *  Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
9  *  Haijun Liu <haijun.liu@mediatek.com>
10  *  Moises Veleta <moises.veleta@intel.com>
11  *  Ricardo Martinez <ricardo.martinez@linux.intel.com>
12  *
13  * Contributors:
14  *  Andy Shevchenko <andriy.shevchenko@linux.intel.com>
15  *  Chiranjeevi Rapolu <chiranjeevi.rapolu@intel.com>
16  *  Eliot Lee <eliot.lee@intel.com>
17  *  Sreehari Kancharla <sreehari.kancharla@intel.com>
18  */
19
20 #include <linux/atomic.h>
21 #include <linux/bitfield.h>
22 #include <linux/dev_printk.h>
23 #include <linux/err.h>
24 #include <linux/gfp.h>
25 #include <linux/minmax.h>
26 #include <linux/netdevice.h>
27 #include <linux/skbuff.h>
28 #include <linux/spinlock.h>
29 #include <linux/string.h>
30 #include <linux/wwan.h>
31
32 #include "t7xx_port.h"
33 #include "t7xx_port_proxy.h"
34 #include "t7xx_state_monitor.h"
35
36 static int t7xx_port_ctrl_start(struct wwan_port *port)
37 {
38         struct t7xx_port *port_mtk = wwan_port_get_drvdata(port);
39
40         if (atomic_read(&port_mtk->usage_cnt))
41                 return -EBUSY;
42
43         atomic_inc(&port_mtk->usage_cnt);
44         return 0;
45 }
46
47 static void t7xx_port_ctrl_stop(struct wwan_port *port)
48 {
49         struct t7xx_port *port_mtk = wwan_port_get_drvdata(port);
50
51         atomic_dec(&port_mtk->usage_cnt);
52 }
53
54 static int t7xx_port_ctrl_tx(struct wwan_port *port, struct sk_buff *skb)
55 {
56         struct t7xx_port *port_private = wwan_port_get_drvdata(port);
57         const struct t7xx_port_conf *port_conf;
58         struct sk_buff *cur = skb, *cloned;
59         struct t7xx_fsm_ctl *ctl;
60         enum md_state md_state;
61         int cnt = 0, ret;
62
63         if (!port_private->chan_enable)
64                 return -EINVAL;
65
66         port_conf = port_private->port_conf;
67         ctl = port_private->t7xx_dev->md->fsm_ctl;
68         md_state = t7xx_fsm_get_md_state(ctl);
69         if (md_state == MD_STATE_WAITING_FOR_HS1 || md_state == MD_STATE_WAITING_FOR_HS2) {
70                 dev_warn(port_private->dev, "Cannot write to %s port when md_state=%d\n",
71                          port_conf->name, md_state);
72                 return -ENODEV;
73         }
74
75         while (cur) {
76                 cloned = skb_clone(cur, GFP_KERNEL);
77                 cloned->len = skb_headlen(cur);
78                 ret = t7xx_port_send_skb(port_private, cloned, 0, 0);
79                 if (ret) {
80                         dev_kfree_skb(cloned);
81                         dev_err(port_private->dev, "Write error on %s port, %d\n",
82                                 port_conf->name, ret);
83                         return cnt ? cnt + ret : ret;
84                 }
85                 cnt += cur->len;
86                 if (cur == skb)
87                         cur = skb_shinfo(skb)->frag_list;
88                 else
89                         cur = cur->next;
90         }
91
92         dev_kfree_skb(skb);
93         return 0;
94 }
95
96 static const struct wwan_port_ops wwan_ops = {
97         .start = t7xx_port_ctrl_start,
98         .stop = t7xx_port_ctrl_stop,
99         .tx = t7xx_port_ctrl_tx,
100 };
101
102 static int t7xx_port_wwan_init(struct t7xx_port *port)
103 {
104         port->rx_length_th = RX_QUEUE_MAXLEN;
105         return 0;
106 }
107
108 static void t7xx_port_wwan_uninit(struct t7xx_port *port)
109 {
110         if (!port->wwan.wwan_port)
111                 return;
112
113         port->rx_length_th = 0;
114         wwan_remove_port(port->wwan.wwan_port);
115         port->wwan.wwan_port = NULL;
116 }
117
118 static int t7xx_port_wwan_recv_skb(struct t7xx_port *port, struct sk_buff *skb)
119 {
120         if (!atomic_read(&port->usage_cnt) || !port->chan_enable) {
121                 const struct t7xx_port_conf *port_conf = port->port_conf;
122
123                 dev_kfree_skb_any(skb);
124                 dev_err_ratelimited(port->dev, "Port %s is not opened, drop packets\n",
125                                     port_conf->name);
126                 /* Dropping skb, caller should not access skb.*/
127                 return 0;
128         }
129
130         wwan_port_rx(port->wwan.wwan_port, skb);
131         return 0;
132 }
133
134 static int t7xx_port_wwan_enable_chl(struct t7xx_port *port)
135 {
136         spin_lock(&port->port_update_lock);
137         port->chan_enable = true;
138         spin_unlock(&port->port_update_lock);
139
140         return 0;
141 }
142
143 static int t7xx_port_wwan_disable_chl(struct t7xx_port *port)
144 {
145         spin_lock(&port->port_update_lock);
146         port->chan_enable = false;
147         spin_unlock(&port->port_update_lock);
148
149         return 0;
150 }
151
152 static void t7xx_port_wwan_md_state_notify(struct t7xx_port *port, unsigned int state)
153 {
154         const struct t7xx_port_conf *port_conf = port->port_conf;
155         unsigned int header_len = sizeof(struct ccci_header);
156         struct wwan_port_caps caps;
157
158         if (state != MD_STATE_READY)
159                 return;
160
161         if (!port->wwan.wwan_port) {
162                 caps.frag_len = CLDMA_MTU - header_len;
163                 caps.headroom_len = header_len;
164                 port->wwan.wwan_port = wwan_create_port(port->dev, port_conf->port_type,
165                                                         &wwan_ops, &caps, port);
166                 if (IS_ERR(port->wwan.wwan_port))
167                         dev_err(port->dev, "Unable to create WWWAN port %s", port_conf->name);
168         }
169 }
170
171 struct port_ops wwan_sub_port_ops = {
172         .init = t7xx_port_wwan_init,
173         .recv_skb = t7xx_port_wwan_recv_skb,
174         .uninit = t7xx_port_wwan_uninit,
175         .enable_chl = t7xx_port_wwan_enable_chl,
176         .disable_chl = t7xx_port_wwan_disable_chl,
177         .md_state_notify = t7xx_port_wwan_md_state_notify,
178 };