xref: /linux/drivers/net/wwan/t7xx/t7xx_port_ctrl_msg.c (revision a23e1966932464e1c5226cb9ac4ce1d5fc10ba22)
1da45d256SHaijun Liu // SPDX-License-Identifier: GPL-2.0-only
2da45d256SHaijun Liu /*
3da45d256SHaijun Liu  * Copyright (c) 2021, MediaTek Inc.
4da45d256SHaijun Liu  * Copyright (c) 2021-2022, Intel Corporation.
5da45d256SHaijun Liu  *
6da45d256SHaijun Liu  * Authors:
7da45d256SHaijun Liu  *  Haijun Liu <haijun.liu@mediatek.com>
8da45d256SHaijun Liu  *  Ricardo Martinez <ricardo.martinez@linux.intel.com>
9da45d256SHaijun Liu  *  Moises Veleta <moises.veleta@intel.com>
10da45d256SHaijun Liu  *
11da45d256SHaijun Liu  * Contributors:
12da45d256SHaijun Liu  *  Amir Hanania <amir.hanania@intel.com>
13da45d256SHaijun Liu  *  Chiranjeevi Rapolu <chiranjeevi.rapolu@intel.com>
14da45d256SHaijun Liu  *  Eliot Lee <eliot.lee@intel.com>
15da45d256SHaijun Liu  *  Sreehari Kancharla <sreehari.kancharla@intel.com>
16da45d256SHaijun Liu  */
17da45d256SHaijun Liu 
18da45d256SHaijun Liu #include <linux/bitfield.h>
19da45d256SHaijun Liu #include <linux/device.h>
20da45d256SHaijun Liu #include <linux/err.h>
21da45d256SHaijun Liu #include <linux/kthread.h>
22da45d256SHaijun Liu #include <linux/netdevice.h>
23da45d256SHaijun Liu #include <linux/skbuff.h>
24da45d256SHaijun Liu #include <linux/spinlock.h>
25da45d256SHaijun Liu 
26da45d256SHaijun Liu #include "t7xx_port.h"
27da45d256SHaijun Liu #include "t7xx_port_proxy.h"
28da45d256SHaijun Liu #include "t7xx_state_monitor.h"
29da45d256SHaijun Liu 
30da45d256SHaijun Liu #define PORT_MSG_VERSION	GENMASK(31, 16)
31da45d256SHaijun Liu #define PORT_MSG_PRT_CNT	GENMASK(15, 0)
32da45d256SHaijun Liu 
33da45d256SHaijun Liu struct port_msg {
34da45d256SHaijun Liu 	__le32	head_pattern;
35da45d256SHaijun Liu 	__le32	info;
36da45d256SHaijun Liu 	__le32	tail_pattern;
37da45d256SHaijun Liu 	__le32	data[];
38da45d256SHaijun Liu };
39da45d256SHaijun Liu 
port_ctl_send_msg_to_md(struct t7xx_port * port,unsigned int msg,unsigned int ex_msg)40da45d256SHaijun Liu static int port_ctl_send_msg_to_md(struct t7xx_port *port, unsigned int msg, unsigned int ex_msg)
41da45d256SHaijun Liu {
42da45d256SHaijun Liu 	struct sk_buff *skb;
43da45d256SHaijun Liu 	int ret;
44da45d256SHaijun Liu 
45da45d256SHaijun Liu 	skb = t7xx_ctrl_alloc_skb(0);
46da45d256SHaijun Liu 	if (!skb)
47da45d256SHaijun Liu 		return -ENOMEM;
48da45d256SHaijun Liu 
49da45d256SHaijun Liu 	ret = t7xx_port_send_ctl_skb(port, skb, msg, ex_msg);
50da45d256SHaijun Liu 	if (ret)
51da45d256SHaijun Liu 		dev_kfree_skb_any(skb);
52da45d256SHaijun Liu 
53da45d256SHaijun Liu 	return ret;
54da45d256SHaijun Liu }
55da45d256SHaijun Liu 
fsm_ee_message_handler(struct t7xx_port * port,struct t7xx_fsm_ctl * ctl,struct sk_buff * skb)56da45d256SHaijun Liu static int fsm_ee_message_handler(struct t7xx_port *port, struct t7xx_fsm_ctl *ctl,
57da45d256SHaijun Liu 				  struct sk_buff *skb)
58da45d256SHaijun Liu {
59da45d256SHaijun Liu 	struct ctrl_msg_header *ctrl_msg_h = (struct ctrl_msg_header *)skb->data;
60da45d256SHaijun Liu 	struct device *dev = &ctl->md->t7xx_dev->pdev->dev;
61da45d256SHaijun Liu 	enum md_state md_state;
62da45d256SHaijun Liu 	int ret = -EINVAL;
63da45d256SHaijun Liu 
64da45d256SHaijun Liu 	md_state = t7xx_fsm_get_md_state(ctl);
65da45d256SHaijun Liu 	if (md_state != MD_STATE_EXCEPTION) {
66da45d256SHaijun Liu 		dev_err(dev, "Receive invalid MD_EX %x when MD state is %d\n",
67da45d256SHaijun Liu 			ctrl_msg_h->ex_msg, md_state);
68da45d256SHaijun Liu 		return -EINVAL;
69da45d256SHaijun Liu 	}
70da45d256SHaijun Liu 
71da45d256SHaijun Liu 	switch (le32_to_cpu(ctrl_msg_h->ctrl_msg_id)) {
72da45d256SHaijun Liu 	case CTL_ID_MD_EX:
73da45d256SHaijun Liu 		if (le32_to_cpu(ctrl_msg_h->ex_msg) != MD_EX_CHK_ID) {
74da45d256SHaijun Liu 			dev_err(dev, "Receive invalid MD_EX %x\n", ctrl_msg_h->ex_msg);
75da45d256SHaijun Liu 			break;
76da45d256SHaijun Liu 		}
77da45d256SHaijun Liu 
78da45d256SHaijun Liu 		ret = port_ctl_send_msg_to_md(port, CTL_ID_MD_EX, MD_EX_CHK_ID);
79da45d256SHaijun Liu 		if (ret) {
80da45d256SHaijun Liu 			dev_err(dev, "Failed to send exception message to modem\n");
81da45d256SHaijun Liu 			break;
82da45d256SHaijun Liu 		}
83da45d256SHaijun Liu 
84da45d256SHaijun Liu 		ret = t7xx_fsm_append_event(ctl, FSM_EVENT_MD_EX, NULL, 0);
85da45d256SHaijun Liu 		if (ret)
86da45d256SHaijun Liu 			dev_err(dev, "Failed to append Modem Exception event");
87da45d256SHaijun Liu 
88da45d256SHaijun Liu 		break;
89da45d256SHaijun Liu 
90da45d256SHaijun Liu 	case CTL_ID_MD_EX_ACK:
91da45d256SHaijun Liu 		if (le32_to_cpu(ctrl_msg_h->ex_msg) != MD_EX_CHK_ACK_ID) {
92da45d256SHaijun Liu 			dev_err(dev, "Receive invalid MD_EX_ACK %x\n", ctrl_msg_h->ex_msg);
93da45d256SHaijun Liu 			break;
94da45d256SHaijun Liu 		}
95da45d256SHaijun Liu 
96da45d256SHaijun Liu 		ret = t7xx_fsm_append_event(ctl, FSM_EVENT_MD_EX_REC_OK, NULL, 0);
97da45d256SHaijun Liu 		if (ret)
98da45d256SHaijun Liu 			dev_err(dev, "Failed to append Modem Exception Received event");
99da45d256SHaijun Liu 
100da45d256SHaijun Liu 		break;
101da45d256SHaijun Liu 
102da45d256SHaijun Liu 	case CTL_ID_MD_EX_PASS:
103da45d256SHaijun Liu 		ret = t7xx_fsm_append_event(ctl, FSM_EVENT_MD_EX_PASS, NULL, 0);
104da45d256SHaijun Liu 		if (ret)
105da45d256SHaijun Liu 			dev_err(dev, "Failed to append Modem Exception Passed event");
106da45d256SHaijun Liu 
107da45d256SHaijun Liu 		break;
108da45d256SHaijun Liu 
109da45d256SHaijun Liu 	case CTL_ID_DRV_VER_ERROR:
110da45d256SHaijun Liu 		dev_err(dev, "AP/MD driver version mismatch\n");
111da45d256SHaijun Liu 	}
112da45d256SHaijun Liu 
113da45d256SHaijun Liu 	return ret;
114da45d256SHaijun Liu }
115da45d256SHaijun Liu 
116da45d256SHaijun Liu /**
117da45d256SHaijun Liu  * t7xx_port_enum_msg_handler() - Parse the port enumeration message to create/remove nodes.
118da45d256SHaijun Liu  * @md: Modem context.
119da45d256SHaijun Liu  * @msg: Message.
120da45d256SHaijun Liu  *
121da45d256SHaijun Liu  * Used to control create/remove device node.
122da45d256SHaijun Liu  *
123da45d256SHaijun Liu  * Return:
124da45d256SHaijun Liu  * * 0		- Success.
125da45d256SHaijun Liu  * * -EFAULT	- Message check failure.
126da45d256SHaijun Liu  */
t7xx_port_enum_msg_handler(struct t7xx_modem * md,void * msg)127da45d256SHaijun Liu int t7xx_port_enum_msg_handler(struct t7xx_modem *md, void *msg)
128da45d256SHaijun Liu {
129da45d256SHaijun Liu 	struct device *dev = &md->t7xx_dev->pdev->dev;
130da45d256SHaijun Liu 	unsigned int version, port_count, i;
131da45d256SHaijun Liu 	struct port_msg *port_msg = msg;
132da45d256SHaijun Liu 
133da45d256SHaijun Liu 	version = FIELD_GET(PORT_MSG_VERSION, le32_to_cpu(port_msg->info));
134da45d256SHaijun Liu 	if (version != PORT_ENUM_VER ||
135da45d256SHaijun Liu 	    le32_to_cpu(port_msg->head_pattern) != PORT_ENUM_HEAD_PATTERN ||
136da45d256SHaijun Liu 	    le32_to_cpu(port_msg->tail_pattern) != PORT_ENUM_TAIL_PATTERN) {
137da45d256SHaijun Liu 		dev_err(dev, "Invalid port control message %x:%x:%x\n",
138da45d256SHaijun Liu 			version, le32_to_cpu(port_msg->head_pattern),
139da45d256SHaijun Liu 			le32_to_cpu(port_msg->tail_pattern));
140da45d256SHaijun Liu 		return -EFAULT;
141da45d256SHaijun Liu 	}
142da45d256SHaijun Liu 
143da45d256SHaijun Liu 	port_count = FIELD_GET(PORT_MSG_PRT_CNT, le32_to_cpu(port_msg->info));
144da45d256SHaijun Liu 	for (i = 0; i < port_count; i++) {
145da45d256SHaijun Liu 		u32 port_info = le32_to_cpu(port_msg->data[i]);
146da45d256SHaijun Liu 		unsigned int ch_id;
147da45d256SHaijun Liu 		bool en_flag;
148da45d256SHaijun Liu 
149da45d256SHaijun Liu 		ch_id = FIELD_GET(PORT_INFO_CH_ID, port_info);
150da45d256SHaijun Liu 		en_flag = port_info & PORT_INFO_ENFLG;
151da45d256SHaijun Liu 		if (t7xx_port_proxy_chl_enable_disable(md->port_prox, ch_id, en_flag))
152da45d256SHaijun Liu 			dev_dbg(dev, "Port:%x not found\n", ch_id);
153da45d256SHaijun Liu 	}
154da45d256SHaijun Liu 
155da45d256SHaijun Liu 	return 0;
156da45d256SHaijun Liu }
157da45d256SHaijun Liu 
control_msg_handler(struct t7xx_port * port,struct sk_buff * skb)158da45d256SHaijun Liu static int control_msg_handler(struct t7xx_port *port, struct sk_buff *skb)
159da45d256SHaijun Liu {
160da45d256SHaijun Liu 	const struct t7xx_port_conf *port_conf = port->port_conf;
161da45d256SHaijun Liu 	struct t7xx_fsm_ctl *ctl = port->t7xx_dev->md->fsm_ctl;
162da45d256SHaijun Liu 	struct ctrl_msg_header *ctrl_msg_h;
163da45d256SHaijun Liu 	int ret = 0;
164da45d256SHaijun Liu 
165da45d256SHaijun Liu 	ctrl_msg_h = (struct ctrl_msg_header *)skb->data;
166da45d256SHaijun Liu 	switch (le32_to_cpu(ctrl_msg_h->ctrl_msg_id)) {
167da45d256SHaijun Liu 	case CTL_ID_HS2_MSG:
168da45d256SHaijun Liu 		skb_pull(skb, sizeof(*ctrl_msg_h));
169da45d256SHaijun Liu 
170ba2274dcSJose Ignacio Tornos Martinez 		if (port_conf->rx_ch == PORT_CH_CONTROL_RX ||
171ba2274dcSJose Ignacio Tornos Martinez 		    port_conf->rx_ch == PORT_CH_AP_CONTROL_RX) {
172ba2274dcSJose Ignacio Tornos Martinez 			int event = port_conf->rx_ch == PORT_CH_CONTROL_RX ?
173ba2274dcSJose Ignacio Tornos Martinez 				    FSM_EVENT_MD_HS2 : FSM_EVENT_AP_HS2;
174ba2274dcSJose Ignacio Tornos Martinez 
175ba2274dcSJose Ignacio Tornos Martinez 			ret = t7xx_fsm_append_event(ctl, event, skb->data,
176da45d256SHaijun Liu 						    le32_to_cpu(ctrl_msg_h->data_length));
177da45d256SHaijun Liu 			if (ret)
178da45d256SHaijun Liu 				dev_err(port->dev, "Failed to append Handshake 2 event");
179da45d256SHaijun Liu 		}
180da45d256SHaijun Liu 
181da45d256SHaijun Liu 		dev_kfree_skb_any(skb);
182da45d256SHaijun Liu 		break;
183da45d256SHaijun Liu 
184da45d256SHaijun Liu 	case CTL_ID_MD_EX:
185da45d256SHaijun Liu 	case CTL_ID_MD_EX_ACK:
186da45d256SHaijun Liu 	case CTL_ID_MD_EX_PASS:
187da45d256SHaijun Liu 	case CTL_ID_DRV_VER_ERROR:
188da45d256SHaijun Liu 		ret = fsm_ee_message_handler(port, ctl, skb);
189da45d256SHaijun Liu 		dev_kfree_skb_any(skb);
190da45d256SHaijun Liu 		break;
191da45d256SHaijun Liu 
192da45d256SHaijun Liu 	case CTL_ID_PORT_ENUM:
193da45d256SHaijun Liu 		skb_pull(skb, sizeof(*ctrl_msg_h));
194da45d256SHaijun Liu 		ret = t7xx_port_enum_msg_handler(ctl->md, (struct port_msg *)skb->data);
195da45d256SHaijun Liu 		if (!ret)
196da45d256SHaijun Liu 			ret = port_ctl_send_msg_to_md(port, CTL_ID_PORT_ENUM, 0);
197da45d256SHaijun Liu 		else
198da45d256SHaijun Liu 			ret = port_ctl_send_msg_to_md(port, CTL_ID_PORT_ENUM,
199da45d256SHaijun Liu 						      PORT_ENUM_VER_MISMATCH);
200da45d256SHaijun Liu 
201da45d256SHaijun Liu 		break;
202da45d256SHaijun Liu 
203da45d256SHaijun Liu 	default:
204da45d256SHaijun Liu 		ret = -EINVAL;
205da45d256SHaijun Liu 		dev_err(port->dev, "Unknown control message ID to FSM %x\n",
206da45d256SHaijun Liu 			le32_to_cpu(ctrl_msg_h->ctrl_msg_id));
207da45d256SHaijun Liu 		break;
208da45d256SHaijun Liu 	}
209da45d256SHaijun Liu 
210da45d256SHaijun Liu 	if (ret)
211da45d256SHaijun Liu 		dev_err(port->dev, "%s control message handle error: %d\n", port_conf->name, ret);
212da45d256SHaijun Liu 
213da45d256SHaijun Liu 	return ret;
214da45d256SHaijun Liu }
215da45d256SHaijun Liu 
port_ctl_rx_thread(void * arg)216da45d256SHaijun Liu static int port_ctl_rx_thread(void *arg)
217da45d256SHaijun Liu {
218da45d256SHaijun Liu 	while (!kthread_should_stop()) {
219da45d256SHaijun Liu 		struct t7xx_port *port = arg;
220da45d256SHaijun Liu 		struct sk_buff *skb;
221da45d256SHaijun Liu 		unsigned long flags;
222da45d256SHaijun Liu 
223da45d256SHaijun Liu 		spin_lock_irqsave(&port->rx_wq.lock, flags);
224da45d256SHaijun Liu 		if (skb_queue_empty(&port->rx_skb_list) &&
225da45d256SHaijun Liu 		    wait_event_interruptible_locked_irq(port->rx_wq,
226da45d256SHaijun Liu 							!skb_queue_empty(&port->rx_skb_list) ||
227da45d256SHaijun Liu 							kthread_should_stop())) {
228da45d256SHaijun Liu 			spin_unlock_irqrestore(&port->rx_wq.lock, flags);
229da45d256SHaijun Liu 			continue;
230da45d256SHaijun Liu 		}
231da45d256SHaijun Liu 		if (kthread_should_stop()) {
232da45d256SHaijun Liu 			spin_unlock_irqrestore(&port->rx_wq.lock, flags);
233da45d256SHaijun Liu 			break;
234da45d256SHaijun Liu 		}
235da45d256SHaijun Liu 		skb = __skb_dequeue(&port->rx_skb_list);
236da45d256SHaijun Liu 		spin_unlock_irqrestore(&port->rx_wq.lock, flags);
237da45d256SHaijun Liu 
238da45d256SHaijun Liu 		control_msg_handler(port, skb);
239da45d256SHaijun Liu 	}
240da45d256SHaijun Liu 
241da45d256SHaijun Liu 	return 0;
242da45d256SHaijun Liu }
243da45d256SHaijun Liu 
port_ctl_init(struct t7xx_port * port)244da45d256SHaijun Liu static int port_ctl_init(struct t7xx_port *port)
245da45d256SHaijun Liu {
246da45d256SHaijun Liu 	const struct t7xx_port_conf *port_conf = port->port_conf;
247da45d256SHaijun Liu 
248da45d256SHaijun Liu 	port->thread = kthread_run(port_ctl_rx_thread, port, "%s", port_conf->name);
249da45d256SHaijun Liu 	if (IS_ERR(port->thread)) {
250da45d256SHaijun Liu 		dev_err(port->dev, "Failed to start port control thread\n");
251da45d256SHaijun Liu 		return PTR_ERR(port->thread);
252da45d256SHaijun Liu 	}
253da45d256SHaijun Liu 
254da45d256SHaijun Liu 	port->rx_length_th = CTRL_QUEUE_MAXLEN;
255da45d256SHaijun Liu 	return 0;
256da45d256SHaijun Liu }
257da45d256SHaijun Liu 
port_ctl_uninit(struct t7xx_port * port)258da45d256SHaijun Liu static void port_ctl_uninit(struct t7xx_port *port)
259da45d256SHaijun Liu {
260da45d256SHaijun Liu 	unsigned long flags;
261da45d256SHaijun Liu 	struct sk_buff *skb;
262da45d256SHaijun Liu 
263da45d256SHaijun Liu 	if (port->thread)
264da45d256SHaijun Liu 		kthread_stop(port->thread);
265da45d256SHaijun Liu 
266da45d256SHaijun Liu 	spin_lock_irqsave(&port->rx_wq.lock, flags);
267da45d256SHaijun Liu 	port->rx_length_th = 0;
268da45d256SHaijun Liu 	while ((skb = __skb_dequeue(&port->rx_skb_list)) != NULL)
269da45d256SHaijun Liu 		dev_kfree_skb_any(skb);
270da45d256SHaijun Liu 	spin_unlock_irqrestore(&port->rx_wq.lock, flags);
271da45d256SHaijun Liu }
272da45d256SHaijun Liu 
273da45d256SHaijun Liu struct port_ops ctl_port_ops = {
274da45d256SHaijun Liu 	.init = port_ctl_init,
275da45d256SHaijun Liu 	.recv_skb = t7xx_port_enqueue_skb,
276da45d256SHaijun Liu 	.uninit = port_ctl_uninit,
277da45d256SHaijun Liu };
278