162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (c) 2021, MediaTek Inc. 462306a36Sopenharmony_ci * Copyright (c) 2021-2022, Intel Corporation. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Authors: 762306a36Sopenharmony_ci * Chandrashekar Devegowda <chandrashekar.devegowda@intel.com> 862306a36Sopenharmony_ci * Haijun Liu <haijun.liu@mediatek.com> 962306a36Sopenharmony_ci * Ricardo Martinez <ricardo.martinez@linux.intel.com> 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Contributors: 1262306a36Sopenharmony_ci * Amir Hanania <amir.hanania@intel.com> 1362306a36Sopenharmony_ci * Andy Shevchenko <andriy.shevchenko@linux.intel.com> 1462306a36Sopenharmony_ci * Chiranjeevi Rapolu <chiranjeevi.rapolu@intel.com> 1562306a36Sopenharmony_ci * Eliot Lee <eliot.lee@intel.com> 1662306a36Sopenharmony_ci * Moises Veleta <moises.veleta@intel.com> 1762306a36Sopenharmony_ci * Sreehari Kancharla <sreehari.kancharla@intel.com> 1862306a36Sopenharmony_ci */ 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/atomic.h> 2162306a36Sopenharmony_ci#include <linux/device.h> 2262306a36Sopenharmony_ci#include <linux/gfp.h> 2362306a36Sopenharmony_ci#include <linux/if_arp.h> 2462306a36Sopenharmony_ci#include <linux/if_ether.h> 2562306a36Sopenharmony_ci#include <linux/ip.h> 2662306a36Sopenharmony_ci#include <linux/kernel.h> 2762306a36Sopenharmony_ci#include <linux/list.h> 2862306a36Sopenharmony_ci#include <linux/netdev_features.h> 2962306a36Sopenharmony_ci#include <linux/netdevice.h> 3062306a36Sopenharmony_ci#include <linux/pm_runtime.h> 3162306a36Sopenharmony_ci#include <linux/skbuff.h> 3262306a36Sopenharmony_ci#include <linux/types.h> 3362306a36Sopenharmony_ci#include <linux/wwan.h> 3462306a36Sopenharmony_ci#include <net/ipv6.h> 3562306a36Sopenharmony_ci#include <net/pkt_sched.h> 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci#include "t7xx_hif_dpmaif_rx.h" 3862306a36Sopenharmony_ci#include "t7xx_hif_dpmaif_tx.h" 3962306a36Sopenharmony_ci#include "t7xx_netdev.h" 4062306a36Sopenharmony_ci#include "t7xx_pci.h" 4162306a36Sopenharmony_ci#include "t7xx_port_proxy.h" 4262306a36Sopenharmony_ci#include "t7xx_state_monitor.h" 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#define IP_MUX_SESSION_DEFAULT 0 4562306a36Sopenharmony_ci#define SBD_PACKET_TYPE_MASK GENMASK(7, 4) 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_cistatic void t7xx_ccmni_enable_napi(struct t7xx_ccmni_ctrl *ctlb) 4862306a36Sopenharmony_ci{ 4962306a36Sopenharmony_ci struct dpmaif_ctrl *ctrl; 5062306a36Sopenharmony_ci int i, ret; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci ctrl = ctlb->hif_ctrl; 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci if (ctlb->is_napi_en) 5562306a36Sopenharmony_ci return; 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci for (i = 0; i < RXQ_NUM; i++) { 5862306a36Sopenharmony_ci /* The usage count has to be bumped every time before calling 5962306a36Sopenharmony_ci * napi_schedule. It will be decresed in the poll routine, 6062306a36Sopenharmony_ci * right after napi_complete_done is called. 6162306a36Sopenharmony_ci */ 6262306a36Sopenharmony_ci ret = pm_runtime_resume_and_get(ctrl->dev); 6362306a36Sopenharmony_ci if (ret < 0) { 6462306a36Sopenharmony_ci dev_err(ctrl->dev, "Failed to resume device: %d\n", 6562306a36Sopenharmony_ci ret); 6662306a36Sopenharmony_ci return; 6762306a36Sopenharmony_ci } 6862306a36Sopenharmony_ci napi_enable(ctlb->napi[i]); 6962306a36Sopenharmony_ci napi_schedule(ctlb->napi[i]); 7062306a36Sopenharmony_ci } 7162306a36Sopenharmony_ci ctlb->is_napi_en = true; 7262306a36Sopenharmony_ci} 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_cistatic void t7xx_ccmni_disable_napi(struct t7xx_ccmni_ctrl *ctlb) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci int i; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci if (!ctlb->is_napi_en) 7962306a36Sopenharmony_ci return; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci for (i = 0; i < RXQ_NUM; i++) { 8262306a36Sopenharmony_ci napi_synchronize(ctlb->napi[i]); 8362306a36Sopenharmony_ci napi_disable(ctlb->napi[i]); 8462306a36Sopenharmony_ci } 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci ctlb->is_napi_en = false; 8762306a36Sopenharmony_ci} 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_cistatic int t7xx_ccmni_open(struct net_device *dev) 9062306a36Sopenharmony_ci{ 9162306a36Sopenharmony_ci struct t7xx_ccmni *ccmni = wwan_netdev_drvpriv(dev); 9262306a36Sopenharmony_ci struct t7xx_ccmni_ctrl *ccmni_ctl = ccmni->ctlb; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci netif_carrier_on(dev); 9562306a36Sopenharmony_ci netif_tx_start_all_queues(dev); 9662306a36Sopenharmony_ci if (!atomic_fetch_inc(&ccmni_ctl->napi_usr_refcnt)) 9762306a36Sopenharmony_ci t7xx_ccmni_enable_napi(ccmni_ctl); 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_ci atomic_inc(&ccmni->usage); 10062306a36Sopenharmony_ci return 0; 10162306a36Sopenharmony_ci} 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_cistatic int t7xx_ccmni_close(struct net_device *dev) 10462306a36Sopenharmony_ci{ 10562306a36Sopenharmony_ci struct t7xx_ccmni *ccmni = wwan_netdev_drvpriv(dev); 10662306a36Sopenharmony_ci struct t7xx_ccmni_ctrl *ccmni_ctl = ccmni->ctlb; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci atomic_dec(&ccmni->usage); 10962306a36Sopenharmony_ci if (atomic_dec_and_test(&ccmni_ctl->napi_usr_refcnt)) 11062306a36Sopenharmony_ci t7xx_ccmni_disable_napi(ccmni_ctl); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci netif_carrier_off(dev); 11362306a36Sopenharmony_ci netif_tx_disable(dev); 11462306a36Sopenharmony_ci return 0; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int t7xx_ccmni_send_packet(struct t7xx_ccmni *ccmni, struct sk_buff *skb, 11862306a36Sopenharmony_ci unsigned int txq_number) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct t7xx_ccmni_ctrl *ctlb = ccmni->ctlb; 12162306a36Sopenharmony_ci struct t7xx_skb_cb *skb_cb = T7XX_SKB_CB(skb); 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci skb_cb->netif_idx = ccmni->index; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci if (t7xx_dpmaif_tx_send_skb(ctlb->hif_ctrl, txq_number, skb)) 12662306a36Sopenharmony_ci return NETDEV_TX_BUSY; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci return 0; 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic netdev_tx_t t7xx_ccmni_start_xmit(struct sk_buff *skb, struct net_device *dev) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct t7xx_ccmni *ccmni = wwan_netdev_drvpriv(dev); 13462306a36Sopenharmony_ci int skb_len = skb->len; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci /* If MTU is changed or there is no headroom, drop the packet */ 13762306a36Sopenharmony_ci if (skb->len > dev->mtu || skb_headroom(skb) < sizeof(struct ccci_header)) { 13862306a36Sopenharmony_ci dev_kfree_skb(skb); 13962306a36Sopenharmony_ci dev->stats.tx_dropped++; 14062306a36Sopenharmony_ci return NETDEV_TX_OK; 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci if (t7xx_ccmni_send_packet(ccmni, skb, DPMAIF_TX_DEFAULT_QUEUE)) 14462306a36Sopenharmony_ci return NETDEV_TX_BUSY; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_ci dev->stats.tx_packets++; 14762306a36Sopenharmony_ci dev->stats.tx_bytes += skb_len; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci return NETDEV_TX_OK; 15062306a36Sopenharmony_ci} 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_cistatic void t7xx_ccmni_tx_timeout(struct net_device *dev, unsigned int __always_unused txqueue) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct t7xx_ccmni *ccmni = netdev_priv(dev); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci dev->stats.tx_errors++; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci if (atomic_read(&ccmni->usage) > 0) 15962306a36Sopenharmony_ci netif_tx_wake_all_queues(dev); 16062306a36Sopenharmony_ci} 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_cistatic const struct net_device_ops ccmni_netdev_ops = { 16362306a36Sopenharmony_ci .ndo_open = t7xx_ccmni_open, 16462306a36Sopenharmony_ci .ndo_stop = t7xx_ccmni_close, 16562306a36Sopenharmony_ci .ndo_start_xmit = t7xx_ccmni_start_xmit, 16662306a36Sopenharmony_ci .ndo_tx_timeout = t7xx_ccmni_tx_timeout, 16762306a36Sopenharmony_ci}; 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_cistatic void t7xx_ccmni_start(struct t7xx_ccmni_ctrl *ctlb) 17062306a36Sopenharmony_ci{ 17162306a36Sopenharmony_ci struct t7xx_ccmni *ccmni; 17262306a36Sopenharmony_ci int i; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci for (i = 0; i < ctlb->nic_dev_num; i++) { 17562306a36Sopenharmony_ci ccmni = ctlb->ccmni_inst[i]; 17662306a36Sopenharmony_ci if (!ccmni) 17762306a36Sopenharmony_ci continue; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (atomic_read(&ccmni->usage) > 0) { 18062306a36Sopenharmony_ci netif_tx_start_all_queues(ccmni->dev); 18162306a36Sopenharmony_ci netif_carrier_on(ccmni->dev); 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci if (atomic_read(&ctlb->napi_usr_refcnt)) 18662306a36Sopenharmony_ci t7xx_ccmni_enable_napi(ctlb); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic void t7xx_ccmni_pre_stop(struct t7xx_ccmni_ctrl *ctlb) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci struct t7xx_ccmni *ccmni; 19262306a36Sopenharmony_ci int i; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci for (i = 0; i < ctlb->nic_dev_num; i++) { 19562306a36Sopenharmony_ci ccmni = ctlb->ccmni_inst[i]; 19662306a36Sopenharmony_ci if (!ccmni) 19762306a36Sopenharmony_ci continue; 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci if (atomic_read(&ccmni->usage) > 0) 20062306a36Sopenharmony_ci netif_tx_disable(ccmni->dev); 20162306a36Sopenharmony_ci } 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic void t7xx_ccmni_post_stop(struct t7xx_ccmni_ctrl *ctlb) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci struct t7xx_ccmni *ccmni; 20762306a36Sopenharmony_ci int i; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if (atomic_read(&ctlb->napi_usr_refcnt)) 21062306a36Sopenharmony_ci t7xx_ccmni_disable_napi(ctlb); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci for (i = 0; i < ctlb->nic_dev_num; i++) { 21362306a36Sopenharmony_ci ccmni = ctlb->ccmni_inst[i]; 21462306a36Sopenharmony_ci if (!ccmni) 21562306a36Sopenharmony_ci continue; 21662306a36Sopenharmony_ci 21762306a36Sopenharmony_ci if (atomic_read(&ccmni->usage) > 0) 21862306a36Sopenharmony_ci netif_carrier_off(ccmni->dev); 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci} 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_cistatic void t7xx_ccmni_wwan_setup(struct net_device *dev) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci dev->needed_headroom += sizeof(struct ccci_header); 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci dev->mtu = ETH_DATA_LEN; 22762306a36Sopenharmony_ci dev->max_mtu = CCMNI_MTU_MAX; 22862306a36Sopenharmony_ci BUILD_BUG_ON(CCMNI_MTU_MAX > DPMAIF_HW_MTU_SIZE); 22962306a36Sopenharmony_ci 23062306a36Sopenharmony_ci dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN; 23162306a36Sopenharmony_ci dev->watchdog_timeo = CCMNI_NETDEV_WDT_TO; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci dev->flags = IFF_POINTOPOINT | IFF_NOARP; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci dev->features = NETIF_F_VLAN_CHALLENGED; 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci dev->features |= NETIF_F_SG; 23862306a36Sopenharmony_ci dev->hw_features |= NETIF_F_SG; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci dev->features |= NETIF_F_HW_CSUM; 24162306a36Sopenharmony_ci dev->hw_features |= NETIF_F_HW_CSUM; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci dev->features |= NETIF_F_RXCSUM; 24462306a36Sopenharmony_ci dev->hw_features |= NETIF_F_RXCSUM; 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci dev->features |= NETIF_F_GRO; 24762306a36Sopenharmony_ci dev->hw_features |= NETIF_F_GRO; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci dev->needs_free_netdev = true; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci dev->type = ARPHRD_NONE; 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci dev->netdev_ops = &ccmni_netdev_ops; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic void t7xx_init_netdev_napi(struct t7xx_ccmni_ctrl *ctlb) 25762306a36Sopenharmony_ci{ 25862306a36Sopenharmony_ci int i; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci /* one HW, but shared with multiple net devices, 26162306a36Sopenharmony_ci * so add a dummy device for NAPI. 26262306a36Sopenharmony_ci */ 26362306a36Sopenharmony_ci init_dummy_netdev(&ctlb->dummy_dev); 26462306a36Sopenharmony_ci atomic_set(&ctlb->napi_usr_refcnt, 0); 26562306a36Sopenharmony_ci ctlb->is_napi_en = false; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci for (i = 0; i < RXQ_NUM; i++) { 26862306a36Sopenharmony_ci ctlb->napi[i] = &ctlb->hif_ctrl->rxq[i].napi; 26962306a36Sopenharmony_ci netif_napi_add_weight(&ctlb->dummy_dev, ctlb->napi[i], t7xx_dpmaif_napi_rx_poll, 27062306a36Sopenharmony_ci NIC_NAPI_POLL_BUDGET); 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic void t7xx_uninit_netdev_napi(struct t7xx_ccmni_ctrl *ctlb) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci int i; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci for (i = 0; i < RXQ_NUM; i++) { 27962306a36Sopenharmony_ci netif_napi_del(ctlb->napi[i]); 28062306a36Sopenharmony_ci ctlb->napi[i] = NULL; 28162306a36Sopenharmony_ci } 28262306a36Sopenharmony_ci} 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_cistatic int t7xx_ccmni_wwan_newlink(void *ctxt, struct net_device *dev, u32 if_id, 28562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 28662306a36Sopenharmony_ci{ 28762306a36Sopenharmony_ci struct t7xx_ccmni_ctrl *ctlb = ctxt; 28862306a36Sopenharmony_ci struct t7xx_ccmni *ccmni; 28962306a36Sopenharmony_ci int ret; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci if (if_id >= ARRAY_SIZE(ctlb->ccmni_inst)) 29262306a36Sopenharmony_ci return -EINVAL; 29362306a36Sopenharmony_ci 29462306a36Sopenharmony_ci ccmni = wwan_netdev_drvpriv(dev); 29562306a36Sopenharmony_ci ccmni->index = if_id; 29662306a36Sopenharmony_ci ccmni->ctlb = ctlb; 29762306a36Sopenharmony_ci ccmni->dev = dev; 29862306a36Sopenharmony_ci atomic_set(&ccmni->usage, 0); 29962306a36Sopenharmony_ci ctlb->ccmni_inst[if_id] = ccmni; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci ret = register_netdevice(dev); 30262306a36Sopenharmony_ci if (ret) 30362306a36Sopenharmony_ci return ret; 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci netif_device_attach(dev); 30662306a36Sopenharmony_ci return 0; 30762306a36Sopenharmony_ci} 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_cistatic void t7xx_ccmni_wwan_dellink(void *ctxt, struct net_device *dev, struct list_head *head) 31062306a36Sopenharmony_ci{ 31162306a36Sopenharmony_ci struct t7xx_ccmni *ccmni = wwan_netdev_drvpriv(dev); 31262306a36Sopenharmony_ci struct t7xx_ccmni_ctrl *ctlb = ctxt; 31362306a36Sopenharmony_ci u8 if_id = ccmni->index; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci if (if_id >= ARRAY_SIZE(ctlb->ccmni_inst)) 31662306a36Sopenharmony_ci return; 31762306a36Sopenharmony_ci 31862306a36Sopenharmony_ci if (WARN_ON(ctlb->ccmni_inst[if_id] != ccmni)) 31962306a36Sopenharmony_ci return; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci unregister_netdevice(dev); 32262306a36Sopenharmony_ci} 32362306a36Sopenharmony_ci 32462306a36Sopenharmony_cistatic const struct wwan_ops ccmni_wwan_ops = { 32562306a36Sopenharmony_ci .priv_size = sizeof(struct t7xx_ccmni), 32662306a36Sopenharmony_ci .setup = t7xx_ccmni_wwan_setup, 32762306a36Sopenharmony_ci .newlink = t7xx_ccmni_wwan_newlink, 32862306a36Sopenharmony_ci .dellink = t7xx_ccmni_wwan_dellink, 32962306a36Sopenharmony_ci}; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_cistatic int t7xx_ccmni_register_wwan(struct t7xx_ccmni_ctrl *ctlb) 33262306a36Sopenharmony_ci{ 33362306a36Sopenharmony_ci struct device *dev = ctlb->hif_ctrl->dev; 33462306a36Sopenharmony_ci int ret; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (ctlb->wwan_is_registered) 33762306a36Sopenharmony_ci return 0; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci /* WWAN core will create a netdev for the default IP MUX channel */ 34062306a36Sopenharmony_ci ret = wwan_register_ops(dev, &ccmni_wwan_ops, ctlb, IP_MUX_SESSION_DEFAULT); 34162306a36Sopenharmony_ci if (ret < 0) { 34262306a36Sopenharmony_ci dev_err(dev, "Unable to register WWAN ops, %d\n", ret); 34362306a36Sopenharmony_ci return ret; 34462306a36Sopenharmony_ci } 34562306a36Sopenharmony_ci 34662306a36Sopenharmony_ci ctlb->wwan_is_registered = true; 34762306a36Sopenharmony_ci return 0; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic int t7xx_ccmni_md_state_callback(enum md_state state, void *para) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci struct t7xx_ccmni_ctrl *ctlb = para; 35362306a36Sopenharmony_ci struct device *dev; 35462306a36Sopenharmony_ci int ret = 0; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci dev = ctlb->hif_ctrl->dev; 35762306a36Sopenharmony_ci ctlb->md_sta = state; 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_ci switch (state) { 36062306a36Sopenharmony_ci case MD_STATE_READY: 36162306a36Sopenharmony_ci ret = t7xx_ccmni_register_wwan(ctlb); 36262306a36Sopenharmony_ci if (!ret) 36362306a36Sopenharmony_ci t7xx_ccmni_start(ctlb); 36462306a36Sopenharmony_ci break; 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_ci case MD_STATE_EXCEPTION: 36762306a36Sopenharmony_ci case MD_STATE_STOPPED: 36862306a36Sopenharmony_ci t7xx_ccmni_pre_stop(ctlb); 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_ci ret = t7xx_dpmaif_md_state_callback(ctlb->hif_ctrl, state); 37162306a36Sopenharmony_ci if (ret < 0) 37262306a36Sopenharmony_ci dev_err(dev, "DPMAIF md state callback err, state=%d\n", state); 37362306a36Sopenharmony_ci 37462306a36Sopenharmony_ci t7xx_ccmni_post_stop(ctlb); 37562306a36Sopenharmony_ci break; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci case MD_STATE_WAITING_FOR_HS1: 37862306a36Sopenharmony_ci case MD_STATE_WAITING_TO_STOP: 37962306a36Sopenharmony_ci ret = t7xx_dpmaif_md_state_callback(ctlb->hif_ctrl, state); 38062306a36Sopenharmony_ci if (ret < 0) 38162306a36Sopenharmony_ci dev_err(dev, "DPMAIF md state callback err, state=%d\n", state); 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci break; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci default: 38662306a36Sopenharmony_ci break; 38762306a36Sopenharmony_ci } 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return ret; 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_cistatic void init_md_status_notifier(struct t7xx_pci_dev *t7xx_dev) 39362306a36Sopenharmony_ci{ 39462306a36Sopenharmony_ci struct t7xx_ccmni_ctrl *ctlb = t7xx_dev->ccmni_ctlb; 39562306a36Sopenharmony_ci struct t7xx_fsm_notifier *md_status_notifier; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci md_status_notifier = &ctlb->md_status_notify; 39862306a36Sopenharmony_ci INIT_LIST_HEAD(&md_status_notifier->entry); 39962306a36Sopenharmony_ci md_status_notifier->notifier_fn = t7xx_ccmni_md_state_callback; 40062306a36Sopenharmony_ci md_status_notifier->data = ctlb; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci t7xx_fsm_notifier_register(t7xx_dev->md, md_status_notifier); 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic void t7xx_ccmni_recv_skb(struct t7xx_ccmni_ctrl *ccmni_ctlb, struct sk_buff *skb, 40662306a36Sopenharmony_ci struct napi_struct *napi) 40762306a36Sopenharmony_ci{ 40862306a36Sopenharmony_ci struct t7xx_skb_cb *skb_cb; 40962306a36Sopenharmony_ci struct net_device *net_dev; 41062306a36Sopenharmony_ci struct t7xx_ccmni *ccmni; 41162306a36Sopenharmony_ci int pkt_type, skb_len; 41262306a36Sopenharmony_ci u8 netif_id; 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci skb_cb = T7XX_SKB_CB(skb); 41562306a36Sopenharmony_ci netif_id = skb_cb->netif_idx; 41662306a36Sopenharmony_ci ccmni = ccmni_ctlb->ccmni_inst[netif_id]; 41762306a36Sopenharmony_ci if (!ccmni) { 41862306a36Sopenharmony_ci dev_kfree_skb(skb); 41962306a36Sopenharmony_ci return; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci net_dev = ccmni->dev; 42362306a36Sopenharmony_ci pkt_type = skb_cb->rx_pkt_type; 42462306a36Sopenharmony_ci skb->dev = net_dev; 42562306a36Sopenharmony_ci if (pkt_type == PKT_TYPE_IP6) 42662306a36Sopenharmony_ci skb->protocol = htons(ETH_P_IPV6); 42762306a36Sopenharmony_ci else 42862306a36Sopenharmony_ci skb->protocol = htons(ETH_P_IP); 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci skb_len = skb->len; 43162306a36Sopenharmony_ci napi_gro_receive(napi, skb); 43262306a36Sopenharmony_ci net_dev->stats.rx_packets++; 43362306a36Sopenharmony_ci net_dev->stats.rx_bytes += skb_len; 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic void t7xx_ccmni_queue_tx_irq_notify(struct t7xx_ccmni_ctrl *ctlb, int qno) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci struct t7xx_ccmni *ccmni = ctlb->ccmni_inst[0]; 43962306a36Sopenharmony_ci struct netdev_queue *net_queue; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (netif_running(ccmni->dev) && atomic_read(&ccmni->usage) > 0) { 44262306a36Sopenharmony_ci net_queue = netdev_get_tx_queue(ccmni->dev, qno); 44362306a36Sopenharmony_ci if (netif_tx_queue_stopped(net_queue)) 44462306a36Sopenharmony_ci netif_tx_wake_queue(net_queue); 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci} 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_cistatic void t7xx_ccmni_queue_tx_full_notify(struct t7xx_ccmni_ctrl *ctlb, int qno) 44962306a36Sopenharmony_ci{ 45062306a36Sopenharmony_ci struct t7xx_ccmni *ccmni = ctlb->ccmni_inst[0]; 45162306a36Sopenharmony_ci struct netdev_queue *net_queue; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci if (atomic_read(&ccmni->usage) > 0) { 45462306a36Sopenharmony_ci netdev_err(ccmni->dev, "TX queue %d is full\n", qno); 45562306a36Sopenharmony_ci net_queue = netdev_get_tx_queue(ccmni->dev, qno); 45662306a36Sopenharmony_ci netif_tx_stop_queue(net_queue); 45762306a36Sopenharmony_ci } 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic void t7xx_ccmni_queue_state_notify(struct t7xx_pci_dev *t7xx_dev, 46162306a36Sopenharmony_ci enum dpmaif_txq_state state, int qno) 46262306a36Sopenharmony_ci{ 46362306a36Sopenharmony_ci struct t7xx_ccmni_ctrl *ctlb = t7xx_dev->ccmni_ctlb; 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_ci if (ctlb->md_sta != MD_STATE_READY) 46662306a36Sopenharmony_ci return; 46762306a36Sopenharmony_ci 46862306a36Sopenharmony_ci if (!ctlb->ccmni_inst[0]) { 46962306a36Sopenharmony_ci dev_warn(&t7xx_dev->pdev->dev, "No netdev registered yet\n"); 47062306a36Sopenharmony_ci return; 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci if (state == DMPAIF_TXQ_STATE_IRQ) 47462306a36Sopenharmony_ci t7xx_ccmni_queue_tx_irq_notify(ctlb, qno); 47562306a36Sopenharmony_ci else if (state == DMPAIF_TXQ_STATE_FULL) 47662306a36Sopenharmony_ci t7xx_ccmni_queue_tx_full_notify(ctlb, qno); 47762306a36Sopenharmony_ci} 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ciint t7xx_ccmni_init(struct t7xx_pci_dev *t7xx_dev) 48062306a36Sopenharmony_ci{ 48162306a36Sopenharmony_ci struct device *dev = &t7xx_dev->pdev->dev; 48262306a36Sopenharmony_ci struct t7xx_ccmni_ctrl *ctlb; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci ctlb = devm_kzalloc(dev, sizeof(*ctlb), GFP_KERNEL); 48562306a36Sopenharmony_ci if (!ctlb) 48662306a36Sopenharmony_ci return -ENOMEM; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci t7xx_dev->ccmni_ctlb = ctlb; 48962306a36Sopenharmony_ci ctlb->t7xx_dev = t7xx_dev; 49062306a36Sopenharmony_ci ctlb->callbacks.state_notify = t7xx_ccmni_queue_state_notify; 49162306a36Sopenharmony_ci ctlb->callbacks.recv_skb = t7xx_ccmni_recv_skb; 49262306a36Sopenharmony_ci ctlb->nic_dev_num = NIC_DEV_DEFAULT; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci ctlb->hif_ctrl = t7xx_dpmaif_hif_init(t7xx_dev, &ctlb->callbacks); 49562306a36Sopenharmony_ci if (!ctlb->hif_ctrl) 49662306a36Sopenharmony_ci return -ENOMEM; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci t7xx_init_netdev_napi(ctlb); 49962306a36Sopenharmony_ci init_md_status_notifier(t7xx_dev); 50062306a36Sopenharmony_ci return 0; 50162306a36Sopenharmony_ci} 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_civoid t7xx_ccmni_exit(struct t7xx_pci_dev *t7xx_dev) 50462306a36Sopenharmony_ci{ 50562306a36Sopenharmony_ci struct t7xx_ccmni_ctrl *ctlb = t7xx_dev->ccmni_ctlb; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci t7xx_fsm_notifier_unregister(t7xx_dev->md, &ctlb->md_status_notify); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (ctlb->wwan_is_registered) { 51062306a36Sopenharmony_ci wwan_unregister_ops(&t7xx_dev->pdev->dev); 51162306a36Sopenharmony_ci ctlb->wwan_is_registered = false; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci t7xx_uninit_netdev_napi(ctlb); 51562306a36Sopenharmony_ci t7xx_dpmaif_hif_exit(ctlb->hif_ctrl); 51662306a36Sopenharmony_ci} 517