148cc2f5eSHaijun Liu // SPDX-License-Identifier: GPL-2.0-only
248cc2f5eSHaijun Liu /*
348cc2f5eSHaijun Liu  * Copyright (c) 2021, MediaTek Inc.
448cc2f5eSHaijun Liu  * Copyright (c) 2021-2022, Intel Corporation.
548cc2f5eSHaijun Liu  *
648cc2f5eSHaijun Liu  * Authors:
748cc2f5eSHaijun Liu  *  Amir Hanania <amir.hanania@intel.com>
848cc2f5eSHaijun Liu  *  Haijun Liu <haijun.liu@mediatek.com>
948cc2f5eSHaijun Liu  *  Moises Veleta <moises.veleta@intel.com>
1048cc2f5eSHaijun Liu  *  Ricardo Martinez <ricardo.martinez@linux.intel.com>
1148cc2f5eSHaijun Liu  *
1248cc2f5eSHaijun Liu  * Contributors:
1348cc2f5eSHaijun Liu  *  Andy Shevchenko <andriy.shevchenko@linux.intel.com>
1448cc2f5eSHaijun Liu  *  Chandrashekar Devegowda <chandrashekar.devegowda@intel.com>
1548cc2f5eSHaijun Liu  *  Chiranjeevi Rapolu <chiranjeevi.rapolu@intel.com>
1648cc2f5eSHaijun Liu  *  Eliot Lee <eliot.lee@intel.com>
1748cc2f5eSHaijun Liu  *  Sreehari Kancharla <sreehari.kancharla@intel.com>
1848cc2f5eSHaijun Liu  */
1948cc2f5eSHaijun Liu 
2048cc2f5eSHaijun Liu #include <linux/bits.h>
2148cc2f5eSHaijun Liu #include <linux/bitfield.h>
2248cc2f5eSHaijun Liu #include <linux/device.h>
2348cc2f5eSHaijun Liu #include <linux/gfp.h>
2448cc2f5eSHaijun Liu #include <linux/kernel.h>
2548cc2f5eSHaijun Liu #include <linux/kthread.h>
2648cc2f5eSHaijun Liu #include <linux/list.h>
2748cc2f5eSHaijun Liu #include <linux/mutex.h>
2848cc2f5eSHaijun Liu #include <linux/netdevice.h>
2948cc2f5eSHaijun Liu #include <linux/skbuff.h>
3048cc2f5eSHaijun Liu #include <linux/spinlock.h>
3148cc2f5eSHaijun Liu #include <linux/wait.h>
3248cc2f5eSHaijun Liu #include <linux/wwan.h>
3348cc2f5eSHaijun Liu 
3448cc2f5eSHaijun Liu #include "t7xx_hif_cldma.h"
3548cc2f5eSHaijun Liu #include "t7xx_modem_ops.h"
3648cc2f5eSHaijun Liu #include "t7xx_port.h"
3748cc2f5eSHaijun Liu #include "t7xx_port_proxy.h"
3848cc2f5eSHaijun Liu #include "t7xx_state_monitor.h"
3948cc2f5eSHaijun Liu 
4048cc2f5eSHaijun Liu #define Q_IDX_CTRL			0
4148cc2f5eSHaijun Liu #define Q_IDX_MBIM			2
4248cc2f5eSHaijun Liu #define Q_IDX_AT_CMD			5
4348cc2f5eSHaijun Liu 
4448cc2f5eSHaijun Liu #define INVALID_SEQ_NUM			GENMASK(15, 0)
4548cc2f5eSHaijun Liu 
4648cc2f5eSHaijun Liu #define for_each_proxy_port(i, p, proxy)	\
4748cc2f5eSHaijun Liu 	for (i = 0, (p) = &(proxy)->ports[i];	\
4848cc2f5eSHaijun Liu 	     i < (proxy)->port_count;		\
4948cc2f5eSHaijun Liu 	     i++, (p) = &(proxy)->ports[i])
5048cc2f5eSHaijun Liu 
51*ba2274dcSJose Ignacio Tornos Martinez static const struct t7xx_port_conf t7xx_port_conf[] = {
52da45d256SHaijun Liu 	{
5361b7a291SChandrashekar Devegowda 		.tx_ch = PORT_CH_UART2_TX,
5461b7a291SChandrashekar Devegowda 		.rx_ch = PORT_CH_UART2_RX,
5561b7a291SChandrashekar Devegowda 		.txq_index = Q_IDX_AT_CMD,
5661b7a291SChandrashekar Devegowda 		.rxq_index = Q_IDX_AT_CMD,
5761b7a291SChandrashekar Devegowda 		.txq_exp_index = 0xff,
5861b7a291SChandrashekar Devegowda 		.rxq_exp_index = 0xff,
5961b7a291SChandrashekar Devegowda 		.path_id = CLDMA_ID_MD,
6061b7a291SChandrashekar Devegowda 		.ops = &wwan_sub_port_ops,
6161b7a291SChandrashekar Devegowda 		.name = "AT",
6261b7a291SChandrashekar Devegowda 		.port_type = WWAN_PORT_AT,
6361b7a291SChandrashekar Devegowda 	}, {
6461b7a291SChandrashekar Devegowda 		.tx_ch = PORT_CH_MBIM_TX,
6561b7a291SChandrashekar Devegowda 		.rx_ch = PORT_CH_MBIM_RX,
6661b7a291SChandrashekar Devegowda 		.txq_index = Q_IDX_MBIM,
6761b7a291SChandrashekar Devegowda 		.rxq_index = Q_IDX_MBIM,
6861b7a291SChandrashekar Devegowda 		.path_id = CLDMA_ID_MD,
6961b7a291SChandrashekar Devegowda 		.ops = &wwan_sub_port_ops,
7061b7a291SChandrashekar Devegowda 		.name = "MBIM",
7161b7a291SChandrashekar Devegowda 		.port_type = WWAN_PORT_MBIM,
7261b7a291SChandrashekar Devegowda 	}, {
733349e4a4SM Chetan Kumar #ifdef CONFIG_WWAN_DEBUGFS
743349e4a4SM Chetan Kumar 		.tx_ch = PORT_CH_MD_LOG_TX,
753349e4a4SM Chetan Kumar 		.rx_ch = PORT_CH_MD_LOG_RX,
763349e4a4SM Chetan Kumar 		.txq_index = 7,
773349e4a4SM Chetan Kumar 		.rxq_index = 7,
783349e4a4SM Chetan Kumar 		.txq_exp_index = 7,
793349e4a4SM Chetan Kumar 		.rxq_exp_index = 7,
803349e4a4SM Chetan Kumar 		.path_id = CLDMA_ID_MD,
813349e4a4SM Chetan Kumar 		.ops = &t7xx_trace_port_ops,
823349e4a4SM Chetan Kumar 		.name = "mdlog",
833349e4a4SM Chetan Kumar 	}, {
843349e4a4SM Chetan Kumar #endif
85da45d256SHaijun Liu 		.tx_ch = PORT_CH_CONTROL_TX,
86da45d256SHaijun Liu 		.rx_ch = PORT_CH_CONTROL_RX,
87da45d256SHaijun Liu 		.txq_index = Q_IDX_CTRL,
88da45d256SHaijun Liu 		.rxq_index = Q_IDX_CTRL,
89da45d256SHaijun Liu 		.path_id = CLDMA_ID_MD,
90da45d256SHaijun Liu 		.ops = &ctl_port_ops,
91da45d256SHaijun Liu 		.name = "t7xx_ctrl",
92*ba2274dcSJose Ignacio Tornos Martinez 	}, {
93*ba2274dcSJose Ignacio Tornos Martinez 		.tx_ch = PORT_CH_AP_CONTROL_TX,
94*ba2274dcSJose Ignacio Tornos Martinez 		.rx_ch = PORT_CH_AP_CONTROL_RX,
95*ba2274dcSJose Ignacio Tornos Martinez 		.txq_index = Q_IDX_CTRL,
96*ba2274dcSJose Ignacio Tornos Martinez 		.rxq_index = Q_IDX_CTRL,
97*ba2274dcSJose Ignacio Tornos Martinez 		.path_id = CLDMA_ID_AP,
98*ba2274dcSJose Ignacio Tornos Martinez 		.ops = &ctl_port_ops,
99*ba2274dcSJose Ignacio Tornos Martinez 		.name = "t7xx_ap_ctrl",
100da45d256SHaijun Liu 	},
10148cc2f5eSHaijun Liu };
10248cc2f5eSHaijun Liu 
t7xx_proxy_get_port_by_ch(struct port_proxy * port_prox,enum port_ch ch)10348cc2f5eSHaijun Liu static struct t7xx_port *t7xx_proxy_get_port_by_ch(struct port_proxy *port_prox, enum port_ch ch)
10448cc2f5eSHaijun Liu {
10548cc2f5eSHaijun Liu 	const struct t7xx_port_conf *port_conf;
10648cc2f5eSHaijun Liu 	struct t7xx_port *port;
10748cc2f5eSHaijun Liu 	int i;
10848cc2f5eSHaijun Liu 
10948cc2f5eSHaijun Liu 	for_each_proxy_port(i, port, port_prox) {
11048cc2f5eSHaijun Liu 		port_conf = port->port_conf;
11148cc2f5eSHaijun Liu 		if (port_conf->rx_ch == ch || port_conf->tx_ch == ch)
11248cc2f5eSHaijun Liu 			return port;
11348cc2f5eSHaijun Liu 	}
11448cc2f5eSHaijun Liu 
11548cc2f5eSHaijun Liu 	return NULL;
11648cc2f5eSHaijun Liu }
11748cc2f5eSHaijun Liu 
t7xx_port_next_rx_seq_num(struct t7xx_port * port,struct ccci_header * ccci_h)11848cc2f5eSHaijun Liu static u16 t7xx_port_next_rx_seq_num(struct t7xx_port *port, struct ccci_header *ccci_h)
11948cc2f5eSHaijun Liu {
12048cc2f5eSHaijun Liu 	u32 status = le32_to_cpu(ccci_h->status);
12148cc2f5eSHaijun Liu 	u16 seq_num, next_seq_num;
12248cc2f5eSHaijun Liu 	bool assert_bit;
12348cc2f5eSHaijun Liu 
12448cc2f5eSHaijun Liu 	seq_num = FIELD_GET(CCCI_H_SEQ_FLD, status);
12548cc2f5eSHaijun Liu 	next_seq_num = (seq_num + 1) & FIELD_MAX(CCCI_H_SEQ_FLD);
12648cc2f5eSHaijun Liu 	assert_bit = status & CCCI_H_AST_BIT;
12748cc2f5eSHaijun Liu 	if (!assert_bit || port->seq_nums[MTK_RX] == INVALID_SEQ_NUM)
12848cc2f5eSHaijun Liu 		return next_seq_num;
12948cc2f5eSHaijun Liu 
13048cc2f5eSHaijun Liu 	if (seq_num != port->seq_nums[MTK_RX])
13148cc2f5eSHaijun Liu 		dev_warn_ratelimited(port->dev,
13248cc2f5eSHaijun Liu 				     "seq num out-of-order %u != %u (header %X, len %X)\n",
13348cc2f5eSHaijun Liu 				     seq_num, port->seq_nums[MTK_RX],
13448cc2f5eSHaijun Liu 				     le32_to_cpu(ccci_h->packet_header),
13548cc2f5eSHaijun Liu 				     le32_to_cpu(ccci_h->packet_len));
13648cc2f5eSHaijun Liu 
13748cc2f5eSHaijun Liu 	return next_seq_num;
13848cc2f5eSHaijun Liu }
13948cc2f5eSHaijun Liu 
t7xx_port_proxy_reset(struct port_proxy * port_prox)14048cc2f5eSHaijun Liu void t7xx_port_proxy_reset(struct port_proxy *port_prox)
14148cc2f5eSHaijun Liu {
14248cc2f5eSHaijun Liu 	struct t7xx_port *port;
14348cc2f5eSHaijun Liu 	int i;
14448cc2f5eSHaijun Liu 
14548cc2f5eSHaijun Liu 	for_each_proxy_port(i, port, port_prox) {
14648cc2f5eSHaijun Liu 		port->seq_nums[MTK_RX] = INVALID_SEQ_NUM;
14748cc2f5eSHaijun Liu 		port->seq_nums[MTK_TX] = 0;
14848cc2f5eSHaijun Liu 	}
14948cc2f5eSHaijun Liu }
15048cc2f5eSHaijun Liu 
t7xx_port_get_queue_no(struct t7xx_port * port)15148cc2f5eSHaijun Liu static int t7xx_port_get_queue_no(struct t7xx_port *port)
15248cc2f5eSHaijun Liu {
15348cc2f5eSHaijun Liu 	const struct t7xx_port_conf *port_conf = port->port_conf;
15448cc2f5eSHaijun Liu 	struct t7xx_fsm_ctl *ctl = port->t7xx_dev->md->fsm_ctl;
15548cc2f5eSHaijun Liu 
15648cc2f5eSHaijun Liu 	return t7xx_fsm_get_md_state(ctl) == MD_STATE_EXCEPTION ?
15748cc2f5eSHaijun Liu 		port_conf->txq_exp_index : port_conf->txq_index;
15848cc2f5eSHaijun Liu }
15948cc2f5eSHaijun Liu 
t7xx_port_struct_init(struct t7xx_port * port)16048cc2f5eSHaijun Liu static void t7xx_port_struct_init(struct t7xx_port *port)
16148cc2f5eSHaijun Liu {
16248cc2f5eSHaijun Liu 	INIT_LIST_HEAD(&port->entry);
16348cc2f5eSHaijun Liu 	INIT_LIST_HEAD(&port->queue_entry);
16448cc2f5eSHaijun Liu 	skb_queue_head_init(&port->rx_skb_list);
16548cc2f5eSHaijun Liu 	init_waitqueue_head(&port->rx_wq);
16648cc2f5eSHaijun Liu 	port->seq_nums[MTK_RX] = INVALID_SEQ_NUM;
16748cc2f5eSHaijun Liu 	port->seq_nums[MTK_TX] = 0;
16848cc2f5eSHaijun Liu 	atomic_set(&port->usage_cnt, 0);
16948cc2f5eSHaijun Liu }
17048cc2f5eSHaijun Liu 
t7xx_port_alloc_skb(int payload)17148cc2f5eSHaijun Liu struct sk_buff *t7xx_port_alloc_skb(int payload)
17248cc2f5eSHaijun Liu {
17348cc2f5eSHaijun Liu 	struct sk_buff *skb = __dev_alloc_skb(payload + sizeof(struct ccci_header), GFP_KERNEL);
17448cc2f5eSHaijun Liu 
17548cc2f5eSHaijun Liu 	if (skb)
17648cc2f5eSHaijun Liu 		skb_reserve(skb, sizeof(struct ccci_header));
17748cc2f5eSHaijun Liu 
17848cc2f5eSHaijun Liu 	return skb;
17948cc2f5eSHaijun Liu }
18048cc2f5eSHaijun Liu 
t7xx_ctrl_alloc_skb(int payload)181da45d256SHaijun Liu struct sk_buff *t7xx_ctrl_alloc_skb(int payload)
182da45d256SHaijun Liu {
183da45d256SHaijun Liu 	struct sk_buff *skb = t7xx_port_alloc_skb(payload + sizeof(struct ctrl_msg_header));
184da45d256SHaijun Liu 
185da45d256SHaijun Liu 	if (skb)
186da45d256SHaijun Liu 		skb_reserve(skb, sizeof(struct ctrl_msg_header));
187da45d256SHaijun Liu 
188da45d256SHaijun Liu 	return skb;
189da45d256SHaijun Liu }
190da45d256SHaijun Liu 
19148cc2f5eSHaijun Liu /**
19248cc2f5eSHaijun Liu  * t7xx_port_enqueue_skb() - Enqueue the received skb into the port's rx_skb_list.
19348cc2f5eSHaijun Liu  * @port: port context.
19448cc2f5eSHaijun Liu  * @skb: received skb.
19548cc2f5eSHaijun Liu  *
19648cc2f5eSHaijun Liu  * Return:
19748cc2f5eSHaijun Liu  * * 0		- Success.
19848cc2f5eSHaijun Liu  * * -ENOBUFS	- Not enough buffer space. Caller will try again later, skb is not consumed.
19948cc2f5eSHaijun Liu  */
t7xx_port_enqueue_skb(struct t7xx_port * port,struct sk_buff * skb)20048cc2f5eSHaijun Liu int t7xx_port_enqueue_skb(struct t7xx_port *port, struct sk_buff *skb)
20148cc2f5eSHaijun Liu {
20248cc2f5eSHaijun Liu 	unsigned long flags;
20348cc2f5eSHaijun Liu 
20448cc2f5eSHaijun Liu 	spin_lock_irqsave(&port->rx_wq.lock, flags);
20548cc2f5eSHaijun Liu 	if (port->rx_skb_list.qlen >= port->rx_length_th) {
20648cc2f5eSHaijun Liu 		spin_unlock_irqrestore(&port->rx_wq.lock, flags);
20748cc2f5eSHaijun Liu 
20848cc2f5eSHaijun Liu 		return -ENOBUFS;
20948cc2f5eSHaijun Liu 	}
21048cc2f5eSHaijun Liu 	__skb_queue_tail(&port->rx_skb_list, skb);
21148cc2f5eSHaijun Liu 	spin_unlock_irqrestore(&port->rx_wq.lock, flags);
21248cc2f5eSHaijun Liu 
21348cc2f5eSHaijun Liu 	wake_up_all(&port->rx_wq);
21448cc2f5eSHaijun Liu 	return 0;
21548cc2f5eSHaijun Liu }
21648cc2f5eSHaijun Liu 
t7xx_port_send_raw_skb(struct t7xx_port * port,struct sk_buff * skb)21748cc2f5eSHaijun Liu static int t7xx_port_send_raw_skb(struct t7xx_port *port, struct sk_buff *skb)
21848cc2f5eSHaijun Liu {
21948cc2f5eSHaijun Liu 	enum cldma_id path_id = port->port_conf->path_id;
22048cc2f5eSHaijun Liu 	struct cldma_ctrl *md_ctrl;
22148cc2f5eSHaijun Liu 	int ret, tx_qno;
22248cc2f5eSHaijun Liu 
22348cc2f5eSHaijun Liu 	md_ctrl = port->t7xx_dev->md->md_ctrl[path_id];
22448cc2f5eSHaijun Liu 	tx_qno = t7xx_port_get_queue_no(port);
22548cc2f5eSHaijun Liu 	ret = t7xx_cldma_send_skb(md_ctrl, tx_qno, skb);
22648cc2f5eSHaijun Liu 	if (ret)
22748cc2f5eSHaijun Liu 		dev_err(port->dev, "Failed to send skb: %d\n", ret);
22848cc2f5eSHaijun Liu 
22948cc2f5eSHaijun Liu 	return ret;
23048cc2f5eSHaijun Liu }
23148cc2f5eSHaijun Liu 
t7xx_port_send_ccci_skb(struct t7xx_port * port,struct sk_buff * skb,unsigned int pkt_header,unsigned int ex_msg)23248cc2f5eSHaijun Liu static int t7xx_port_send_ccci_skb(struct t7xx_port *port, struct sk_buff *skb,
23348cc2f5eSHaijun Liu 				   unsigned int pkt_header, unsigned int ex_msg)
23448cc2f5eSHaijun Liu {
23548cc2f5eSHaijun Liu 	const struct t7xx_port_conf *port_conf = port->port_conf;
23648cc2f5eSHaijun Liu 	struct ccci_header *ccci_h;
23748cc2f5eSHaijun Liu 	u32 status;
23848cc2f5eSHaijun Liu 	int ret;
23948cc2f5eSHaijun Liu 
24048cc2f5eSHaijun Liu 	ccci_h = skb_push(skb, sizeof(*ccci_h));
24148cc2f5eSHaijun Liu 	status = FIELD_PREP(CCCI_H_CHN_FLD, port_conf->tx_ch) |
24248cc2f5eSHaijun Liu 		 FIELD_PREP(CCCI_H_SEQ_FLD, port->seq_nums[MTK_TX]) | CCCI_H_AST_BIT;
24348cc2f5eSHaijun Liu 	ccci_h->status = cpu_to_le32(status);
24448cc2f5eSHaijun Liu 	ccci_h->packet_header = cpu_to_le32(pkt_header);
24548cc2f5eSHaijun Liu 	ccci_h->packet_len = cpu_to_le32(skb->len);
24648cc2f5eSHaijun Liu 	ccci_h->ex_msg = cpu_to_le32(ex_msg);
24748cc2f5eSHaijun Liu 
24848cc2f5eSHaijun Liu 	ret = t7xx_port_send_raw_skb(port, skb);
24948cc2f5eSHaijun Liu 	if (ret)
25048cc2f5eSHaijun Liu 		return ret;
25148cc2f5eSHaijun Liu 
25248cc2f5eSHaijun Liu 	port->seq_nums[MTK_TX]++;
25348cc2f5eSHaijun Liu 	return 0;
25448cc2f5eSHaijun Liu }
25548cc2f5eSHaijun Liu 
t7xx_port_send_ctl_skb(struct t7xx_port * port,struct sk_buff * skb,unsigned int msg,unsigned int ex_msg)256da45d256SHaijun Liu int t7xx_port_send_ctl_skb(struct t7xx_port *port, struct sk_buff *skb, unsigned int msg,
257da45d256SHaijun Liu 			   unsigned int ex_msg)
258da45d256SHaijun Liu {
259da45d256SHaijun Liu 	struct ctrl_msg_header *ctrl_msg_h;
260da45d256SHaijun Liu 	unsigned int msg_len = skb->len;
261da45d256SHaijun Liu 	u32 pkt_header = 0;
262da45d256SHaijun Liu 
263da45d256SHaijun Liu 	ctrl_msg_h = skb_push(skb, sizeof(*ctrl_msg_h));
264da45d256SHaijun Liu 	ctrl_msg_h->ctrl_msg_id = cpu_to_le32(msg);
265da45d256SHaijun Liu 	ctrl_msg_h->ex_msg = cpu_to_le32(ex_msg);
266da45d256SHaijun Liu 	ctrl_msg_h->data_length = cpu_to_le32(msg_len);
267da45d256SHaijun Liu 
268da45d256SHaijun Liu 	if (!msg_len)
269da45d256SHaijun Liu 		pkt_header = CCCI_HEADER_NO_DATA;
270da45d256SHaijun Liu 
271da45d256SHaijun Liu 	return t7xx_port_send_ccci_skb(port, skb, pkt_header, ex_msg);
272da45d256SHaijun Liu }
273da45d256SHaijun Liu 
t7xx_port_send_skb(struct t7xx_port * port,struct sk_buff * skb,unsigned int pkt_header,unsigned int ex_msg)27448cc2f5eSHaijun Liu int t7xx_port_send_skb(struct t7xx_port *port, struct sk_buff *skb, unsigned int pkt_header,
27548cc2f5eSHaijun Liu 		       unsigned int ex_msg)
27648cc2f5eSHaijun Liu {
27748cc2f5eSHaijun Liu 	struct t7xx_fsm_ctl *ctl = port->t7xx_dev->md->fsm_ctl;
27848cc2f5eSHaijun Liu 	unsigned int fsm_state;
27948cc2f5eSHaijun Liu 
28048cc2f5eSHaijun Liu 	fsm_state = t7xx_fsm_get_ctl_state(ctl);
28148cc2f5eSHaijun Liu 	if (fsm_state != FSM_STATE_PRE_START) {
28248cc2f5eSHaijun Liu 		const struct t7xx_port_conf *port_conf = port->port_conf;
28348cc2f5eSHaijun Liu 		enum md_state md_state = t7xx_fsm_get_md_state(ctl);
28448cc2f5eSHaijun Liu 
28548cc2f5eSHaijun Liu 		switch (md_state) {
28648cc2f5eSHaijun Liu 		case MD_STATE_EXCEPTION:
28748cc2f5eSHaijun Liu 			if (port_conf->tx_ch != PORT_CH_MD_LOG_TX)
28848cc2f5eSHaijun Liu 				return -EBUSY;
28948cc2f5eSHaijun Liu 			break;
29048cc2f5eSHaijun Liu 
29148cc2f5eSHaijun Liu 		case MD_STATE_WAITING_FOR_HS1:
29248cc2f5eSHaijun Liu 		case MD_STATE_WAITING_FOR_HS2:
29348cc2f5eSHaijun Liu 		case MD_STATE_STOPPED:
29448cc2f5eSHaijun Liu 		case MD_STATE_WAITING_TO_STOP:
29548cc2f5eSHaijun Liu 		case MD_STATE_INVALID:
29648cc2f5eSHaijun Liu 			return -ENODEV;
29748cc2f5eSHaijun Liu 
29848cc2f5eSHaijun Liu 		default:
29948cc2f5eSHaijun Liu 			break;
30048cc2f5eSHaijun Liu 		}
30148cc2f5eSHaijun Liu 	}
30248cc2f5eSHaijun Liu 
30348cc2f5eSHaijun Liu 	return t7xx_port_send_ccci_skb(port, skb, pkt_header, ex_msg);
30448cc2f5eSHaijun Liu }
30548cc2f5eSHaijun Liu 
t7xx_proxy_setup_ch_mapping(struct port_proxy * port_prox)30648cc2f5eSHaijun Liu static void t7xx_proxy_setup_ch_mapping(struct port_proxy *port_prox)
30748cc2f5eSHaijun Liu {
30848cc2f5eSHaijun Liu 	struct t7xx_port *port;
30948cc2f5eSHaijun Liu 
31048cc2f5eSHaijun Liu 	int i, j;
31148cc2f5eSHaijun Liu 
31248cc2f5eSHaijun Liu 	for (i = 0; i < ARRAY_SIZE(port_prox->rx_ch_ports); i++)
31348cc2f5eSHaijun Liu 		INIT_LIST_HEAD(&port_prox->rx_ch_ports[i]);
31448cc2f5eSHaijun Liu 
31548cc2f5eSHaijun Liu 	for (j = 0; j < ARRAY_SIZE(port_prox->queue_ports); j++) {
31648cc2f5eSHaijun Liu 		for (i = 0; i < ARRAY_SIZE(port_prox->queue_ports[j]); i++)
31748cc2f5eSHaijun Liu 			INIT_LIST_HEAD(&port_prox->queue_ports[j][i]);
31848cc2f5eSHaijun Liu 	}
31948cc2f5eSHaijun Liu 
32048cc2f5eSHaijun Liu 	for_each_proxy_port(i, port, port_prox) {
32148cc2f5eSHaijun Liu 		const struct t7xx_port_conf *port_conf = port->port_conf;
32248cc2f5eSHaijun Liu 		enum cldma_id path_id = port_conf->path_id;
32348cc2f5eSHaijun Liu 		u8 ch_id;
32448cc2f5eSHaijun Liu 
32548cc2f5eSHaijun Liu 		ch_id = FIELD_GET(PORT_CH_ID_MASK, port_conf->rx_ch);
32648cc2f5eSHaijun Liu 		list_add_tail(&port->entry, &port_prox->rx_ch_ports[ch_id]);
32748cc2f5eSHaijun Liu 		list_add_tail(&port->queue_entry,
32848cc2f5eSHaijun Liu 			      &port_prox->queue_ports[path_id][port_conf->rxq_index]);
32948cc2f5eSHaijun Liu 	}
33048cc2f5eSHaijun Liu }
33148cc2f5eSHaijun Liu 
t7xx_port_proxy_find_port(struct t7xx_pci_dev * t7xx_dev,struct cldma_queue * queue,u16 channel)33248cc2f5eSHaijun Liu static struct t7xx_port *t7xx_port_proxy_find_port(struct t7xx_pci_dev *t7xx_dev,
33348cc2f5eSHaijun Liu 						   struct cldma_queue *queue, u16 channel)
33448cc2f5eSHaijun Liu {
33548cc2f5eSHaijun Liu 	struct port_proxy *port_prox = t7xx_dev->md->port_prox;
33648cc2f5eSHaijun Liu 	struct list_head *port_list;
33748cc2f5eSHaijun Liu 	struct t7xx_port *port;
33848cc2f5eSHaijun Liu 	u8 ch_id;
33948cc2f5eSHaijun Liu 
34048cc2f5eSHaijun Liu 	ch_id = FIELD_GET(PORT_CH_ID_MASK, channel);
34148cc2f5eSHaijun Liu 	port_list = &port_prox->rx_ch_ports[ch_id];
34248cc2f5eSHaijun Liu 	list_for_each_entry(port, port_list, entry) {
34348cc2f5eSHaijun Liu 		const struct t7xx_port_conf *port_conf = port->port_conf;
34448cc2f5eSHaijun Liu 
34548cc2f5eSHaijun Liu 		if (queue->md_ctrl->hif_id == port_conf->path_id &&
34648cc2f5eSHaijun Liu 		    channel == port_conf->rx_ch)
34748cc2f5eSHaijun Liu 			return port;
34848cc2f5eSHaijun Liu 	}
34948cc2f5eSHaijun Liu 
35048cc2f5eSHaijun Liu 	return NULL;
35148cc2f5eSHaijun Liu }
35248cc2f5eSHaijun Liu 
35348cc2f5eSHaijun Liu /**
35448cc2f5eSHaijun Liu  * t7xx_port_proxy_recv_skb() - Dispatch received skb.
35548cc2f5eSHaijun Liu  * @queue: CLDMA queue.
35648cc2f5eSHaijun Liu  * @skb: Socket buffer.
35748cc2f5eSHaijun Liu  *
35848cc2f5eSHaijun Liu  * Return:
35948cc2f5eSHaijun Liu  ** 0		- Packet consumed.
36048cc2f5eSHaijun Liu  ** -ERROR	- Failed to process skb.
36148cc2f5eSHaijun Liu  */
t7xx_port_proxy_recv_skb(struct cldma_queue * queue,struct sk_buff * skb)36248cc2f5eSHaijun Liu static int t7xx_port_proxy_recv_skb(struct cldma_queue *queue, struct sk_buff *skb)
36348cc2f5eSHaijun Liu {
36448cc2f5eSHaijun Liu 	struct ccci_header *ccci_h = (struct ccci_header *)skb->data;
36548cc2f5eSHaijun Liu 	struct t7xx_pci_dev *t7xx_dev = queue->md_ctrl->t7xx_dev;
36648cc2f5eSHaijun Liu 	struct t7xx_fsm_ctl *ctl = t7xx_dev->md->fsm_ctl;
36748cc2f5eSHaijun Liu 	struct device *dev = queue->md_ctrl->dev;
36848cc2f5eSHaijun Liu 	const struct t7xx_port_conf *port_conf;
36948cc2f5eSHaijun Liu 	struct t7xx_port *port;
37048cc2f5eSHaijun Liu 	u16 seq_num, channel;
37148cc2f5eSHaijun Liu 	int ret;
37248cc2f5eSHaijun Liu 
37348cc2f5eSHaijun Liu 	channel = FIELD_GET(CCCI_H_CHN_FLD, le32_to_cpu(ccci_h->status));
37448cc2f5eSHaijun Liu 	if (t7xx_fsm_get_md_state(ctl) == MD_STATE_INVALID) {
37548cc2f5eSHaijun Liu 		dev_err_ratelimited(dev, "Packet drop on channel 0x%x, modem not ready\n", channel);
37648cc2f5eSHaijun Liu 		goto drop_skb;
37748cc2f5eSHaijun Liu 	}
37848cc2f5eSHaijun Liu 
37948cc2f5eSHaijun Liu 	port = t7xx_port_proxy_find_port(t7xx_dev, queue, channel);
38048cc2f5eSHaijun Liu 	if (!port) {
38148cc2f5eSHaijun Liu 		dev_err_ratelimited(dev, "Packet drop on channel 0x%x, port not found\n", channel);
38248cc2f5eSHaijun Liu 		goto drop_skb;
38348cc2f5eSHaijun Liu 	}
38448cc2f5eSHaijun Liu 
38548cc2f5eSHaijun Liu 	seq_num = t7xx_port_next_rx_seq_num(port, ccci_h);
38648cc2f5eSHaijun Liu 	port_conf = port->port_conf;
38748cc2f5eSHaijun Liu 	skb_pull(skb, sizeof(*ccci_h));
38848cc2f5eSHaijun Liu 
38948cc2f5eSHaijun Liu 	ret = port_conf->ops->recv_skb(port, skb);
39048cc2f5eSHaijun Liu 	/* Error indicates to try again later */
39148cc2f5eSHaijun Liu 	if (ret) {
39248cc2f5eSHaijun Liu 		skb_push(skb, sizeof(*ccci_h));
39348cc2f5eSHaijun Liu 		return ret;
39448cc2f5eSHaijun Liu 	}
39548cc2f5eSHaijun Liu 
39648cc2f5eSHaijun Liu 	port->seq_nums[MTK_RX] = seq_num;
39748cc2f5eSHaijun Liu 	return 0;
39848cc2f5eSHaijun Liu 
39948cc2f5eSHaijun Liu drop_skb:
40048cc2f5eSHaijun Liu 	dev_kfree_skb_any(skb);
40148cc2f5eSHaijun Liu 	return 0;
40248cc2f5eSHaijun Liu }
40348cc2f5eSHaijun Liu 
40448cc2f5eSHaijun Liu /**
40548cc2f5eSHaijun Liu  * t7xx_port_proxy_md_status_notify() - Notify all ports of state.
40648cc2f5eSHaijun Liu  *@port_prox: The port_proxy pointer.
40748cc2f5eSHaijun Liu  *@state: State.
40848cc2f5eSHaijun Liu  *
40948cc2f5eSHaijun Liu  * Called by t7xx_fsm. Used to dispatch modem status for all ports,
41048cc2f5eSHaijun Liu  * which want to know MD state transition.
41148cc2f5eSHaijun Liu  */
t7xx_port_proxy_md_status_notify(struct port_proxy * port_prox,unsigned int state)41248cc2f5eSHaijun Liu void t7xx_port_proxy_md_status_notify(struct port_proxy *port_prox, unsigned int state)
41348cc2f5eSHaijun Liu {
41448cc2f5eSHaijun Liu 	struct t7xx_port *port;
41548cc2f5eSHaijun Liu 	int i;
41648cc2f5eSHaijun Liu 
41748cc2f5eSHaijun Liu 	for_each_proxy_port(i, port, port_prox) {
41848cc2f5eSHaijun Liu 		const struct t7xx_port_conf *port_conf = port->port_conf;
41948cc2f5eSHaijun Liu 
42048cc2f5eSHaijun Liu 		if (port_conf->ops->md_state_notify)
42148cc2f5eSHaijun Liu 			port_conf->ops->md_state_notify(port, state);
42248cc2f5eSHaijun Liu 	}
42348cc2f5eSHaijun Liu }
42448cc2f5eSHaijun Liu 
t7xx_proxy_init_all_ports(struct t7xx_modem * md)42548cc2f5eSHaijun Liu static void t7xx_proxy_init_all_ports(struct t7xx_modem *md)
42648cc2f5eSHaijun Liu {
42748cc2f5eSHaijun Liu 	struct port_proxy *port_prox = md->port_prox;
42848cc2f5eSHaijun Liu 	struct t7xx_port *port;
42948cc2f5eSHaijun Liu 	int i;
43048cc2f5eSHaijun Liu 
43148cc2f5eSHaijun Liu 	for_each_proxy_port(i, port, port_prox) {
43248cc2f5eSHaijun Liu 		const struct t7xx_port_conf *port_conf = port->port_conf;
43348cc2f5eSHaijun Liu 
43448cc2f5eSHaijun Liu 		t7xx_port_struct_init(port);
43548cc2f5eSHaijun Liu 
436da45d256SHaijun Liu 		if (port_conf->tx_ch == PORT_CH_CONTROL_TX)
437da45d256SHaijun Liu 			md->core_md.ctl_port = port;
438da45d256SHaijun Liu 
439*ba2274dcSJose Ignacio Tornos Martinez 		if (port_conf->tx_ch == PORT_CH_AP_CONTROL_TX)
440*ba2274dcSJose Ignacio Tornos Martinez 			md->core_ap.ctl_port = port;
441*ba2274dcSJose Ignacio Tornos Martinez 
44248cc2f5eSHaijun Liu 		port->t7xx_dev = md->t7xx_dev;
44348cc2f5eSHaijun Liu 		port->dev = &md->t7xx_dev->pdev->dev;
44448cc2f5eSHaijun Liu 		spin_lock_init(&port->port_update_lock);
44548cc2f5eSHaijun Liu 		port->chan_enable = false;
44648cc2f5eSHaijun Liu 
44748cc2f5eSHaijun Liu 		if (port_conf->ops->init)
44848cc2f5eSHaijun Liu 			port_conf->ops->init(port);
44948cc2f5eSHaijun Liu 	}
45048cc2f5eSHaijun Liu 
45148cc2f5eSHaijun Liu 	t7xx_proxy_setup_ch_mapping(port_prox);
45248cc2f5eSHaijun Liu }
45348cc2f5eSHaijun Liu 
t7xx_proxy_alloc(struct t7xx_modem * md)45448cc2f5eSHaijun Liu static int t7xx_proxy_alloc(struct t7xx_modem *md)
45548cc2f5eSHaijun Liu {
456*ba2274dcSJose Ignacio Tornos Martinez 	unsigned int port_count = ARRAY_SIZE(t7xx_port_conf);
45748cc2f5eSHaijun Liu 	struct device *dev = &md->t7xx_dev->pdev->dev;
45848cc2f5eSHaijun Liu 	struct port_proxy *port_prox;
45948cc2f5eSHaijun Liu 	int i;
46048cc2f5eSHaijun Liu 
46148cc2f5eSHaijun Liu 	port_prox = devm_kzalloc(dev, sizeof(*port_prox) + sizeof(struct t7xx_port) * port_count,
46248cc2f5eSHaijun Liu 				 GFP_KERNEL);
46348cc2f5eSHaijun Liu 	if (!port_prox)
46448cc2f5eSHaijun Liu 		return -ENOMEM;
46548cc2f5eSHaijun Liu 
46648cc2f5eSHaijun Liu 	md->port_prox = port_prox;
46748cc2f5eSHaijun Liu 	port_prox->dev = dev;
46848cc2f5eSHaijun Liu 
46948cc2f5eSHaijun Liu 	for (i = 0; i < port_count; i++)
470*ba2274dcSJose Ignacio Tornos Martinez 		port_prox->ports[i].port_conf = &t7xx_port_conf[i];
47148cc2f5eSHaijun Liu 
47248cc2f5eSHaijun Liu 	port_prox->port_count = port_count;
47348cc2f5eSHaijun Liu 	t7xx_proxy_init_all_ports(md);
47448cc2f5eSHaijun Liu 	return 0;
47548cc2f5eSHaijun Liu }
47648cc2f5eSHaijun Liu 
47748cc2f5eSHaijun Liu /**
47848cc2f5eSHaijun Liu  * t7xx_port_proxy_init() - Initialize ports.
47948cc2f5eSHaijun Liu  * @md: Modem.
48048cc2f5eSHaijun Liu  *
48148cc2f5eSHaijun Liu  * Create all port instances.
48248cc2f5eSHaijun Liu  *
48348cc2f5eSHaijun Liu  * Return:
48448cc2f5eSHaijun Liu  * * 0		- Success.
48548cc2f5eSHaijun Liu  * * -ERROR	- Error code from failure sub-initializations.
48648cc2f5eSHaijun Liu  */
t7xx_port_proxy_init(struct t7xx_modem * md)48748cc2f5eSHaijun Liu int t7xx_port_proxy_init(struct t7xx_modem *md)
48848cc2f5eSHaijun Liu {
48948cc2f5eSHaijun Liu 	int ret;
49048cc2f5eSHaijun Liu 
49148cc2f5eSHaijun Liu 	ret = t7xx_proxy_alloc(md);
49248cc2f5eSHaijun Liu 	if (ret)
49348cc2f5eSHaijun Liu 		return ret;
49448cc2f5eSHaijun Liu 
495*ba2274dcSJose Ignacio Tornos Martinez 	t7xx_cldma_set_recv_skb(md->md_ctrl[CLDMA_ID_AP], t7xx_port_proxy_recv_skb);
49648cc2f5eSHaijun Liu 	t7xx_cldma_set_recv_skb(md->md_ctrl[CLDMA_ID_MD], t7xx_port_proxy_recv_skb);
49748cc2f5eSHaijun Liu 	return 0;
49848cc2f5eSHaijun Liu }
49948cc2f5eSHaijun Liu 
t7xx_port_proxy_uninit(struct port_proxy * port_prox)50048cc2f5eSHaijun Liu void t7xx_port_proxy_uninit(struct port_proxy *port_prox)
50148cc2f5eSHaijun Liu {
50248cc2f5eSHaijun Liu 	struct t7xx_port *port;
50348cc2f5eSHaijun Liu 	int i;
50448cc2f5eSHaijun Liu 
50548cc2f5eSHaijun Liu 	for_each_proxy_port(i, port, port_prox) {
50648cc2f5eSHaijun Liu 		const struct t7xx_port_conf *port_conf = port->port_conf;
50748cc2f5eSHaijun Liu 
50848cc2f5eSHaijun Liu 		if (port_conf->ops->uninit)
50948cc2f5eSHaijun Liu 			port_conf->ops->uninit(port);
51048cc2f5eSHaijun Liu 	}
51148cc2f5eSHaijun Liu }
51248cc2f5eSHaijun Liu 
t7xx_port_proxy_chl_enable_disable(struct port_proxy * port_prox,unsigned int ch_id,bool en_flag)51348cc2f5eSHaijun Liu int t7xx_port_proxy_chl_enable_disable(struct port_proxy *port_prox, unsigned int ch_id,
51448cc2f5eSHaijun Liu 				       bool en_flag)
51548cc2f5eSHaijun Liu {
51648cc2f5eSHaijun Liu 	struct t7xx_port *port = t7xx_proxy_get_port_by_ch(port_prox, ch_id);
51748cc2f5eSHaijun Liu 	const struct t7xx_port_conf *port_conf;
51848cc2f5eSHaijun Liu 
51948cc2f5eSHaijun Liu 	if (!port)
52048cc2f5eSHaijun Liu 		return -EINVAL;
52148cc2f5eSHaijun Liu 
52248cc2f5eSHaijun Liu 	port_conf = port->port_conf;
52348cc2f5eSHaijun Liu 
52448cc2f5eSHaijun Liu 	if (en_flag) {
52548cc2f5eSHaijun Liu 		if (port_conf->ops->enable_chl)
52648cc2f5eSHaijun Liu 			port_conf->ops->enable_chl(port);
52748cc2f5eSHaijun Liu 	} else {
52848cc2f5eSHaijun Liu 		if (port_conf->ops->disable_chl)
52948cc2f5eSHaijun Liu 			port_conf->ops->disable_chl(port);
53048cc2f5eSHaijun Liu 	}
53148cc2f5eSHaijun Liu 
53248cc2f5eSHaijun Liu 	return 0;
53348cc2f5eSHaijun Liu }
534