xref: /linux/drivers/net/wwan/t7xx/t7xx_port_proxy.c (revision 7f71507851fc7764b36a3221839607d3a45c2025)
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  *  Haijun Liu <haijun.liu@mediatek.com>
9  *  Moises Veleta <moises.veleta@intel.com>
10  *  Ricardo Martinez <ricardo.martinez@linux.intel.com>
11  *
12  * Contributors:
13  *  Andy Shevchenko <andriy.shevchenko@linux.intel.com>
14  *  Chandrashekar Devegowda <chandrashekar.devegowda@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/bits.h>
21 #include <linux/bitfield.h>
22 #include <linux/device.h>
23 #include <linux/gfp.h>
24 #include <linux/kernel.h>
25 #include <linux/kthread.h>
26 #include <linux/list.h>
27 #include <linux/mutex.h>
28 #include <linux/netdevice.h>
29 #include <linux/skbuff.h>
30 #include <linux/spinlock.h>
31 #include <linux/wait.h>
32 #include <linux/wwan.h>
33 
34 #include "t7xx_hif_cldma.h"
35 #include "t7xx_modem_ops.h"
36 #include "t7xx_port.h"
37 #include "t7xx_port_proxy.h"
38 #include "t7xx_state_monitor.h"
39 
40 #define Q_IDX_CTRL			0
41 #define Q_IDX_MBIM_MIPC		2
42 #define Q_IDX_ADB			3
43 #define Q_IDX_AT_CMD			5
44 
45 #define INVALID_SEQ_NUM			GENMASK(15, 0)
46 
47 #define for_each_proxy_port(i, p, proxy)	\
48 	for (i = 0, (p) = &(proxy)->ports[i];	\
49 	     i < (proxy)->port_count;		\
50 	     i++, (p) = &(proxy)->ports[i])
51 
52 #define T7XX_MAX_POSSIBLE_PORTS_NUM	\
53 	(max(ARRAY_SIZE(t7xx_port_conf), ARRAY_SIZE(t7xx_early_port_conf)))
54 
55 static const struct t7xx_port_conf t7xx_port_conf[] = {
56 	{
57 		.tx_ch = PORT_CH_UART2_TX,
58 		.rx_ch = PORT_CH_UART2_RX,
59 		.txq_index = Q_IDX_AT_CMD,
60 		.rxq_index = Q_IDX_AT_CMD,
61 		.txq_exp_index = 0xff,
62 		.rxq_exp_index = 0xff,
63 		.path_id = CLDMA_ID_MD,
64 		.ops = &wwan_sub_port_ops,
65 		.name = "AT",
66 		.port_type = WWAN_PORT_AT,
67 	}, {
68 		.tx_ch = PORT_CH_MBIM_TX,
69 		.rx_ch = PORT_CH_MBIM_RX,
70 		.txq_index = Q_IDX_MBIM_MIPC,
71 		.rxq_index = Q_IDX_MBIM_MIPC,
72 		.path_id = CLDMA_ID_MD,
73 		.ops = &wwan_sub_port_ops,
74 		.name = "MBIM",
75 		.port_type = WWAN_PORT_MBIM,
76 	}, {
77 #ifdef CONFIG_WWAN_DEBUGFS
78 		.tx_ch = PORT_CH_MD_LOG_TX,
79 		.rx_ch = PORT_CH_MD_LOG_RX,
80 		.txq_index = 7,
81 		.rxq_index = 7,
82 		.txq_exp_index = 7,
83 		.rxq_exp_index = 7,
84 		.path_id = CLDMA_ID_MD,
85 		.ops = &t7xx_trace_port_ops,
86 		.name = "mdlog",
87 	}, {
88 #endif
89 		.tx_ch = PORT_CH_CONTROL_TX,
90 		.rx_ch = PORT_CH_CONTROL_RX,
91 		.txq_index = Q_IDX_CTRL,
92 		.rxq_index = Q_IDX_CTRL,
93 		.path_id = CLDMA_ID_MD,
94 		.ops = &ctl_port_ops,
95 		.name = "t7xx_ctrl",
96 	}, {
97 		.tx_ch = PORT_CH_AP_CONTROL_TX,
98 		.rx_ch = PORT_CH_AP_CONTROL_RX,
99 		.txq_index = Q_IDX_CTRL,
100 		.rxq_index = Q_IDX_CTRL,
101 		.path_id = CLDMA_ID_AP,
102 		.ops = &ctl_port_ops,
103 		.name = "t7xx_ap_ctrl",
104 	}, {
105 		.tx_ch = PORT_CH_AP_ADB_TX,
106 		.rx_ch = PORT_CH_AP_ADB_RX,
107 		.txq_index = Q_IDX_ADB,
108 		.rxq_index = Q_IDX_ADB,
109 		.path_id = CLDMA_ID_AP,
110 		.ops = &wwan_sub_port_ops,
111 		.name = "adb",
112 		.port_type = WWAN_PORT_ADB,
113 		.debug = true,
114 	}, {
115 		.tx_ch = PORT_CH_MIPC_TX,
116 		.rx_ch = PORT_CH_MIPC_RX,
117 		.txq_index = Q_IDX_MBIM_MIPC,
118 		.rxq_index = Q_IDX_MBIM_MIPC,
119 		.path_id = CLDMA_ID_MD,
120 		.ops = &wwan_sub_port_ops,
121 		.name = "mipc",
122 		.port_type = WWAN_PORT_MIPC,
123 		.debug = true,
124 	}
125 };
126 
127 static const struct t7xx_port_conf t7xx_early_port_conf[] = {
128 	{
129 		.tx_ch = PORT_CH_UNIMPORTANT,
130 		.rx_ch = PORT_CH_UNIMPORTANT,
131 		.txq_index = CLDMA_Q_IDX_DUMP,
132 		.rxq_index = CLDMA_Q_IDX_DUMP,
133 		.txq_exp_index = CLDMA_Q_IDX_DUMP,
134 		.rxq_exp_index = CLDMA_Q_IDX_DUMP,
135 		.path_id = CLDMA_ID_AP,
136 		.ops = &wwan_sub_port_ops,
137 		.name = "fastboot",
138 		.port_type = WWAN_PORT_FASTBOOT,
139 	},
140 };
141 
142 static struct t7xx_port *t7xx_proxy_get_port_by_ch(struct port_proxy *port_prox, enum port_ch ch)
143 {
144 	const struct t7xx_port_conf *port_conf;
145 	struct t7xx_port *port;
146 	int i;
147 
148 	for_each_proxy_port(i, port, port_prox) {
149 		port_conf = port->port_conf;
150 		if (port_conf->rx_ch == ch || port_conf->tx_ch == ch)
151 			return port;
152 	}
153 
154 	return NULL;
155 }
156 
157 static u16 t7xx_port_next_rx_seq_num(struct t7xx_port *port, struct ccci_header *ccci_h)
158 {
159 	u32 status = le32_to_cpu(ccci_h->status);
160 	u16 seq_num, next_seq_num;
161 	bool assert_bit;
162 
163 	seq_num = FIELD_GET(CCCI_H_SEQ_FLD, status);
164 	next_seq_num = (seq_num + 1) & FIELD_MAX(CCCI_H_SEQ_FLD);
165 	assert_bit = status & CCCI_H_AST_BIT;
166 	if (!assert_bit || port->seq_nums[MTK_RX] == INVALID_SEQ_NUM)
167 		return next_seq_num;
168 
169 	if (seq_num != port->seq_nums[MTK_RX])
170 		dev_warn_ratelimited(port->dev,
171 				     "seq num out-of-order %u != %u (header %X, len %X)\n",
172 				     seq_num, port->seq_nums[MTK_RX],
173 				     le32_to_cpu(ccci_h->packet_header),
174 				     le32_to_cpu(ccci_h->packet_len));
175 
176 	return next_seq_num;
177 }
178 
179 void t7xx_port_proxy_reset(struct port_proxy *port_prox)
180 {
181 	struct t7xx_port *port;
182 	int i;
183 
184 	for_each_proxy_port(i, port, port_prox) {
185 		port->seq_nums[MTK_RX] = INVALID_SEQ_NUM;
186 		port->seq_nums[MTK_TX] = 0;
187 	}
188 }
189 
190 static int t7xx_port_get_queue_no(struct t7xx_port *port)
191 {
192 	const struct t7xx_port_conf *port_conf = port->port_conf;
193 	struct t7xx_fsm_ctl *ctl = port->t7xx_dev->md->fsm_ctl;
194 
195 	return t7xx_fsm_get_md_state(ctl) == MD_STATE_EXCEPTION ?
196 		port_conf->txq_exp_index : port_conf->txq_index;
197 }
198 
199 static void t7xx_port_struct_init(struct t7xx_port *port)
200 {
201 	INIT_LIST_HEAD(&port->entry);
202 	INIT_LIST_HEAD(&port->queue_entry);
203 	skb_queue_head_init(&port->rx_skb_list);
204 	init_waitqueue_head(&port->rx_wq);
205 	port->seq_nums[MTK_RX] = INVALID_SEQ_NUM;
206 	port->seq_nums[MTK_TX] = 0;
207 	atomic_set(&port->usage_cnt, 0);
208 }
209 
210 struct sk_buff *t7xx_port_alloc_skb(int payload)
211 {
212 	struct sk_buff *skb = __dev_alloc_skb(payload + sizeof(struct ccci_header), GFP_KERNEL);
213 
214 	if (skb)
215 		skb_reserve(skb, sizeof(struct ccci_header));
216 
217 	return skb;
218 }
219 
220 struct sk_buff *t7xx_ctrl_alloc_skb(int payload)
221 {
222 	struct sk_buff *skb = t7xx_port_alloc_skb(payload + sizeof(struct ctrl_msg_header));
223 
224 	if (skb)
225 		skb_reserve(skb, sizeof(struct ctrl_msg_header));
226 
227 	return skb;
228 }
229 
230 /**
231  * t7xx_port_enqueue_skb() - Enqueue the received skb into the port's rx_skb_list.
232  * @port: port context.
233  * @skb: received skb.
234  *
235  * Return:
236  * * 0		- Success.
237  * * -ENOBUFS	- Not enough buffer space. Caller will try again later, skb is not consumed.
238  */
239 int t7xx_port_enqueue_skb(struct t7xx_port *port, struct sk_buff *skb)
240 {
241 	unsigned long flags;
242 
243 	spin_lock_irqsave(&port->rx_wq.lock, flags);
244 	if (port->rx_skb_list.qlen >= port->rx_length_th) {
245 		spin_unlock_irqrestore(&port->rx_wq.lock, flags);
246 
247 		return -ENOBUFS;
248 	}
249 	__skb_queue_tail(&port->rx_skb_list, skb);
250 	spin_unlock_irqrestore(&port->rx_wq.lock, flags);
251 
252 	wake_up_all(&port->rx_wq);
253 	return 0;
254 }
255 
256 int t7xx_get_port_mtu(struct t7xx_port *port)
257 {
258 	enum cldma_id path_id = port->port_conf->path_id;
259 	int tx_qno = t7xx_port_get_queue_no(port);
260 	struct cldma_ctrl *md_ctrl;
261 
262 	md_ctrl = port->t7xx_dev->md->md_ctrl[path_id];
263 	return md_ctrl->tx_ring[tx_qno].pkt_size;
264 }
265 
266 int t7xx_port_send_raw_skb(struct t7xx_port *port, struct sk_buff *skb)
267 {
268 	enum cldma_id path_id = port->port_conf->path_id;
269 	struct cldma_ctrl *md_ctrl;
270 	int ret, tx_qno;
271 
272 	md_ctrl = port->t7xx_dev->md->md_ctrl[path_id];
273 	tx_qno = t7xx_port_get_queue_no(port);
274 	ret = t7xx_cldma_send_skb(md_ctrl, tx_qno, skb);
275 	if (ret)
276 		dev_err(port->dev, "Failed to send skb: %d\n", ret);
277 
278 	return ret;
279 }
280 
281 static int t7xx_port_send_ccci_skb(struct t7xx_port *port, struct sk_buff *skb,
282 				   unsigned int pkt_header, unsigned int ex_msg)
283 {
284 	const struct t7xx_port_conf *port_conf = port->port_conf;
285 	struct ccci_header *ccci_h;
286 	u32 status;
287 	int ret;
288 
289 	ccci_h = skb_push(skb, sizeof(*ccci_h));
290 	status = FIELD_PREP(CCCI_H_CHN_FLD, port_conf->tx_ch) |
291 		 FIELD_PREP(CCCI_H_SEQ_FLD, port->seq_nums[MTK_TX]) | CCCI_H_AST_BIT;
292 	ccci_h->status = cpu_to_le32(status);
293 	ccci_h->packet_header = cpu_to_le32(pkt_header);
294 	ccci_h->packet_len = cpu_to_le32(skb->len);
295 	ccci_h->ex_msg = cpu_to_le32(ex_msg);
296 
297 	ret = t7xx_port_send_raw_skb(port, skb);
298 	if (ret)
299 		return ret;
300 
301 	port->seq_nums[MTK_TX]++;
302 	return 0;
303 }
304 
305 int t7xx_port_send_ctl_skb(struct t7xx_port *port, struct sk_buff *skb, unsigned int msg,
306 			   unsigned int ex_msg)
307 {
308 	struct ctrl_msg_header *ctrl_msg_h;
309 	unsigned int msg_len = skb->len;
310 	u32 pkt_header = 0;
311 
312 	ctrl_msg_h = skb_push(skb, sizeof(*ctrl_msg_h));
313 	ctrl_msg_h->ctrl_msg_id = cpu_to_le32(msg);
314 	ctrl_msg_h->ex_msg = cpu_to_le32(ex_msg);
315 	ctrl_msg_h->data_length = cpu_to_le32(msg_len);
316 
317 	if (!msg_len)
318 		pkt_header = CCCI_HEADER_NO_DATA;
319 
320 	return t7xx_port_send_ccci_skb(port, skb, pkt_header, ex_msg);
321 }
322 
323 int t7xx_port_send_skb(struct t7xx_port *port, struct sk_buff *skb, unsigned int pkt_header,
324 		       unsigned int ex_msg)
325 {
326 	struct t7xx_fsm_ctl *ctl = port->t7xx_dev->md->fsm_ctl;
327 	unsigned int fsm_state;
328 
329 	fsm_state = t7xx_fsm_get_ctl_state(ctl);
330 	if (fsm_state != FSM_STATE_PRE_START) {
331 		const struct t7xx_port_conf *port_conf = port->port_conf;
332 		enum md_state md_state = t7xx_fsm_get_md_state(ctl);
333 
334 		switch (md_state) {
335 		case MD_STATE_EXCEPTION:
336 			if (port_conf->tx_ch != PORT_CH_MD_LOG_TX)
337 				return -EBUSY;
338 			break;
339 
340 		case MD_STATE_WAITING_FOR_HS1:
341 		case MD_STATE_WAITING_FOR_HS2:
342 		case MD_STATE_STOPPED:
343 		case MD_STATE_WAITING_TO_STOP:
344 		case MD_STATE_INVALID:
345 			return -ENODEV;
346 
347 		default:
348 			break;
349 		}
350 	}
351 
352 	return t7xx_port_send_ccci_skb(port, skb, pkt_header, ex_msg);
353 }
354 
355 static void t7xx_proxy_setup_ch_mapping(struct port_proxy *port_prox)
356 {
357 	struct t7xx_port *port;
358 
359 	int i, j;
360 
361 	for (i = 0; i < ARRAY_SIZE(port_prox->rx_ch_ports); i++)
362 		INIT_LIST_HEAD(&port_prox->rx_ch_ports[i]);
363 
364 	for (j = 0; j < ARRAY_SIZE(port_prox->queue_ports); j++) {
365 		for (i = 0; i < ARRAY_SIZE(port_prox->queue_ports[j]); i++)
366 			INIT_LIST_HEAD(&port_prox->queue_ports[j][i]);
367 	}
368 
369 	for_each_proxy_port(i, port, port_prox) {
370 		const struct t7xx_port_conf *port_conf = port->port_conf;
371 		enum cldma_id path_id = port_conf->path_id;
372 		u8 ch_id;
373 
374 		ch_id = FIELD_GET(PORT_CH_ID_MASK, port_conf->rx_ch);
375 		list_add_tail(&port->entry, &port_prox->rx_ch_ports[ch_id]);
376 		list_add_tail(&port->queue_entry,
377 			      &port_prox->queue_ports[path_id][port_conf->rxq_index]);
378 	}
379 }
380 
381 /**
382  * t7xx_port_proxy_recv_skb_from_dedicated_queue() - Dispatch early port received skb.
383  * @queue: CLDMA queue.
384  * @skb: Socket buffer.
385  *
386  * Return:
387  ** 0		- Packet consumed.
388  ** -ERROR	- Failed to process skb.
389  */
390 int t7xx_port_proxy_recv_skb_from_dedicated_queue(struct cldma_queue *queue, struct sk_buff *skb)
391 {
392 	struct t7xx_pci_dev *t7xx_dev = queue->md_ctrl->t7xx_dev;
393 	struct port_proxy *port_prox = t7xx_dev->md->port_prox;
394 	const struct t7xx_port_conf *port_conf;
395 	struct t7xx_port *port;
396 	int ret;
397 
398 	port = &port_prox->ports[0];
399 	if (WARN_ON_ONCE(port->port_conf->rxq_index != queue->index)) {
400 		dev_kfree_skb_any(skb);
401 		return -EINVAL;
402 	}
403 
404 	port_conf = port->port_conf;
405 	ret = port_conf->ops->recv_skb(port, skb);
406 	if (ret < 0 && ret != -ENOBUFS) {
407 		dev_err(port->dev, "drop on RX ch %d, %d\n", port_conf->rx_ch, ret);
408 		dev_kfree_skb_any(skb);
409 	}
410 
411 	return ret;
412 }
413 
414 static struct t7xx_port *t7xx_port_proxy_find_port(struct t7xx_pci_dev *t7xx_dev,
415 						   struct cldma_queue *queue, u16 channel)
416 {
417 	struct port_proxy *port_prox = t7xx_dev->md->port_prox;
418 	struct list_head *port_list;
419 	struct t7xx_port *port;
420 	u8 ch_id;
421 
422 	ch_id = FIELD_GET(PORT_CH_ID_MASK, channel);
423 	port_list = &port_prox->rx_ch_ports[ch_id];
424 	list_for_each_entry(port, port_list, entry) {
425 		const struct t7xx_port_conf *port_conf = port->port_conf;
426 
427 		if (queue->md_ctrl->hif_id == port_conf->path_id &&
428 		    channel == port_conf->rx_ch)
429 			return port;
430 	}
431 
432 	return NULL;
433 }
434 
435 /**
436  * t7xx_port_proxy_recv_skb() - Dispatch received skb.
437  * @queue: CLDMA queue.
438  * @skb: Socket buffer.
439  *
440  * Return:
441  ** 0		- Packet consumed.
442  ** -ERROR	- Failed to process skb.
443  */
444 int t7xx_port_proxy_recv_skb(struct cldma_queue *queue, struct sk_buff *skb)
445 {
446 	struct ccci_header *ccci_h = (struct ccci_header *)skb->data;
447 	struct t7xx_pci_dev *t7xx_dev = queue->md_ctrl->t7xx_dev;
448 	struct t7xx_fsm_ctl *ctl = t7xx_dev->md->fsm_ctl;
449 	struct device *dev = queue->md_ctrl->dev;
450 	const struct t7xx_port_conf *port_conf;
451 	struct t7xx_port *port;
452 	u16 seq_num, channel;
453 	int ret;
454 
455 	channel = FIELD_GET(CCCI_H_CHN_FLD, le32_to_cpu(ccci_h->status));
456 	if (t7xx_fsm_get_md_state(ctl) == MD_STATE_INVALID) {
457 		dev_err_ratelimited(dev, "Packet drop on channel 0x%x, modem not ready\n", channel);
458 		goto drop_skb;
459 	}
460 
461 	port = t7xx_port_proxy_find_port(t7xx_dev, queue, channel);
462 	if (!port) {
463 		dev_err_ratelimited(dev, "Packet drop on channel 0x%x, port not found\n", channel);
464 		goto drop_skb;
465 	}
466 
467 	seq_num = t7xx_port_next_rx_seq_num(port, ccci_h);
468 	port_conf = port->port_conf;
469 	skb_pull(skb, sizeof(*ccci_h));
470 
471 	ret = port_conf->ops->recv_skb(port, skb);
472 	/* Error indicates to try again later */
473 	if (ret) {
474 		skb_push(skb, sizeof(*ccci_h));
475 		return ret;
476 	}
477 
478 	port->seq_nums[MTK_RX] = seq_num;
479 	return 0;
480 
481 drop_skb:
482 	dev_kfree_skb_any(skb);
483 	return 0;
484 }
485 
486 /**
487  * t7xx_port_proxy_md_status_notify() - Notify all ports of state.
488  *@port_prox: The port_proxy pointer.
489  *@state: State.
490  *
491  * Called by t7xx_fsm. Used to dispatch modem status for all ports,
492  * which want to know MD state transition.
493  */
494 void t7xx_port_proxy_md_status_notify(struct port_proxy *port_prox, unsigned int state)
495 {
496 	struct t7xx_port *port;
497 	int i;
498 
499 	for_each_proxy_port(i, port, port_prox) {
500 		const struct t7xx_port_conf *port_conf = port->port_conf;
501 
502 		if (port_conf->ops->md_state_notify)
503 			port_conf->ops->md_state_notify(port, state);
504 	}
505 }
506 
507 static void t7xx_proxy_init_all_ports(struct t7xx_modem *md)
508 {
509 	struct port_proxy *port_prox = md->port_prox;
510 	struct t7xx_port *port;
511 	int i;
512 
513 	for_each_proxy_port(i, port, port_prox) {
514 		const struct t7xx_port_conf *port_conf = port->port_conf;
515 
516 		t7xx_port_struct_init(port);
517 
518 		if (port_conf->tx_ch == PORT_CH_CONTROL_TX)
519 			md->core_md.ctl_port = port;
520 
521 		if (port_conf->tx_ch == PORT_CH_AP_CONTROL_TX)
522 			md->core_ap.ctl_port = port;
523 
524 		port->t7xx_dev = md->t7xx_dev;
525 		port->dev = &md->t7xx_dev->pdev->dev;
526 		spin_lock_init(&port->port_update_lock);
527 		port->chan_enable = false;
528 
529 		if (!port_conf->debug &&
530 		    port_conf->ops &&
531 		    port_conf->ops->init)
532 			port_conf->ops->init(port);
533 	}
534 
535 	t7xx_proxy_setup_ch_mapping(port_prox);
536 }
537 
538 void t7xx_proxy_debug_ports_show(struct t7xx_pci_dev *t7xx_dev, bool show)
539 {
540 	struct port_proxy *port_prox = t7xx_dev->md->port_prox;
541 	struct t7xx_port *port;
542 	int i;
543 
544 	for_each_proxy_port(i, port, port_prox) {
545 		const struct t7xx_port_conf *port_conf = port->port_conf;
546 
547 		if (port_conf->debug && port_conf->ops) {
548 			if (show && port_conf->ops->init)
549 				port_conf->ops->init(port);
550 			else if (!show && port_conf->ops->uninit)
551 				port_conf->ops->uninit(port);
552 		}
553 	}
554 }
555 
556 void t7xx_port_proxy_set_cfg(struct t7xx_modem *md, enum port_cfg_id cfg_id)
557 {
558 	struct port_proxy *port_prox = md->port_prox;
559 	const struct t7xx_port_conf *port_conf;
560 	u32 port_count;
561 	int i;
562 
563 	t7xx_port_proxy_uninit(port_prox);
564 
565 	if (cfg_id == PORT_CFG_ID_EARLY) {
566 		port_conf = t7xx_early_port_conf;
567 		port_count = ARRAY_SIZE(t7xx_early_port_conf);
568 	} else {
569 		port_conf = t7xx_port_conf;
570 		port_count = ARRAY_SIZE(t7xx_port_conf);
571 	}
572 
573 	for (i = 0; i < port_count; i++)
574 		port_prox->ports[i].port_conf = &port_conf[i];
575 
576 	port_prox->cfg_id = cfg_id;
577 	port_prox->port_count = port_count;
578 
579 	t7xx_proxy_init_all_ports(md);
580 }
581 
582 static int t7xx_proxy_alloc(struct t7xx_modem *md)
583 {
584 	struct device *dev = &md->t7xx_dev->pdev->dev;
585 	struct port_proxy *port_prox;
586 
587 	port_prox = devm_kzalloc(dev,
588 				 struct_size(port_prox,
589 					     ports,
590 					     T7XX_MAX_POSSIBLE_PORTS_NUM),
591 				 GFP_KERNEL);
592 	if (!port_prox)
593 		return -ENOMEM;
594 
595 	md->port_prox = port_prox;
596 	port_prox->dev = dev;
597 
598 	return 0;
599 }
600 
601 /**
602  * t7xx_port_proxy_init() - Initialize ports.
603  * @md: Modem.
604  *
605  * Create all port instances.
606  *
607  * Return:
608  * * 0		- Success.
609  * * -ERROR	- Error code from failure sub-initializations.
610  */
611 int t7xx_port_proxy_init(struct t7xx_modem *md)
612 {
613 	int ret;
614 
615 	ret = t7xx_proxy_alloc(md);
616 	if (ret)
617 		return ret;
618 
619 	return 0;
620 }
621 
622 void t7xx_port_proxy_uninit(struct port_proxy *port_prox)
623 {
624 	struct t7xx_port *port;
625 	int i;
626 
627 	for_each_proxy_port(i, port, port_prox) {
628 		const struct t7xx_port_conf *port_conf = port->port_conf;
629 
630 		if (port_conf->ops && port_conf->ops->uninit)
631 			port_conf->ops->uninit(port);
632 	}
633 }
634 
635 int t7xx_port_proxy_chl_enable_disable(struct port_proxy *port_prox, unsigned int ch_id,
636 				       bool en_flag)
637 {
638 	struct t7xx_port *port = t7xx_proxy_get_port_by_ch(port_prox, ch_id);
639 	const struct t7xx_port_conf *port_conf;
640 
641 	if (!port)
642 		return -EINVAL;
643 
644 	port_conf = port->port_conf;
645 
646 	if (en_flag) {
647 		if (port_conf->ops->enable_chl)
648 			port_conf->ops->enable_chl(port);
649 	} else {
650 		if (port_conf->ops->disable_chl)
651 			port_conf->ops->disable_chl(port);
652 	}
653 
654 	return 0;
655 }
656