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 5148cc2f5eSHaijun Liu static const struct t7xx_port_conf t7xx_md_port_conf[] = { 52da45d256SHaijun Liu { 53*61b7a291SChandrashekar Devegowda .tx_ch = PORT_CH_UART2_TX, 54*61b7a291SChandrashekar Devegowda .rx_ch = PORT_CH_UART2_RX, 55*61b7a291SChandrashekar Devegowda .txq_index = Q_IDX_AT_CMD, 56*61b7a291SChandrashekar Devegowda .rxq_index = Q_IDX_AT_CMD, 57*61b7a291SChandrashekar Devegowda .txq_exp_index = 0xff, 58*61b7a291SChandrashekar Devegowda .rxq_exp_index = 0xff, 59*61b7a291SChandrashekar Devegowda .path_id = CLDMA_ID_MD, 60*61b7a291SChandrashekar Devegowda .ops = &wwan_sub_port_ops, 61*61b7a291SChandrashekar Devegowda .name = "AT", 62*61b7a291SChandrashekar Devegowda .port_type = WWAN_PORT_AT, 63*61b7a291SChandrashekar Devegowda }, { 64*61b7a291SChandrashekar Devegowda .tx_ch = PORT_CH_MBIM_TX, 65*61b7a291SChandrashekar Devegowda .rx_ch = PORT_CH_MBIM_RX, 66*61b7a291SChandrashekar Devegowda .txq_index = Q_IDX_MBIM, 67*61b7a291SChandrashekar Devegowda .rxq_index = Q_IDX_MBIM, 68*61b7a291SChandrashekar Devegowda .path_id = CLDMA_ID_MD, 69*61b7a291SChandrashekar Devegowda .ops = &wwan_sub_port_ops, 70*61b7a291SChandrashekar Devegowda .name = "MBIM", 71*61b7a291SChandrashekar Devegowda .port_type = WWAN_PORT_MBIM, 72*61b7a291SChandrashekar Devegowda }, { 73da45d256SHaijun Liu .tx_ch = PORT_CH_CONTROL_TX, 74da45d256SHaijun Liu .rx_ch = PORT_CH_CONTROL_RX, 75da45d256SHaijun Liu .txq_index = Q_IDX_CTRL, 76da45d256SHaijun Liu .rxq_index = Q_IDX_CTRL, 77da45d256SHaijun Liu .path_id = CLDMA_ID_MD, 78da45d256SHaijun Liu .ops = &ctl_port_ops, 79da45d256SHaijun Liu .name = "t7xx_ctrl", 80da45d256SHaijun Liu }, 8148cc2f5eSHaijun Liu }; 8248cc2f5eSHaijun Liu 8348cc2f5eSHaijun Liu static struct t7xx_port *t7xx_proxy_get_port_by_ch(struct port_proxy *port_prox, enum port_ch ch) 8448cc2f5eSHaijun Liu { 8548cc2f5eSHaijun Liu const struct t7xx_port_conf *port_conf; 8648cc2f5eSHaijun Liu struct t7xx_port *port; 8748cc2f5eSHaijun Liu int i; 8848cc2f5eSHaijun Liu 8948cc2f5eSHaijun Liu for_each_proxy_port(i, port, port_prox) { 9048cc2f5eSHaijun Liu port_conf = port->port_conf; 9148cc2f5eSHaijun Liu if (port_conf->rx_ch == ch || port_conf->tx_ch == ch) 9248cc2f5eSHaijun Liu return port; 9348cc2f5eSHaijun Liu } 9448cc2f5eSHaijun Liu 9548cc2f5eSHaijun Liu return NULL; 9648cc2f5eSHaijun Liu } 9748cc2f5eSHaijun Liu 9848cc2f5eSHaijun Liu static u16 t7xx_port_next_rx_seq_num(struct t7xx_port *port, struct ccci_header *ccci_h) 9948cc2f5eSHaijun Liu { 10048cc2f5eSHaijun Liu u32 status = le32_to_cpu(ccci_h->status); 10148cc2f5eSHaijun Liu u16 seq_num, next_seq_num; 10248cc2f5eSHaijun Liu bool assert_bit; 10348cc2f5eSHaijun Liu 10448cc2f5eSHaijun Liu seq_num = FIELD_GET(CCCI_H_SEQ_FLD, status); 10548cc2f5eSHaijun Liu next_seq_num = (seq_num + 1) & FIELD_MAX(CCCI_H_SEQ_FLD); 10648cc2f5eSHaijun Liu assert_bit = status & CCCI_H_AST_BIT; 10748cc2f5eSHaijun Liu if (!assert_bit || port->seq_nums[MTK_RX] == INVALID_SEQ_NUM) 10848cc2f5eSHaijun Liu return next_seq_num; 10948cc2f5eSHaijun Liu 11048cc2f5eSHaijun Liu if (seq_num != port->seq_nums[MTK_RX]) 11148cc2f5eSHaijun Liu dev_warn_ratelimited(port->dev, 11248cc2f5eSHaijun Liu "seq num out-of-order %u != %u (header %X, len %X)\n", 11348cc2f5eSHaijun Liu seq_num, port->seq_nums[MTK_RX], 11448cc2f5eSHaijun Liu le32_to_cpu(ccci_h->packet_header), 11548cc2f5eSHaijun Liu le32_to_cpu(ccci_h->packet_len)); 11648cc2f5eSHaijun Liu 11748cc2f5eSHaijun Liu return next_seq_num; 11848cc2f5eSHaijun Liu } 11948cc2f5eSHaijun Liu 12048cc2f5eSHaijun Liu void t7xx_port_proxy_reset(struct port_proxy *port_prox) 12148cc2f5eSHaijun Liu { 12248cc2f5eSHaijun Liu struct t7xx_port *port; 12348cc2f5eSHaijun Liu int i; 12448cc2f5eSHaijun Liu 12548cc2f5eSHaijun Liu for_each_proxy_port(i, port, port_prox) { 12648cc2f5eSHaijun Liu port->seq_nums[MTK_RX] = INVALID_SEQ_NUM; 12748cc2f5eSHaijun Liu port->seq_nums[MTK_TX] = 0; 12848cc2f5eSHaijun Liu } 12948cc2f5eSHaijun Liu } 13048cc2f5eSHaijun Liu 13148cc2f5eSHaijun Liu static int t7xx_port_get_queue_no(struct t7xx_port *port) 13248cc2f5eSHaijun Liu { 13348cc2f5eSHaijun Liu const struct t7xx_port_conf *port_conf = port->port_conf; 13448cc2f5eSHaijun Liu struct t7xx_fsm_ctl *ctl = port->t7xx_dev->md->fsm_ctl; 13548cc2f5eSHaijun Liu 13648cc2f5eSHaijun Liu return t7xx_fsm_get_md_state(ctl) == MD_STATE_EXCEPTION ? 13748cc2f5eSHaijun Liu port_conf->txq_exp_index : port_conf->txq_index; 13848cc2f5eSHaijun Liu } 13948cc2f5eSHaijun Liu 14048cc2f5eSHaijun Liu static void t7xx_port_struct_init(struct t7xx_port *port) 14148cc2f5eSHaijun Liu { 14248cc2f5eSHaijun Liu INIT_LIST_HEAD(&port->entry); 14348cc2f5eSHaijun Liu INIT_LIST_HEAD(&port->queue_entry); 14448cc2f5eSHaijun Liu skb_queue_head_init(&port->rx_skb_list); 14548cc2f5eSHaijun Liu init_waitqueue_head(&port->rx_wq); 14648cc2f5eSHaijun Liu port->seq_nums[MTK_RX] = INVALID_SEQ_NUM; 14748cc2f5eSHaijun Liu port->seq_nums[MTK_TX] = 0; 14848cc2f5eSHaijun Liu atomic_set(&port->usage_cnt, 0); 14948cc2f5eSHaijun Liu } 15048cc2f5eSHaijun Liu 15148cc2f5eSHaijun Liu struct sk_buff *t7xx_port_alloc_skb(int payload) 15248cc2f5eSHaijun Liu { 15348cc2f5eSHaijun Liu struct sk_buff *skb = __dev_alloc_skb(payload + sizeof(struct ccci_header), GFP_KERNEL); 15448cc2f5eSHaijun Liu 15548cc2f5eSHaijun Liu if (skb) 15648cc2f5eSHaijun Liu skb_reserve(skb, sizeof(struct ccci_header)); 15748cc2f5eSHaijun Liu 15848cc2f5eSHaijun Liu return skb; 15948cc2f5eSHaijun Liu } 16048cc2f5eSHaijun Liu 161da45d256SHaijun Liu struct sk_buff *t7xx_ctrl_alloc_skb(int payload) 162da45d256SHaijun Liu { 163da45d256SHaijun Liu struct sk_buff *skb = t7xx_port_alloc_skb(payload + sizeof(struct ctrl_msg_header)); 164da45d256SHaijun Liu 165da45d256SHaijun Liu if (skb) 166da45d256SHaijun Liu skb_reserve(skb, sizeof(struct ctrl_msg_header)); 167da45d256SHaijun Liu 168da45d256SHaijun Liu return skb; 169da45d256SHaijun Liu } 170da45d256SHaijun Liu 17148cc2f5eSHaijun Liu /** 17248cc2f5eSHaijun Liu * t7xx_port_enqueue_skb() - Enqueue the received skb into the port's rx_skb_list. 17348cc2f5eSHaijun Liu * @port: port context. 17448cc2f5eSHaijun Liu * @skb: received skb. 17548cc2f5eSHaijun Liu * 17648cc2f5eSHaijun Liu * Return: 17748cc2f5eSHaijun Liu * * 0 - Success. 17848cc2f5eSHaijun Liu * * -ENOBUFS - Not enough buffer space. Caller will try again later, skb is not consumed. 17948cc2f5eSHaijun Liu */ 18048cc2f5eSHaijun Liu int t7xx_port_enqueue_skb(struct t7xx_port *port, struct sk_buff *skb) 18148cc2f5eSHaijun Liu { 18248cc2f5eSHaijun Liu unsigned long flags; 18348cc2f5eSHaijun Liu 18448cc2f5eSHaijun Liu spin_lock_irqsave(&port->rx_wq.lock, flags); 18548cc2f5eSHaijun Liu if (port->rx_skb_list.qlen >= port->rx_length_th) { 18648cc2f5eSHaijun Liu spin_unlock_irqrestore(&port->rx_wq.lock, flags); 18748cc2f5eSHaijun Liu 18848cc2f5eSHaijun Liu return -ENOBUFS; 18948cc2f5eSHaijun Liu } 19048cc2f5eSHaijun Liu __skb_queue_tail(&port->rx_skb_list, skb); 19148cc2f5eSHaijun Liu spin_unlock_irqrestore(&port->rx_wq.lock, flags); 19248cc2f5eSHaijun Liu 19348cc2f5eSHaijun Liu wake_up_all(&port->rx_wq); 19448cc2f5eSHaijun Liu return 0; 19548cc2f5eSHaijun Liu } 19648cc2f5eSHaijun Liu 19748cc2f5eSHaijun Liu static int t7xx_port_send_raw_skb(struct t7xx_port *port, struct sk_buff *skb) 19848cc2f5eSHaijun Liu { 19948cc2f5eSHaijun Liu enum cldma_id path_id = port->port_conf->path_id; 20048cc2f5eSHaijun Liu struct cldma_ctrl *md_ctrl; 20148cc2f5eSHaijun Liu int ret, tx_qno; 20248cc2f5eSHaijun Liu 20348cc2f5eSHaijun Liu md_ctrl = port->t7xx_dev->md->md_ctrl[path_id]; 20448cc2f5eSHaijun Liu tx_qno = t7xx_port_get_queue_no(port); 20548cc2f5eSHaijun Liu ret = t7xx_cldma_send_skb(md_ctrl, tx_qno, skb); 20648cc2f5eSHaijun Liu if (ret) 20748cc2f5eSHaijun Liu dev_err(port->dev, "Failed to send skb: %d\n", ret); 20848cc2f5eSHaijun Liu 20948cc2f5eSHaijun Liu return ret; 21048cc2f5eSHaijun Liu } 21148cc2f5eSHaijun Liu 21248cc2f5eSHaijun Liu static int t7xx_port_send_ccci_skb(struct t7xx_port *port, struct sk_buff *skb, 21348cc2f5eSHaijun Liu unsigned int pkt_header, unsigned int ex_msg) 21448cc2f5eSHaijun Liu { 21548cc2f5eSHaijun Liu const struct t7xx_port_conf *port_conf = port->port_conf; 21648cc2f5eSHaijun Liu struct ccci_header *ccci_h; 21748cc2f5eSHaijun Liu u32 status; 21848cc2f5eSHaijun Liu int ret; 21948cc2f5eSHaijun Liu 22048cc2f5eSHaijun Liu ccci_h = skb_push(skb, sizeof(*ccci_h)); 22148cc2f5eSHaijun Liu status = FIELD_PREP(CCCI_H_CHN_FLD, port_conf->tx_ch) | 22248cc2f5eSHaijun Liu FIELD_PREP(CCCI_H_SEQ_FLD, port->seq_nums[MTK_TX]) | CCCI_H_AST_BIT; 22348cc2f5eSHaijun Liu ccci_h->status = cpu_to_le32(status); 22448cc2f5eSHaijun Liu ccci_h->packet_header = cpu_to_le32(pkt_header); 22548cc2f5eSHaijun Liu ccci_h->packet_len = cpu_to_le32(skb->len); 22648cc2f5eSHaijun Liu ccci_h->ex_msg = cpu_to_le32(ex_msg); 22748cc2f5eSHaijun Liu 22848cc2f5eSHaijun Liu ret = t7xx_port_send_raw_skb(port, skb); 22948cc2f5eSHaijun Liu if (ret) 23048cc2f5eSHaijun Liu return ret; 23148cc2f5eSHaijun Liu 23248cc2f5eSHaijun Liu port->seq_nums[MTK_TX]++; 23348cc2f5eSHaijun Liu return 0; 23448cc2f5eSHaijun Liu } 23548cc2f5eSHaijun Liu 236da45d256SHaijun Liu int t7xx_port_send_ctl_skb(struct t7xx_port *port, struct sk_buff *skb, unsigned int msg, 237da45d256SHaijun Liu unsigned int ex_msg) 238da45d256SHaijun Liu { 239da45d256SHaijun Liu struct ctrl_msg_header *ctrl_msg_h; 240da45d256SHaijun Liu unsigned int msg_len = skb->len; 241da45d256SHaijun Liu u32 pkt_header = 0; 242da45d256SHaijun Liu 243da45d256SHaijun Liu ctrl_msg_h = skb_push(skb, sizeof(*ctrl_msg_h)); 244da45d256SHaijun Liu ctrl_msg_h->ctrl_msg_id = cpu_to_le32(msg); 245da45d256SHaijun Liu ctrl_msg_h->ex_msg = cpu_to_le32(ex_msg); 246da45d256SHaijun Liu ctrl_msg_h->data_length = cpu_to_le32(msg_len); 247da45d256SHaijun Liu 248da45d256SHaijun Liu if (!msg_len) 249da45d256SHaijun Liu pkt_header = CCCI_HEADER_NO_DATA; 250da45d256SHaijun Liu 251da45d256SHaijun Liu return t7xx_port_send_ccci_skb(port, skb, pkt_header, ex_msg); 252da45d256SHaijun Liu } 253da45d256SHaijun Liu 25448cc2f5eSHaijun Liu int t7xx_port_send_skb(struct t7xx_port *port, struct sk_buff *skb, unsigned int pkt_header, 25548cc2f5eSHaijun Liu unsigned int ex_msg) 25648cc2f5eSHaijun Liu { 25748cc2f5eSHaijun Liu struct t7xx_fsm_ctl *ctl = port->t7xx_dev->md->fsm_ctl; 25848cc2f5eSHaijun Liu unsigned int fsm_state; 25948cc2f5eSHaijun Liu 26048cc2f5eSHaijun Liu fsm_state = t7xx_fsm_get_ctl_state(ctl); 26148cc2f5eSHaijun Liu if (fsm_state != FSM_STATE_PRE_START) { 26248cc2f5eSHaijun Liu const struct t7xx_port_conf *port_conf = port->port_conf; 26348cc2f5eSHaijun Liu enum md_state md_state = t7xx_fsm_get_md_state(ctl); 26448cc2f5eSHaijun Liu 26548cc2f5eSHaijun Liu switch (md_state) { 26648cc2f5eSHaijun Liu case MD_STATE_EXCEPTION: 26748cc2f5eSHaijun Liu if (port_conf->tx_ch != PORT_CH_MD_LOG_TX) 26848cc2f5eSHaijun Liu return -EBUSY; 26948cc2f5eSHaijun Liu break; 27048cc2f5eSHaijun Liu 27148cc2f5eSHaijun Liu case MD_STATE_WAITING_FOR_HS1: 27248cc2f5eSHaijun Liu case MD_STATE_WAITING_FOR_HS2: 27348cc2f5eSHaijun Liu case MD_STATE_STOPPED: 27448cc2f5eSHaijun Liu case MD_STATE_WAITING_TO_STOP: 27548cc2f5eSHaijun Liu case MD_STATE_INVALID: 27648cc2f5eSHaijun Liu return -ENODEV; 27748cc2f5eSHaijun Liu 27848cc2f5eSHaijun Liu default: 27948cc2f5eSHaijun Liu break; 28048cc2f5eSHaijun Liu } 28148cc2f5eSHaijun Liu } 28248cc2f5eSHaijun Liu 28348cc2f5eSHaijun Liu return t7xx_port_send_ccci_skb(port, skb, pkt_header, ex_msg); 28448cc2f5eSHaijun Liu } 28548cc2f5eSHaijun Liu 28648cc2f5eSHaijun Liu static void t7xx_proxy_setup_ch_mapping(struct port_proxy *port_prox) 28748cc2f5eSHaijun Liu { 28848cc2f5eSHaijun Liu struct t7xx_port *port; 28948cc2f5eSHaijun Liu 29048cc2f5eSHaijun Liu int i, j; 29148cc2f5eSHaijun Liu 29248cc2f5eSHaijun Liu for (i = 0; i < ARRAY_SIZE(port_prox->rx_ch_ports); i++) 29348cc2f5eSHaijun Liu INIT_LIST_HEAD(&port_prox->rx_ch_ports[i]); 29448cc2f5eSHaijun Liu 29548cc2f5eSHaijun Liu for (j = 0; j < ARRAY_SIZE(port_prox->queue_ports); j++) { 29648cc2f5eSHaijun Liu for (i = 0; i < ARRAY_SIZE(port_prox->queue_ports[j]); i++) 29748cc2f5eSHaijun Liu INIT_LIST_HEAD(&port_prox->queue_ports[j][i]); 29848cc2f5eSHaijun Liu } 29948cc2f5eSHaijun Liu 30048cc2f5eSHaijun Liu for_each_proxy_port(i, port, port_prox) { 30148cc2f5eSHaijun Liu const struct t7xx_port_conf *port_conf = port->port_conf; 30248cc2f5eSHaijun Liu enum cldma_id path_id = port_conf->path_id; 30348cc2f5eSHaijun Liu u8 ch_id; 30448cc2f5eSHaijun Liu 30548cc2f5eSHaijun Liu ch_id = FIELD_GET(PORT_CH_ID_MASK, port_conf->rx_ch); 30648cc2f5eSHaijun Liu list_add_tail(&port->entry, &port_prox->rx_ch_ports[ch_id]); 30748cc2f5eSHaijun Liu list_add_tail(&port->queue_entry, 30848cc2f5eSHaijun Liu &port_prox->queue_ports[path_id][port_conf->rxq_index]); 30948cc2f5eSHaijun Liu } 31048cc2f5eSHaijun Liu } 31148cc2f5eSHaijun Liu 31248cc2f5eSHaijun Liu static struct t7xx_port *t7xx_port_proxy_find_port(struct t7xx_pci_dev *t7xx_dev, 31348cc2f5eSHaijun Liu struct cldma_queue *queue, u16 channel) 31448cc2f5eSHaijun Liu { 31548cc2f5eSHaijun Liu struct port_proxy *port_prox = t7xx_dev->md->port_prox; 31648cc2f5eSHaijun Liu struct list_head *port_list; 31748cc2f5eSHaijun Liu struct t7xx_port *port; 31848cc2f5eSHaijun Liu u8 ch_id; 31948cc2f5eSHaijun Liu 32048cc2f5eSHaijun Liu ch_id = FIELD_GET(PORT_CH_ID_MASK, channel); 32148cc2f5eSHaijun Liu port_list = &port_prox->rx_ch_ports[ch_id]; 32248cc2f5eSHaijun Liu list_for_each_entry(port, port_list, entry) { 32348cc2f5eSHaijun Liu const struct t7xx_port_conf *port_conf = port->port_conf; 32448cc2f5eSHaijun Liu 32548cc2f5eSHaijun Liu if (queue->md_ctrl->hif_id == port_conf->path_id && 32648cc2f5eSHaijun Liu channel == port_conf->rx_ch) 32748cc2f5eSHaijun Liu return port; 32848cc2f5eSHaijun Liu } 32948cc2f5eSHaijun Liu 33048cc2f5eSHaijun Liu return NULL; 33148cc2f5eSHaijun Liu } 33248cc2f5eSHaijun Liu 33348cc2f5eSHaijun Liu /** 33448cc2f5eSHaijun Liu * t7xx_port_proxy_recv_skb() - Dispatch received skb. 33548cc2f5eSHaijun Liu * @queue: CLDMA queue. 33648cc2f5eSHaijun Liu * @skb: Socket buffer. 33748cc2f5eSHaijun Liu * 33848cc2f5eSHaijun Liu * Return: 33948cc2f5eSHaijun Liu ** 0 - Packet consumed. 34048cc2f5eSHaijun Liu ** -ERROR - Failed to process skb. 34148cc2f5eSHaijun Liu */ 34248cc2f5eSHaijun Liu static int t7xx_port_proxy_recv_skb(struct cldma_queue *queue, struct sk_buff *skb) 34348cc2f5eSHaijun Liu { 34448cc2f5eSHaijun Liu struct ccci_header *ccci_h = (struct ccci_header *)skb->data; 34548cc2f5eSHaijun Liu struct t7xx_pci_dev *t7xx_dev = queue->md_ctrl->t7xx_dev; 34648cc2f5eSHaijun Liu struct t7xx_fsm_ctl *ctl = t7xx_dev->md->fsm_ctl; 34748cc2f5eSHaijun Liu struct device *dev = queue->md_ctrl->dev; 34848cc2f5eSHaijun Liu const struct t7xx_port_conf *port_conf; 34948cc2f5eSHaijun Liu struct t7xx_port *port; 35048cc2f5eSHaijun Liu u16 seq_num, channel; 35148cc2f5eSHaijun Liu int ret; 35248cc2f5eSHaijun Liu 35348cc2f5eSHaijun Liu if (!skb) 35448cc2f5eSHaijun Liu return -EINVAL; 35548cc2f5eSHaijun Liu 35648cc2f5eSHaijun Liu channel = FIELD_GET(CCCI_H_CHN_FLD, le32_to_cpu(ccci_h->status)); 35748cc2f5eSHaijun Liu if (t7xx_fsm_get_md_state(ctl) == MD_STATE_INVALID) { 35848cc2f5eSHaijun Liu dev_err_ratelimited(dev, "Packet drop on channel 0x%x, modem not ready\n", channel); 35948cc2f5eSHaijun Liu goto drop_skb; 36048cc2f5eSHaijun Liu } 36148cc2f5eSHaijun Liu 36248cc2f5eSHaijun Liu port = t7xx_port_proxy_find_port(t7xx_dev, queue, channel); 36348cc2f5eSHaijun Liu if (!port) { 36448cc2f5eSHaijun Liu dev_err_ratelimited(dev, "Packet drop on channel 0x%x, port not found\n", channel); 36548cc2f5eSHaijun Liu goto drop_skb; 36648cc2f5eSHaijun Liu } 36748cc2f5eSHaijun Liu 36848cc2f5eSHaijun Liu seq_num = t7xx_port_next_rx_seq_num(port, ccci_h); 36948cc2f5eSHaijun Liu port_conf = port->port_conf; 37048cc2f5eSHaijun Liu skb_pull(skb, sizeof(*ccci_h)); 37148cc2f5eSHaijun Liu 37248cc2f5eSHaijun Liu ret = port_conf->ops->recv_skb(port, skb); 37348cc2f5eSHaijun Liu /* Error indicates to try again later */ 37448cc2f5eSHaijun Liu if (ret) { 37548cc2f5eSHaijun Liu skb_push(skb, sizeof(*ccci_h)); 37648cc2f5eSHaijun Liu return ret; 37748cc2f5eSHaijun Liu } 37848cc2f5eSHaijun Liu 37948cc2f5eSHaijun Liu port->seq_nums[MTK_RX] = seq_num; 38048cc2f5eSHaijun Liu return 0; 38148cc2f5eSHaijun Liu 38248cc2f5eSHaijun Liu drop_skb: 38348cc2f5eSHaijun Liu dev_kfree_skb_any(skb); 38448cc2f5eSHaijun Liu return 0; 38548cc2f5eSHaijun Liu } 38648cc2f5eSHaijun Liu 38748cc2f5eSHaijun Liu /** 38848cc2f5eSHaijun Liu * t7xx_port_proxy_md_status_notify() - Notify all ports of state. 38948cc2f5eSHaijun Liu *@port_prox: The port_proxy pointer. 39048cc2f5eSHaijun Liu *@state: State. 39148cc2f5eSHaijun Liu * 39248cc2f5eSHaijun Liu * Called by t7xx_fsm. Used to dispatch modem status for all ports, 39348cc2f5eSHaijun Liu * which want to know MD state transition. 39448cc2f5eSHaijun Liu */ 39548cc2f5eSHaijun Liu void t7xx_port_proxy_md_status_notify(struct port_proxy *port_prox, unsigned int state) 39648cc2f5eSHaijun Liu { 39748cc2f5eSHaijun Liu struct t7xx_port *port; 39848cc2f5eSHaijun Liu int i; 39948cc2f5eSHaijun Liu 40048cc2f5eSHaijun Liu for_each_proxy_port(i, port, port_prox) { 40148cc2f5eSHaijun Liu const struct t7xx_port_conf *port_conf = port->port_conf; 40248cc2f5eSHaijun Liu 40348cc2f5eSHaijun Liu if (port_conf->ops->md_state_notify) 40448cc2f5eSHaijun Liu port_conf->ops->md_state_notify(port, state); 40548cc2f5eSHaijun Liu } 40648cc2f5eSHaijun Liu } 40748cc2f5eSHaijun Liu 40848cc2f5eSHaijun Liu static void t7xx_proxy_init_all_ports(struct t7xx_modem *md) 40948cc2f5eSHaijun Liu { 41048cc2f5eSHaijun Liu struct port_proxy *port_prox = md->port_prox; 41148cc2f5eSHaijun Liu struct t7xx_port *port; 41248cc2f5eSHaijun Liu int i; 41348cc2f5eSHaijun Liu 41448cc2f5eSHaijun Liu for_each_proxy_port(i, port, port_prox) { 41548cc2f5eSHaijun Liu const struct t7xx_port_conf *port_conf = port->port_conf; 41648cc2f5eSHaijun Liu 41748cc2f5eSHaijun Liu t7xx_port_struct_init(port); 41848cc2f5eSHaijun Liu 419da45d256SHaijun Liu if (port_conf->tx_ch == PORT_CH_CONTROL_TX) 420da45d256SHaijun Liu md->core_md.ctl_port = port; 421da45d256SHaijun Liu 42248cc2f5eSHaijun Liu port->t7xx_dev = md->t7xx_dev; 42348cc2f5eSHaijun Liu port->dev = &md->t7xx_dev->pdev->dev; 42448cc2f5eSHaijun Liu spin_lock_init(&port->port_update_lock); 42548cc2f5eSHaijun Liu port->chan_enable = false; 42648cc2f5eSHaijun Liu 42748cc2f5eSHaijun Liu if (port_conf->ops->init) 42848cc2f5eSHaijun Liu port_conf->ops->init(port); 42948cc2f5eSHaijun Liu } 43048cc2f5eSHaijun Liu 43148cc2f5eSHaijun Liu t7xx_proxy_setup_ch_mapping(port_prox); 43248cc2f5eSHaijun Liu } 43348cc2f5eSHaijun Liu 43448cc2f5eSHaijun Liu static int t7xx_proxy_alloc(struct t7xx_modem *md) 43548cc2f5eSHaijun Liu { 43648cc2f5eSHaijun Liu unsigned int port_count = ARRAY_SIZE(t7xx_md_port_conf); 43748cc2f5eSHaijun Liu struct device *dev = &md->t7xx_dev->pdev->dev; 43848cc2f5eSHaijun Liu struct port_proxy *port_prox; 43948cc2f5eSHaijun Liu int i; 44048cc2f5eSHaijun Liu 44148cc2f5eSHaijun Liu port_prox = devm_kzalloc(dev, sizeof(*port_prox) + sizeof(struct t7xx_port) * port_count, 44248cc2f5eSHaijun Liu GFP_KERNEL); 44348cc2f5eSHaijun Liu if (!port_prox) 44448cc2f5eSHaijun Liu return -ENOMEM; 44548cc2f5eSHaijun Liu 44648cc2f5eSHaijun Liu md->port_prox = port_prox; 44748cc2f5eSHaijun Liu port_prox->dev = dev; 44848cc2f5eSHaijun Liu 44948cc2f5eSHaijun Liu for (i = 0; i < port_count; i++) 45048cc2f5eSHaijun Liu port_prox->ports[i].port_conf = &t7xx_md_port_conf[i]; 45148cc2f5eSHaijun Liu 45248cc2f5eSHaijun Liu port_prox->port_count = port_count; 45348cc2f5eSHaijun Liu t7xx_proxy_init_all_ports(md); 45448cc2f5eSHaijun Liu return 0; 45548cc2f5eSHaijun Liu } 45648cc2f5eSHaijun Liu 45748cc2f5eSHaijun Liu /** 45848cc2f5eSHaijun Liu * t7xx_port_proxy_init() - Initialize ports. 45948cc2f5eSHaijun Liu * @md: Modem. 46048cc2f5eSHaijun Liu * 46148cc2f5eSHaijun Liu * Create all port instances. 46248cc2f5eSHaijun Liu * 46348cc2f5eSHaijun Liu * Return: 46448cc2f5eSHaijun Liu * * 0 - Success. 46548cc2f5eSHaijun Liu * * -ERROR - Error code from failure sub-initializations. 46648cc2f5eSHaijun Liu */ 46748cc2f5eSHaijun Liu int t7xx_port_proxy_init(struct t7xx_modem *md) 46848cc2f5eSHaijun Liu { 46948cc2f5eSHaijun Liu int ret; 47048cc2f5eSHaijun Liu 47148cc2f5eSHaijun Liu ret = t7xx_proxy_alloc(md); 47248cc2f5eSHaijun Liu if (ret) 47348cc2f5eSHaijun Liu return ret; 47448cc2f5eSHaijun Liu 47548cc2f5eSHaijun Liu t7xx_cldma_set_recv_skb(md->md_ctrl[CLDMA_ID_MD], t7xx_port_proxy_recv_skb); 47648cc2f5eSHaijun Liu return 0; 47748cc2f5eSHaijun Liu } 47848cc2f5eSHaijun Liu 47948cc2f5eSHaijun Liu void t7xx_port_proxy_uninit(struct port_proxy *port_prox) 48048cc2f5eSHaijun Liu { 48148cc2f5eSHaijun Liu struct t7xx_port *port; 48248cc2f5eSHaijun Liu int i; 48348cc2f5eSHaijun Liu 48448cc2f5eSHaijun Liu for_each_proxy_port(i, port, port_prox) { 48548cc2f5eSHaijun Liu const struct t7xx_port_conf *port_conf = port->port_conf; 48648cc2f5eSHaijun Liu 48748cc2f5eSHaijun Liu if (port_conf->ops->uninit) 48848cc2f5eSHaijun Liu port_conf->ops->uninit(port); 48948cc2f5eSHaijun Liu } 49048cc2f5eSHaijun Liu } 49148cc2f5eSHaijun Liu 49248cc2f5eSHaijun Liu int t7xx_port_proxy_chl_enable_disable(struct port_proxy *port_prox, unsigned int ch_id, 49348cc2f5eSHaijun Liu bool en_flag) 49448cc2f5eSHaijun Liu { 49548cc2f5eSHaijun Liu struct t7xx_port *port = t7xx_proxy_get_port_by_ch(port_prox, ch_id); 49648cc2f5eSHaijun Liu const struct t7xx_port_conf *port_conf; 49748cc2f5eSHaijun Liu 49848cc2f5eSHaijun Liu if (!port) 49948cc2f5eSHaijun Liu return -EINVAL; 50048cc2f5eSHaijun Liu 50148cc2f5eSHaijun Liu port_conf = port->port_conf; 50248cc2f5eSHaijun Liu 50348cc2f5eSHaijun Liu if (en_flag) { 50448cc2f5eSHaijun Liu if (port_conf->ops->enable_chl) 50548cc2f5eSHaijun Liu port_conf->ops->enable_chl(port); 50648cc2f5eSHaijun Liu } else { 50748cc2f5eSHaijun Liu if (port_conf->ops->disable_chl) 50848cc2f5eSHaijun Liu port_conf->ops->disable_chl(port); 50948cc2f5eSHaijun Liu } 51048cc2f5eSHaijun Liu 51148cc2f5eSHaijun Liu return 0; 51248cc2f5eSHaijun Liu } 513