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