1// SPDX-License-Identifier: GPL-2.0-only
2/*
3 * Copyright (c) 2021, MediaTek Inc.
4 * Copyright (c) 2021-2022, Intel Corporation.
5 * Copyright (c) 2024, Fibocom Wireless Inc.
6 *
7 * Authors:
8 *  Amir Hanania <amir.hanania@intel.com>
9 *  Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
10 *  Haijun Liu <haijun.liu@mediatek.com>
11 *  Moises Veleta <moises.veleta@intel.com>
12 *  Ricardo Martinez <ricardo.martinez@linux.intel.com>
13 *
14 * Contributors:
15 *  Andy Shevchenko <andriy.shevchenko@linux.intel.com>
16 *  Chiranjeevi Rapolu <chiranjeevi.rapolu@intel.com>
17 *  Eliot Lee <eliot.lee@intel.com>
18 *  Sreehari Kancharla <sreehari.kancharla@intel.com>
19 *  Jinjian Song <jinjian.song@fibocom.com>
20 */
21
22#include <linux/atomic.h>
23#include <linux/bitfield.h>
24#include <linux/dev_printk.h>
25#include <linux/err.h>
26#include <linux/gfp.h>
27#include <linux/minmax.h>
28#include <linux/netdevice.h>
29#include <linux/skbuff.h>
30#include <linux/spinlock.h>
31#include <linux/string.h>
32#include <linux/wwan.h>
33
34#include "t7xx_port.h"
35#include "t7xx_port_proxy.h"
36#include "t7xx_state_monitor.h"
37
38static int t7xx_port_wwan_start(struct wwan_port *port)
39{
40	struct t7xx_port *port_mtk = wwan_port_get_drvdata(port);
41
42	if (atomic_read(&port_mtk->usage_cnt))
43		return -EBUSY;
44
45	atomic_inc(&port_mtk->usage_cnt);
46	return 0;
47}
48
49static void t7xx_port_wwan_stop(struct wwan_port *port)
50{
51	struct t7xx_port *port_mtk = wwan_port_get_drvdata(port);
52
53	atomic_dec(&port_mtk->usage_cnt);
54}
55
56static int t7xx_port_fastboot_tx(struct t7xx_port *port, struct sk_buff *skb)
57{
58	struct sk_buff *cur = skb, *tx_skb;
59	size_t actual, len, offset = 0;
60	int txq_mtu;
61	int ret;
62
63	txq_mtu = t7xx_get_port_mtu(port);
64	if (txq_mtu < 0)
65		return -EINVAL;
66
67	actual = cur->len;
68	while (actual) {
69		len = min_t(size_t, actual, txq_mtu);
70		tx_skb = __dev_alloc_skb(len, GFP_KERNEL);
71		if (!tx_skb)
72			return -ENOMEM;
73
74		skb_put_data(tx_skb, cur->data + offset, len);
75
76		ret = t7xx_port_send_raw_skb(port, tx_skb);
77		if (ret) {
78			dev_kfree_skb(tx_skb);
79			dev_err(port->dev, "Write error on fastboot port, %d\n", ret);
80			break;
81		}
82		offset += len;
83		actual -= len;
84	}
85
86	dev_kfree_skb(skb);
87	return 0;
88}
89
90static int t7xx_port_ctrl_tx(struct t7xx_port *port, struct sk_buff *skb)
91{
92	const struct t7xx_port_conf *port_conf;
93	struct sk_buff *cur = skb, *cloned;
94	struct t7xx_fsm_ctl *ctl;
95	enum md_state md_state;
96	int cnt = 0, ret;
97
98	port_conf = port->port_conf;
99	ctl = port->t7xx_dev->md->fsm_ctl;
100	md_state = t7xx_fsm_get_md_state(ctl);
101	if (md_state == MD_STATE_WAITING_FOR_HS1 || md_state == MD_STATE_WAITING_FOR_HS2) {
102		dev_warn(port->dev, "Cannot write to %s port when md_state=%d\n",
103			 port_conf->name, md_state);
104		return -ENODEV;
105	}
106
107	while (cur) {
108		cloned = skb_clone(cur, GFP_KERNEL);
109		cloned->len = skb_headlen(cur);
110		ret = t7xx_port_send_skb(port, cloned, 0, 0);
111		if (ret) {
112			dev_kfree_skb(cloned);
113			dev_err(port->dev, "Write error on %s port, %d\n",
114				port_conf->name, ret);
115			return cnt ? cnt + ret : ret;
116		}
117		cnt += cur->len;
118		if (cur == skb)
119			cur = skb_shinfo(skb)->frag_list;
120		else
121			cur = cur->next;
122	}
123
124	dev_kfree_skb(skb);
125	return 0;
126}
127
128static int t7xx_port_wwan_tx(struct wwan_port *port, struct sk_buff *skb)
129{
130	struct t7xx_port *port_private = wwan_port_get_drvdata(port);
131	const struct t7xx_port_conf *port_conf = port_private->port_conf;
132	int ret;
133
134	if (!port_private->chan_enable)
135		return -EINVAL;
136
137	if (port_conf->port_type != WWAN_PORT_FASTBOOT)
138		ret = t7xx_port_ctrl_tx(port_private, skb);
139	else
140		ret = t7xx_port_fastboot_tx(port_private, skb);
141
142	return ret;
143}
144
145static const struct wwan_port_ops wwan_ops = {
146	.start = t7xx_port_wwan_start,
147	.stop = t7xx_port_wwan_stop,
148	.tx = t7xx_port_wwan_tx,
149};
150
151static void t7xx_port_wwan_create(struct t7xx_port *port)
152{
153	const struct t7xx_port_conf *port_conf = port->port_conf;
154	unsigned int header_len = sizeof(struct ccci_header), mtu;
155	struct wwan_port_caps caps;
156
157	if (!port->wwan.wwan_port) {
158		mtu = t7xx_get_port_mtu(port);
159		caps.frag_len = mtu - header_len;
160		caps.headroom_len = header_len;
161		port->wwan.wwan_port = wwan_create_port(port->dev, port_conf->port_type,
162							&wwan_ops, &caps, port);
163		if (IS_ERR(port->wwan.wwan_port))
164			dev_err(port->dev, "Unable to create WWAN port %s", port_conf->name);
165	}
166}
167
168static int t7xx_port_wwan_init(struct t7xx_port *port)
169{
170	const struct t7xx_port_conf *port_conf = port->port_conf;
171
172	if (port_conf->port_type == WWAN_PORT_FASTBOOT)
173		t7xx_port_wwan_create(port);
174
175	port->rx_length_th = RX_QUEUE_MAXLEN;
176	return 0;
177}
178
179static void t7xx_port_wwan_uninit(struct t7xx_port *port)
180{
181	if (!port->wwan.wwan_port)
182		return;
183
184	port->rx_length_th = 0;
185	wwan_remove_port(port->wwan.wwan_port);
186	port->wwan.wwan_port = NULL;
187}
188
189static int t7xx_port_wwan_recv_skb(struct t7xx_port *port, struct sk_buff *skb)
190{
191	if (!atomic_read(&port->usage_cnt) || !port->chan_enable) {
192		const struct t7xx_port_conf *port_conf = port->port_conf;
193
194		dev_kfree_skb_any(skb);
195		dev_err_ratelimited(port->dev, "Port %s is not opened, drop packets\n",
196				    port_conf->name);
197		/* Dropping skb, caller should not access skb.*/
198		return 0;
199	}
200
201	wwan_port_rx(port->wwan.wwan_port, skb);
202	return 0;
203}
204
205static int t7xx_port_wwan_enable_chl(struct t7xx_port *port)
206{
207	spin_lock(&port->port_update_lock);
208	port->chan_enable = true;
209	spin_unlock(&port->port_update_lock);
210
211	return 0;
212}
213
214static int t7xx_port_wwan_disable_chl(struct t7xx_port *port)
215{
216	spin_lock(&port->port_update_lock);
217	port->chan_enable = false;
218	spin_unlock(&port->port_update_lock);
219
220	return 0;
221}
222
223static void t7xx_port_wwan_md_state_notify(struct t7xx_port *port, unsigned int state)
224{
225	const struct t7xx_port_conf *port_conf = port->port_conf;
226
227	if (port_conf->port_type == WWAN_PORT_FASTBOOT)
228		return;
229
230	if (state != MD_STATE_READY)
231		return;
232
233	t7xx_port_wwan_create(port);
234}
235
236struct port_ops wwan_sub_port_ops = {
237	.init = t7xx_port_wwan_init,
238	.recv_skb = t7xx_port_wwan_recv_skb,
239	.uninit = t7xx_port_wwan_uninit,
240	.enable_chl = t7xx_port_wwan_enable_chl,
241	.disable_chl = t7xx_port_wwan_disable_chl,
242	.md_state_notify = t7xx_port_wwan_md_state_notify,
243};
244