18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* Copyright (c) 2018 Quantenna Communications, Inc. All rights reserved. */ 38c2ecf20Sopenharmony_ci 48c2ecf20Sopenharmony_ci#include <linux/module.h> 58c2ecf20Sopenharmony_ci#include <linux/printk.h> 68c2ecf20Sopenharmony_ci#include <linux/pci.h> 78c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 88c2ecf20Sopenharmony_ci#include <linux/mutex.h> 98c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 108c2ecf20Sopenharmony_ci#include <linux/seq_file.h> 118c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 128c2ecf20Sopenharmony_ci#include <linux/completion.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "pcie_priv.h" 158c2ecf20Sopenharmony_ci#include "bus.h" 168c2ecf20Sopenharmony_ci#include "shm_ipc.h" 178c2ecf20Sopenharmony_ci#include "core.h" 188c2ecf20Sopenharmony_ci#include "debug.h" 198c2ecf20Sopenharmony_ci#include "util.h" 208c2ecf20Sopenharmony_ci#include "qtn_hw_ids.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define QTN_SYSCTL_BAR 0 238c2ecf20Sopenharmony_ci#define QTN_SHMEM_BAR 2 248c2ecf20Sopenharmony_ci#define QTN_DMA_BAR 3 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci#define QTN_PCIE_MAX_FW_BUFSZ (1 * 1024 * 1024) 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_cistatic bool use_msi = true; 298c2ecf20Sopenharmony_cimodule_param(use_msi, bool, 0644); 308c2ecf20Sopenharmony_ciMODULE_PARM_DESC(use_msi, "set 0 to use legacy interrupt"); 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_cistatic unsigned int tx_bd_size_param; 338c2ecf20Sopenharmony_cimodule_param(tx_bd_size_param, uint, 0644); 348c2ecf20Sopenharmony_ciMODULE_PARM_DESC(tx_bd_size_param, "Tx descriptors queue size"); 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistatic unsigned int rx_bd_size_param; 378c2ecf20Sopenharmony_cimodule_param(rx_bd_size_param, uint, 0644); 388c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rx_bd_size_param, "Rx descriptors queue size"); 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_cistatic u8 flashboot = 1; 418c2ecf20Sopenharmony_cimodule_param(flashboot, byte, 0644); 428c2ecf20Sopenharmony_ciMODULE_PARM_DESC(flashboot, "set to 0 to use FW binary file on FS"); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic unsigned int fw_blksize_param = QTN_PCIE_MAX_FW_BUFSZ; 458c2ecf20Sopenharmony_cimodule_param(fw_blksize_param, uint, 0644); 468c2ecf20Sopenharmony_ciMODULE_PARM_DESC(fw_blksize_param, "firmware loading block size in bytes"); 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#define DRV_NAME "qtnfmac_pcie" 498c2ecf20Sopenharmony_ci 508c2ecf20Sopenharmony_ciint qtnf_pcie_control_tx(struct qtnf_bus *bus, struct sk_buff *skb) 518c2ecf20Sopenharmony_ci{ 528c2ecf20Sopenharmony_ci struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus); 538c2ecf20Sopenharmony_ci int ret; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci ret = qtnf_shm_ipc_send(&priv->shm_ipc_ep_in, skb->data, skb->len); 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci if (ret == -ETIMEDOUT) { 588c2ecf20Sopenharmony_ci pr_err("EP firmware is dead\n"); 598c2ecf20Sopenharmony_ci bus->fw_state = QTNF_FW_STATE_DEAD; 608c2ecf20Sopenharmony_ci } 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci return ret; 638c2ecf20Sopenharmony_ci} 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ciint qtnf_pcie_alloc_skb_array(struct qtnf_pcie_bus_priv *priv) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci struct sk_buff **vaddr; 688c2ecf20Sopenharmony_ci int len; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci len = priv->tx_bd_num * sizeof(*priv->tx_skb) + 718c2ecf20Sopenharmony_ci priv->rx_bd_num * sizeof(*priv->rx_skb); 728c2ecf20Sopenharmony_ci vaddr = devm_kzalloc(&priv->pdev->dev, len, GFP_KERNEL); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci if (!vaddr) 758c2ecf20Sopenharmony_ci return -ENOMEM; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci priv->tx_skb = vaddr; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci vaddr += priv->tx_bd_num; 808c2ecf20Sopenharmony_ci priv->rx_skb = vaddr; 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ci return 0; 838c2ecf20Sopenharmony_ci} 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_cistatic void qtnf_pcie_bringup_fw_async(struct qtnf_bus *bus) 868c2ecf20Sopenharmony_ci{ 878c2ecf20Sopenharmony_ci struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus); 888c2ecf20Sopenharmony_ci struct pci_dev *pdev = priv->pdev; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci get_device(&pdev->dev); 918c2ecf20Sopenharmony_ci schedule_work(&bus->fw_work); 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic int qtnf_dbg_mps_show(struct seq_file *s, void *data) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci struct qtnf_bus *bus = dev_get_drvdata(s->private); 978c2ecf20Sopenharmony_ci struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci seq_printf(s, "%d\n", pcie_get_mps(priv->pdev)); 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci return 0; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic int qtnf_dbg_msi_show(struct seq_file *s, void *data) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct qtnf_bus *bus = dev_get_drvdata(s->private); 1078c2ecf20Sopenharmony_ci struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci seq_printf(s, "%u\n", priv->msi_enabled); 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci return 0; 1128c2ecf20Sopenharmony_ci} 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_cistatic int qtnf_dbg_shm_stats(struct seq_file *s, void *data) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct qtnf_bus *bus = dev_get_drvdata(s->private); 1178c2ecf20Sopenharmony_ci struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci seq_printf(s, "shm_ipc_ep_in.tx_packet_count(%zu)\n", 1208c2ecf20Sopenharmony_ci priv->shm_ipc_ep_in.tx_packet_count); 1218c2ecf20Sopenharmony_ci seq_printf(s, "shm_ipc_ep_in.rx_packet_count(%zu)\n", 1228c2ecf20Sopenharmony_ci priv->shm_ipc_ep_in.rx_packet_count); 1238c2ecf20Sopenharmony_ci seq_printf(s, "shm_ipc_ep_out.tx_packet_count(%zu)\n", 1248c2ecf20Sopenharmony_ci priv->shm_ipc_ep_out.tx_timeout_count); 1258c2ecf20Sopenharmony_ci seq_printf(s, "shm_ipc_ep_out.rx_packet_count(%zu)\n", 1268c2ecf20Sopenharmony_ci priv->shm_ipc_ep_out.rx_packet_count); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci return 0; 1298c2ecf20Sopenharmony_ci} 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ciint qtnf_pcie_fw_boot_done(struct qtnf_bus *bus) 1328c2ecf20Sopenharmony_ci{ 1338c2ecf20Sopenharmony_ci struct qtnf_pcie_bus_priv *priv = get_bus_priv(bus); 1348c2ecf20Sopenharmony_ci char card_id[64]; 1358c2ecf20Sopenharmony_ci int ret; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci bus->fw_state = QTNF_FW_STATE_BOOT_DONE; 1388c2ecf20Sopenharmony_ci ret = qtnf_core_attach(bus); 1398c2ecf20Sopenharmony_ci if (ret) { 1408c2ecf20Sopenharmony_ci pr_err("failed to attach core\n"); 1418c2ecf20Sopenharmony_ci } else { 1428c2ecf20Sopenharmony_ci snprintf(card_id, sizeof(card_id), "%s:%s", 1438c2ecf20Sopenharmony_ci DRV_NAME, pci_name(priv->pdev)); 1448c2ecf20Sopenharmony_ci qtnf_debugfs_init(bus, card_id); 1458c2ecf20Sopenharmony_ci qtnf_debugfs_add_entry(bus, "mps", qtnf_dbg_mps_show); 1468c2ecf20Sopenharmony_ci qtnf_debugfs_add_entry(bus, "msi_enabled", qtnf_dbg_msi_show); 1478c2ecf20Sopenharmony_ci qtnf_debugfs_add_entry(bus, "shm_stats", qtnf_dbg_shm_stats); 1488c2ecf20Sopenharmony_ci } 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return ret; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_cistatic void qtnf_tune_pcie_mps(struct pci_dev *pdev) 1548c2ecf20Sopenharmony_ci{ 1558c2ecf20Sopenharmony_ci struct pci_dev *parent; 1568c2ecf20Sopenharmony_ci int mps_p, mps_o, mps_m, mps; 1578c2ecf20Sopenharmony_ci int ret; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* current mps */ 1608c2ecf20Sopenharmony_ci mps_o = pcie_get_mps(pdev); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* maximum supported mps */ 1638c2ecf20Sopenharmony_ci mps_m = 128 << pdev->pcie_mpss; 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci /* suggested new mps value */ 1668c2ecf20Sopenharmony_ci mps = mps_m; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci if (pdev->bus && pdev->bus->self) { 1698c2ecf20Sopenharmony_ci /* parent (bus) mps */ 1708c2ecf20Sopenharmony_ci parent = pdev->bus->self; 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (pci_is_pcie(parent)) { 1738c2ecf20Sopenharmony_ci mps_p = pcie_get_mps(parent); 1748c2ecf20Sopenharmony_ci mps = min(mps_m, mps_p); 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci ret = pcie_set_mps(pdev, mps); 1798c2ecf20Sopenharmony_ci if (ret) { 1808c2ecf20Sopenharmony_ci pr_err("failed to set mps to %d, keep using current %d\n", 1818c2ecf20Sopenharmony_ci mps, mps_o); 1828c2ecf20Sopenharmony_ci return; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci pr_debug("set mps to %d (was %d, max %d)\n", mps, mps_o, mps_m); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cistatic void qtnf_pcie_init_irq(struct qtnf_pcie_bus_priv *priv, bool use_msi) 1898c2ecf20Sopenharmony_ci{ 1908c2ecf20Sopenharmony_ci struct pci_dev *pdev = priv->pdev; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci /* fall back to legacy INTx interrupts by default */ 1938c2ecf20Sopenharmony_ci priv->msi_enabled = 0; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* check if MSI capability is available */ 1968c2ecf20Sopenharmony_ci if (use_msi) { 1978c2ecf20Sopenharmony_ci if (!pci_enable_msi(pdev)) { 1988c2ecf20Sopenharmony_ci pr_debug("enabled MSI interrupt\n"); 1998c2ecf20Sopenharmony_ci priv->msi_enabled = 1; 2008c2ecf20Sopenharmony_ci } else { 2018c2ecf20Sopenharmony_ci pr_warn("failed to enable MSI interrupts"); 2028c2ecf20Sopenharmony_ci } 2038c2ecf20Sopenharmony_ci } 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci if (!priv->msi_enabled) { 2068c2ecf20Sopenharmony_ci pr_warn("legacy PCIE interrupts enabled\n"); 2078c2ecf20Sopenharmony_ci pci_intx(pdev, 1); 2088c2ecf20Sopenharmony_ci } 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic void __iomem *qtnf_map_bar(struct pci_dev *pdev, u8 index) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci void __iomem *vaddr; 2148c2ecf20Sopenharmony_ci dma_addr_t busaddr; 2158c2ecf20Sopenharmony_ci size_t len; 2168c2ecf20Sopenharmony_ci int ret; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_ci ret = pcim_iomap_regions(pdev, 1 << index, "qtnfmac_pcie"); 2198c2ecf20Sopenharmony_ci if (ret) 2208c2ecf20Sopenharmony_ci return IOMEM_ERR_PTR(ret); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci busaddr = pci_resource_start(pdev, index); 2238c2ecf20Sopenharmony_ci len = pci_resource_len(pdev, index); 2248c2ecf20Sopenharmony_ci vaddr = pcim_iomap_table(pdev)[index]; 2258c2ecf20Sopenharmony_ci if (!vaddr) 2268c2ecf20Sopenharmony_ci return IOMEM_ERR_PTR(-ENOMEM); 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ci pr_debug("BAR%u vaddr=0x%p busaddr=%pad len=%u\n", 2298c2ecf20Sopenharmony_ci index, vaddr, &busaddr, (int)len); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci return vaddr; 2328c2ecf20Sopenharmony_ci} 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_cistatic void qtnf_pcie_control_rx_callback(void *arg, const u8 __iomem *buf, 2358c2ecf20Sopenharmony_ci size_t len) 2368c2ecf20Sopenharmony_ci{ 2378c2ecf20Sopenharmony_ci struct qtnf_pcie_bus_priv *priv = arg; 2388c2ecf20Sopenharmony_ci struct qtnf_bus *bus = pci_get_drvdata(priv->pdev); 2398c2ecf20Sopenharmony_ci struct sk_buff *skb; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci if (unlikely(len == 0)) { 2428c2ecf20Sopenharmony_ci pr_warn("zero length packet received\n"); 2438c2ecf20Sopenharmony_ci return; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci skb = __dev_alloc_skb(len, GFP_KERNEL); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (unlikely(!skb)) { 2498c2ecf20Sopenharmony_ci pr_err("failed to allocate skb\n"); 2508c2ecf20Sopenharmony_ci return; 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci memcpy_fromio(skb_put(skb, len), buf, len); 2548c2ecf20Sopenharmony_ci 2558c2ecf20Sopenharmony_ci qtnf_trans_handle_rx_ctl_packet(bus, skb); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_civoid qtnf_pcie_init_shm_ipc(struct qtnf_pcie_bus_priv *priv, 2598c2ecf20Sopenharmony_ci struct qtnf_shm_ipc_region __iomem *ipc_tx_reg, 2608c2ecf20Sopenharmony_ci struct qtnf_shm_ipc_region __iomem *ipc_rx_reg, 2618c2ecf20Sopenharmony_ci const struct qtnf_shm_ipc_int *ipc_int) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci const struct qtnf_shm_ipc_rx_callback rx_callback = { 2648c2ecf20Sopenharmony_ci qtnf_pcie_control_rx_callback, priv }; 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci qtnf_shm_ipc_init(&priv->shm_ipc_ep_in, QTNF_SHM_IPC_OUTBOUND, 2678c2ecf20Sopenharmony_ci ipc_tx_reg, priv->workqueue, 2688c2ecf20Sopenharmony_ci ipc_int, &rx_callback); 2698c2ecf20Sopenharmony_ci qtnf_shm_ipc_init(&priv->shm_ipc_ep_out, QTNF_SHM_IPC_INBOUND, 2708c2ecf20Sopenharmony_ci ipc_rx_reg, priv->workqueue, 2718c2ecf20Sopenharmony_ci ipc_int, &rx_callback); 2728c2ecf20Sopenharmony_ci} 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_cistatic int qtnf_pcie_probe(struct pci_dev *pdev, const struct pci_device_id *id) 2758c2ecf20Sopenharmony_ci{ 2768c2ecf20Sopenharmony_ci struct qtnf_pcie_bus_priv *pcie_priv; 2778c2ecf20Sopenharmony_ci struct qtnf_bus *bus; 2788c2ecf20Sopenharmony_ci void __iomem *sysctl_bar; 2798c2ecf20Sopenharmony_ci void __iomem *epmem_bar; 2808c2ecf20Sopenharmony_ci void __iomem *dmareg_bar; 2818c2ecf20Sopenharmony_ci unsigned int chipid; 2828c2ecf20Sopenharmony_ci int ret; 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci if (!pci_is_pcie(pdev)) { 2858c2ecf20Sopenharmony_ci pr_err("device %s is not PCI Express\n", pci_name(pdev)); 2868c2ecf20Sopenharmony_ci return -EIO; 2878c2ecf20Sopenharmony_ci } 2888c2ecf20Sopenharmony_ci 2898c2ecf20Sopenharmony_ci qtnf_tune_pcie_mps(pdev); 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci ret = pcim_enable_device(pdev); 2928c2ecf20Sopenharmony_ci if (ret) { 2938c2ecf20Sopenharmony_ci pr_err("failed to init PCI device %x\n", pdev->device); 2948c2ecf20Sopenharmony_ci return ret; 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci pci_set_master(pdev); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci sysctl_bar = qtnf_map_bar(pdev, QTN_SYSCTL_BAR); 3008c2ecf20Sopenharmony_ci if (IS_ERR(sysctl_bar)) { 3018c2ecf20Sopenharmony_ci pr_err("failed to map BAR%u\n", QTN_SYSCTL_BAR); 3028c2ecf20Sopenharmony_ci return PTR_ERR(sysctl_bar); 3038c2ecf20Sopenharmony_ci } 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci dmareg_bar = qtnf_map_bar(pdev, QTN_DMA_BAR); 3068c2ecf20Sopenharmony_ci if (IS_ERR(dmareg_bar)) { 3078c2ecf20Sopenharmony_ci pr_err("failed to map BAR%u\n", QTN_DMA_BAR); 3088c2ecf20Sopenharmony_ci return PTR_ERR(dmareg_bar); 3098c2ecf20Sopenharmony_ci } 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci epmem_bar = qtnf_map_bar(pdev, QTN_SHMEM_BAR); 3128c2ecf20Sopenharmony_ci if (IS_ERR(epmem_bar)) { 3138c2ecf20Sopenharmony_ci pr_err("failed to map BAR%u\n", QTN_SHMEM_BAR); 3148c2ecf20Sopenharmony_ci return PTR_ERR(epmem_bar); 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci chipid = qtnf_chip_id_get(sysctl_bar); 3188c2ecf20Sopenharmony_ci 3198c2ecf20Sopenharmony_ci pr_info("identified device: %s\n", qtnf_chipid_to_string(chipid)); 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci switch (chipid) { 3228c2ecf20Sopenharmony_ci case QTN_CHIP_ID_PEARL: 3238c2ecf20Sopenharmony_ci case QTN_CHIP_ID_PEARL_B: 3248c2ecf20Sopenharmony_ci case QTN_CHIP_ID_PEARL_C: 3258c2ecf20Sopenharmony_ci bus = qtnf_pcie_pearl_alloc(pdev); 3268c2ecf20Sopenharmony_ci break; 3278c2ecf20Sopenharmony_ci case QTN_CHIP_ID_TOPAZ: 3288c2ecf20Sopenharmony_ci bus = qtnf_pcie_topaz_alloc(pdev); 3298c2ecf20Sopenharmony_ci break; 3308c2ecf20Sopenharmony_ci default: 3318c2ecf20Sopenharmony_ci pr_err("unsupported chip ID 0x%x\n", chipid); 3328c2ecf20Sopenharmony_ci return -ENOTSUPP; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci if (!bus) 3368c2ecf20Sopenharmony_ci return -ENOMEM; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci pcie_priv = get_bus_priv(bus); 3398c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, bus); 3408c2ecf20Sopenharmony_ci bus->dev = &pdev->dev; 3418c2ecf20Sopenharmony_ci bus->fw_state = QTNF_FW_STATE_DETACHED; 3428c2ecf20Sopenharmony_ci pcie_priv->pdev = pdev; 3438c2ecf20Sopenharmony_ci pcie_priv->tx_stopped = 0; 3448c2ecf20Sopenharmony_ci pcie_priv->flashboot = flashboot; 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci if (fw_blksize_param > QTN_PCIE_MAX_FW_BUFSZ) 3478c2ecf20Sopenharmony_ci pcie_priv->fw_blksize = QTN_PCIE_MAX_FW_BUFSZ; 3488c2ecf20Sopenharmony_ci else 3498c2ecf20Sopenharmony_ci pcie_priv->fw_blksize = fw_blksize_param; 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ci mutex_init(&bus->bus_lock); 3528c2ecf20Sopenharmony_ci spin_lock_init(&pcie_priv->tx_lock); 3538c2ecf20Sopenharmony_ci spin_lock_init(&pcie_priv->tx_reclaim_lock); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci pcie_priv->tx_full_count = 0; 3568c2ecf20Sopenharmony_ci pcie_priv->tx_done_count = 0; 3578c2ecf20Sopenharmony_ci pcie_priv->pcie_irq_count = 0; 3588c2ecf20Sopenharmony_ci pcie_priv->tx_reclaim_done = 0; 3598c2ecf20Sopenharmony_ci pcie_priv->tx_reclaim_req = 0; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci pcie_priv->workqueue = create_singlethread_workqueue("QTNF_PCIE"); 3628c2ecf20Sopenharmony_ci if (!pcie_priv->workqueue) { 3638c2ecf20Sopenharmony_ci pr_err("failed to alloc bus workqueue\n"); 3648c2ecf20Sopenharmony_ci return -ENODEV; 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci ret = dma_set_mask_and_coherent(&pdev->dev, 3688c2ecf20Sopenharmony_ci pcie_priv->dma_mask_get_cb()); 3698c2ecf20Sopenharmony_ci if (ret) { 3708c2ecf20Sopenharmony_ci pr_err("PCIE DMA coherent mask init failed 0x%llx\n", 3718c2ecf20Sopenharmony_ci pcie_priv->dma_mask_get_cb()); 3728c2ecf20Sopenharmony_ci goto error; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci init_dummy_netdev(&bus->mux_dev); 3768c2ecf20Sopenharmony_ci qtnf_pcie_init_irq(pcie_priv, use_msi); 3778c2ecf20Sopenharmony_ci pcie_priv->sysctl_bar = sysctl_bar; 3788c2ecf20Sopenharmony_ci pcie_priv->dmareg_bar = dmareg_bar; 3798c2ecf20Sopenharmony_ci pcie_priv->epmem_bar = epmem_bar; 3808c2ecf20Sopenharmony_ci pci_save_state(pdev); 3818c2ecf20Sopenharmony_ci 3828c2ecf20Sopenharmony_ci ret = pcie_priv->probe_cb(bus, tx_bd_size_param, rx_bd_size_param); 3838c2ecf20Sopenharmony_ci if (ret) 3848c2ecf20Sopenharmony_ci goto error; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci qtnf_pcie_bringup_fw_async(bus); 3878c2ecf20Sopenharmony_ci return 0; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_cierror: 3908c2ecf20Sopenharmony_ci flush_workqueue(pcie_priv->workqueue); 3918c2ecf20Sopenharmony_ci destroy_workqueue(pcie_priv->workqueue); 3928c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, NULL); 3938c2ecf20Sopenharmony_ci return ret; 3948c2ecf20Sopenharmony_ci} 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_cistatic void qtnf_pcie_free_shm_ipc(struct qtnf_pcie_bus_priv *priv) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci qtnf_shm_ipc_free(&priv->shm_ipc_ep_in); 3998c2ecf20Sopenharmony_ci qtnf_shm_ipc_free(&priv->shm_ipc_ep_out); 4008c2ecf20Sopenharmony_ci} 4018c2ecf20Sopenharmony_ci 4028c2ecf20Sopenharmony_cistatic void qtnf_pcie_remove(struct pci_dev *dev) 4038c2ecf20Sopenharmony_ci{ 4048c2ecf20Sopenharmony_ci struct qtnf_pcie_bus_priv *priv; 4058c2ecf20Sopenharmony_ci struct qtnf_bus *bus; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci bus = pci_get_drvdata(dev); 4088c2ecf20Sopenharmony_ci if (!bus) 4098c2ecf20Sopenharmony_ci return; 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci priv = get_bus_priv(bus); 4128c2ecf20Sopenharmony_ci 4138c2ecf20Sopenharmony_ci cancel_work_sync(&bus->fw_work); 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci if (qtnf_fw_is_attached(bus)) 4168c2ecf20Sopenharmony_ci qtnf_core_detach(bus); 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci netif_napi_del(&bus->mux_napi); 4198c2ecf20Sopenharmony_ci flush_workqueue(priv->workqueue); 4208c2ecf20Sopenharmony_ci destroy_workqueue(priv->workqueue); 4218c2ecf20Sopenharmony_ci tasklet_kill(&priv->reclaim_tq); 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci qtnf_pcie_free_shm_ipc(priv); 4248c2ecf20Sopenharmony_ci qtnf_debugfs_remove(bus); 4258c2ecf20Sopenharmony_ci priv->remove_cb(bus); 4268c2ecf20Sopenharmony_ci pci_set_drvdata(priv->pdev, NULL); 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4308c2ecf20Sopenharmony_cistatic int qtnf_pcie_suspend(struct device *dev) 4318c2ecf20Sopenharmony_ci{ 4328c2ecf20Sopenharmony_ci struct qtnf_pcie_bus_priv *priv; 4338c2ecf20Sopenharmony_ci struct qtnf_bus *bus; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci bus = dev_get_drvdata(dev); 4368c2ecf20Sopenharmony_ci if (!bus) 4378c2ecf20Sopenharmony_ci return -EFAULT; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci priv = get_bus_priv(bus); 4408c2ecf20Sopenharmony_ci return priv->suspend_cb(bus); 4418c2ecf20Sopenharmony_ci} 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_cistatic int qtnf_pcie_resume(struct device *dev) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci struct qtnf_pcie_bus_priv *priv; 4468c2ecf20Sopenharmony_ci struct qtnf_bus *bus; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci bus = dev_get_drvdata(dev); 4498c2ecf20Sopenharmony_ci if (!bus) 4508c2ecf20Sopenharmony_ci return -EFAULT; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci priv = get_bus_priv(bus); 4538c2ecf20Sopenharmony_ci return priv->resume_cb(bus); 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci/* Power Management Hooks */ 4578c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(qtnf_pcie_pm_ops, qtnf_pcie_suspend, 4588c2ecf20Sopenharmony_ci qtnf_pcie_resume); 4598c2ecf20Sopenharmony_ci#endif 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_cistatic const struct pci_device_id qtnf_pcie_devid_table[] = { 4628c2ecf20Sopenharmony_ci { 4638c2ecf20Sopenharmony_ci PCIE_VENDOR_ID_QUANTENNA, PCIE_DEVICE_ID_QSR, 4648c2ecf20Sopenharmony_ci PCI_ANY_ID, PCI_ANY_ID, 0, 0, 4658c2ecf20Sopenharmony_ci }, 4668c2ecf20Sopenharmony_ci { }, 4678c2ecf20Sopenharmony_ci}; 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, qtnf_pcie_devid_table); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_cistatic struct pci_driver qtnf_pcie_drv_data = { 4728c2ecf20Sopenharmony_ci .name = DRV_NAME, 4738c2ecf20Sopenharmony_ci .id_table = qtnf_pcie_devid_table, 4748c2ecf20Sopenharmony_ci .probe = qtnf_pcie_probe, 4758c2ecf20Sopenharmony_ci .remove = qtnf_pcie_remove, 4768c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 4778c2ecf20Sopenharmony_ci .driver = { 4788c2ecf20Sopenharmony_ci .pm = &qtnf_pcie_pm_ops, 4798c2ecf20Sopenharmony_ci }, 4808c2ecf20Sopenharmony_ci#endif 4818c2ecf20Sopenharmony_ci}; 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_cistatic int __init qtnf_pcie_register(void) 4848c2ecf20Sopenharmony_ci{ 4858c2ecf20Sopenharmony_ci return pci_register_driver(&qtnf_pcie_drv_data); 4868c2ecf20Sopenharmony_ci} 4878c2ecf20Sopenharmony_ci 4888c2ecf20Sopenharmony_cistatic void __exit qtnf_pcie_exit(void) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci pci_unregister_driver(&qtnf_pcie_drv_data); 4918c2ecf20Sopenharmony_ci} 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_cimodule_init(qtnf_pcie_register); 4948c2ecf20Sopenharmony_cimodule_exit(qtnf_pcie_exit); 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ciMODULE_AUTHOR("Quantenna Communications"); 4978c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Quantenna PCIe bus driver for 802.11 wireless LAN."); 4988c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 499