18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license. When using or 38c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Copyright (C) 2016 Advanced Micro Devices, Inc. All Rights Reserved. 88c2ecf20Sopenharmony_ci * Copyright (C) 2016 T-Platforms. All Rights Reserved. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 118c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as 128c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * BSD LICENSE 158c2ecf20Sopenharmony_ci * 168c2ecf20Sopenharmony_ci * Copyright (C) 2016 Advanced Micro Devices, Inc. All Rights Reserved. 178c2ecf20Sopenharmony_ci * Copyright (C) 2016 T-Platforms. All Rights Reserved. 188c2ecf20Sopenharmony_ci * 198c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 208c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 218c2ecf20Sopenharmony_ci * are met: 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * * Redistributions of source code must retain the above copyright 248c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 258c2ecf20Sopenharmony_ci * * Redistributions in binary form must reproduce the above copy 268c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 278c2ecf20Sopenharmony_ci * the documentation and/or other materials provided with the 288c2ecf20Sopenharmony_ci * distribution. 298c2ecf20Sopenharmony_ci * * Neither the name of AMD Corporation nor the names of its 308c2ecf20Sopenharmony_ci * contributors may be used to endorse or promote products derived 318c2ecf20Sopenharmony_ci * from this software without specific prior written permission. 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 348c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 358c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 368c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 378c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 388c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 398c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 408c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 418c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 428c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 438c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 448c2ecf20Sopenharmony_ci * 458c2ecf20Sopenharmony_ci * AMD PCIe NTB Linux driver 468c2ecf20Sopenharmony_ci * 478c2ecf20Sopenharmony_ci * Contact Information: 488c2ecf20Sopenharmony_ci * Xiangliang Yu <Xiangliang.Yu@amd.com> 498c2ecf20Sopenharmony_ci */ 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_ci#include <linux/debugfs.h> 528c2ecf20Sopenharmony_ci#include <linux/delay.h> 538c2ecf20Sopenharmony_ci#include <linux/init.h> 548c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 558c2ecf20Sopenharmony_ci#include <linux/module.h> 568c2ecf20Sopenharmony_ci#include <linux/acpi.h> 578c2ecf20Sopenharmony_ci#include <linux/pci.h> 588c2ecf20Sopenharmony_ci#include <linux/random.h> 598c2ecf20Sopenharmony_ci#include <linux/slab.h> 608c2ecf20Sopenharmony_ci#include <linux/ntb.h> 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci#include "ntb_hw_amd.h" 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci#define NTB_NAME "ntb_hw_amd" 658c2ecf20Sopenharmony_ci#define NTB_DESC "AMD(R) PCI-E Non-Transparent Bridge Driver" 668c2ecf20Sopenharmony_ci#define NTB_VER "1.0" 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(NTB_DESC); 698c2ecf20Sopenharmony_ciMODULE_VERSION(NTB_VER); 708c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 718c2ecf20Sopenharmony_ciMODULE_AUTHOR("AMD Inc."); 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic const struct file_operations amd_ntb_debugfs_info; 748c2ecf20Sopenharmony_cistatic struct dentry *debugfs_dir; 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_cistatic int ndev_mw_to_bar(struct amd_ntb_dev *ndev, int idx) 778c2ecf20Sopenharmony_ci{ 788c2ecf20Sopenharmony_ci if (idx < 0 || idx > ndev->mw_count) 798c2ecf20Sopenharmony_ci return -EINVAL; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci return ndev->dev_data->mw_idx << idx; 828c2ecf20Sopenharmony_ci} 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistatic int amd_ntb_mw_count(struct ntb_dev *ntb, int pidx) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci if (pidx != NTB_DEF_PEER_IDX) 878c2ecf20Sopenharmony_ci return -EINVAL; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci return ntb_ndev(ntb)->mw_count; 908c2ecf20Sopenharmony_ci} 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_cistatic int amd_ntb_mw_get_align(struct ntb_dev *ntb, int pidx, int idx, 938c2ecf20Sopenharmony_ci resource_size_t *addr_align, 948c2ecf20Sopenharmony_ci resource_size_t *size_align, 958c2ecf20Sopenharmony_ci resource_size_t *size_max) 968c2ecf20Sopenharmony_ci{ 978c2ecf20Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 988c2ecf20Sopenharmony_ci int bar; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (pidx != NTB_DEF_PEER_IDX) 1018c2ecf20Sopenharmony_ci return -EINVAL; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci bar = ndev_mw_to_bar(ndev, idx); 1048c2ecf20Sopenharmony_ci if (bar < 0) 1058c2ecf20Sopenharmony_ci return bar; 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci if (addr_align) 1088c2ecf20Sopenharmony_ci *addr_align = SZ_4K; 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_ci if (size_align) 1118c2ecf20Sopenharmony_ci *size_align = 1; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci if (size_max) 1148c2ecf20Sopenharmony_ci *size_max = pci_resource_len(ndev->ntb.pdev, bar); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci return 0; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int amd_ntb_mw_set_trans(struct ntb_dev *ntb, int pidx, int idx, 1208c2ecf20Sopenharmony_ci dma_addr_t addr, resource_size_t size) 1218c2ecf20Sopenharmony_ci{ 1228c2ecf20Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 1238c2ecf20Sopenharmony_ci unsigned long xlat_reg, limit_reg = 0; 1248c2ecf20Sopenharmony_ci resource_size_t mw_size; 1258c2ecf20Sopenharmony_ci void __iomem *mmio, *peer_mmio; 1268c2ecf20Sopenharmony_ci u64 base_addr, limit, reg_val; 1278c2ecf20Sopenharmony_ci int bar; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci if (pidx != NTB_DEF_PEER_IDX) 1308c2ecf20Sopenharmony_ci return -EINVAL; 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci bar = ndev_mw_to_bar(ndev, idx); 1338c2ecf20Sopenharmony_ci if (bar < 0) 1348c2ecf20Sopenharmony_ci return bar; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci mw_size = pci_resource_len(ntb->pdev, bar); 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_ci /* make sure the range fits in the usable mw size */ 1398c2ecf20Sopenharmony_ci if (size > mw_size) 1408c2ecf20Sopenharmony_ci return -EINVAL; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci mmio = ndev->self_mmio; 1438c2ecf20Sopenharmony_ci peer_mmio = ndev->peer_mmio; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci base_addr = pci_resource_start(ntb->pdev, bar); 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci if (bar != 1) { 1488c2ecf20Sopenharmony_ci xlat_reg = AMD_BAR23XLAT_OFFSET + ((bar - 2) << 2); 1498c2ecf20Sopenharmony_ci limit_reg = AMD_BAR23LMT_OFFSET + ((bar - 2) << 2); 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci /* Set the limit if supported */ 1528c2ecf20Sopenharmony_ci limit = size; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* set and verify setting the translation address */ 1558c2ecf20Sopenharmony_ci write64(addr, peer_mmio + xlat_reg); 1568c2ecf20Sopenharmony_ci reg_val = read64(peer_mmio + xlat_reg); 1578c2ecf20Sopenharmony_ci if (reg_val != addr) { 1588c2ecf20Sopenharmony_ci write64(0, peer_mmio + xlat_reg); 1598c2ecf20Sopenharmony_ci return -EIO; 1608c2ecf20Sopenharmony_ci } 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci /* set and verify setting the limit */ 1638c2ecf20Sopenharmony_ci write64(limit, peer_mmio + limit_reg); 1648c2ecf20Sopenharmony_ci reg_val = read64(peer_mmio + limit_reg); 1658c2ecf20Sopenharmony_ci if (reg_val != limit) { 1668c2ecf20Sopenharmony_ci write64(base_addr, mmio + limit_reg); 1678c2ecf20Sopenharmony_ci write64(0, peer_mmio + xlat_reg); 1688c2ecf20Sopenharmony_ci return -EIO; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci } else { 1718c2ecf20Sopenharmony_ci xlat_reg = AMD_BAR1XLAT_OFFSET; 1728c2ecf20Sopenharmony_ci limit_reg = AMD_BAR1LMT_OFFSET; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci /* Set the limit if supported */ 1758c2ecf20Sopenharmony_ci limit = size; 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci /* set and verify setting the translation address */ 1788c2ecf20Sopenharmony_ci write64(addr, peer_mmio + xlat_reg); 1798c2ecf20Sopenharmony_ci reg_val = read64(peer_mmio + xlat_reg); 1808c2ecf20Sopenharmony_ci if (reg_val != addr) { 1818c2ecf20Sopenharmony_ci write64(0, peer_mmio + xlat_reg); 1828c2ecf20Sopenharmony_ci return -EIO; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_ci /* set and verify setting the limit */ 1868c2ecf20Sopenharmony_ci writel(limit, peer_mmio + limit_reg); 1878c2ecf20Sopenharmony_ci reg_val = readl(peer_mmio + limit_reg); 1888c2ecf20Sopenharmony_ci if (reg_val != limit) { 1898c2ecf20Sopenharmony_ci writel(base_addr, mmio + limit_reg); 1908c2ecf20Sopenharmony_ci writel(0, peer_mmio + xlat_reg); 1918c2ecf20Sopenharmony_ci return -EIO; 1928c2ecf20Sopenharmony_ci } 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return 0; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic int amd_ntb_get_link_status(struct amd_ntb_dev *ndev) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct pci_dev *pdev = NULL; 2018c2ecf20Sopenharmony_ci struct pci_dev *pci_swds = NULL; 2028c2ecf20Sopenharmony_ci struct pci_dev *pci_swus = NULL; 2038c2ecf20Sopenharmony_ci u32 stat; 2048c2ecf20Sopenharmony_ci int rc; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (ndev->ntb.topo == NTB_TOPO_SEC) { 2078c2ecf20Sopenharmony_ci /* Locate the pointer to Downstream Switch for this device */ 2088c2ecf20Sopenharmony_ci pci_swds = pci_upstream_bridge(ndev->ntb.pdev); 2098c2ecf20Sopenharmony_ci if (pci_swds) { 2108c2ecf20Sopenharmony_ci /* 2118c2ecf20Sopenharmony_ci * Locate the pointer to Upstream Switch for 2128c2ecf20Sopenharmony_ci * the Downstream Switch. 2138c2ecf20Sopenharmony_ci */ 2148c2ecf20Sopenharmony_ci pci_swus = pci_upstream_bridge(pci_swds); 2158c2ecf20Sopenharmony_ci if (pci_swus) { 2168c2ecf20Sopenharmony_ci rc = pcie_capability_read_dword(pci_swus, 2178c2ecf20Sopenharmony_ci PCI_EXP_LNKCTL, 2188c2ecf20Sopenharmony_ci &stat); 2198c2ecf20Sopenharmony_ci if (rc) 2208c2ecf20Sopenharmony_ci return 0; 2218c2ecf20Sopenharmony_ci } else { 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci } else { 2258c2ecf20Sopenharmony_ci return 0; 2268c2ecf20Sopenharmony_ci } 2278c2ecf20Sopenharmony_ci } else if (ndev->ntb.topo == NTB_TOPO_PRI) { 2288c2ecf20Sopenharmony_ci /* 2298c2ecf20Sopenharmony_ci * For NTB primary, we simply read the Link Status and control 2308c2ecf20Sopenharmony_ci * register of the NTB device itself. 2318c2ecf20Sopenharmony_ci */ 2328c2ecf20Sopenharmony_ci pdev = ndev->ntb.pdev; 2338c2ecf20Sopenharmony_ci rc = pcie_capability_read_dword(pdev, PCI_EXP_LNKCTL, &stat); 2348c2ecf20Sopenharmony_ci if (rc) 2358c2ecf20Sopenharmony_ci return 0; 2368c2ecf20Sopenharmony_ci } else { 2378c2ecf20Sopenharmony_ci /* Catch all for everything else */ 2388c2ecf20Sopenharmony_ci return 0; 2398c2ecf20Sopenharmony_ci } 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci ndev->lnk_sta = stat; 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_ci return 1; 2448c2ecf20Sopenharmony_ci} 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_cistatic int amd_link_is_up(struct amd_ntb_dev *ndev) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci int ret; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci /* 2518c2ecf20Sopenharmony_ci * We consider the link to be up under two conditions: 2528c2ecf20Sopenharmony_ci * 2538c2ecf20Sopenharmony_ci * - When a link-up event is received. This is indicated by 2548c2ecf20Sopenharmony_ci * AMD_LINK_UP_EVENT set in peer_sta. 2558c2ecf20Sopenharmony_ci * - When driver on both sides of the link have been loaded. 2568c2ecf20Sopenharmony_ci * This is indicated by bit 1 being set in the peer 2578c2ecf20Sopenharmony_ci * SIDEINFO register. 2588c2ecf20Sopenharmony_ci * 2598c2ecf20Sopenharmony_ci * This function should return 1 when the latter of the above 2608c2ecf20Sopenharmony_ci * two conditions is true. 2618c2ecf20Sopenharmony_ci * 2628c2ecf20Sopenharmony_ci * Now consider the sequence of events - Link-Up event occurs, 2638c2ecf20Sopenharmony_ci * then the peer side driver loads. In this case, we would have 2648c2ecf20Sopenharmony_ci * received LINK_UP event and bit 1 of peer SIDEINFO is also 2658c2ecf20Sopenharmony_ci * set. What happens now if the link goes down? Bit 1 of 2668c2ecf20Sopenharmony_ci * peer SIDEINFO remains set, but LINK_DOWN bit is set in 2678c2ecf20Sopenharmony_ci * peer_sta. So we should return 0 from this function. Not only 2688c2ecf20Sopenharmony_ci * that, we clear bit 1 of peer SIDEINFO to 0, since the peer 2698c2ecf20Sopenharmony_ci * side driver did not even get a chance to clear it before 2708c2ecf20Sopenharmony_ci * the link went down. This can be the case of surprise link 2718c2ecf20Sopenharmony_ci * removal. 2728c2ecf20Sopenharmony_ci * 2738c2ecf20Sopenharmony_ci * LINK_UP event will always occur before the peer side driver 2748c2ecf20Sopenharmony_ci * gets loaded the very first time. So there can be a case when 2758c2ecf20Sopenharmony_ci * the LINK_UP event has occurred, but the peer side driver hasn't 2768c2ecf20Sopenharmony_ci * yet loaded. We return 0 in that case. 2778c2ecf20Sopenharmony_ci * 2788c2ecf20Sopenharmony_ci * There is also a special case when the primary side driver is 2798c2ecf20Sopenharmony_ci * unloaded and then loaded again. Since there is no change in 2808c2ecf20Sopenharmony_ci * the status of NTB secondary in this case, there is no Link-Up 2818c2ecf20Sopenharmony_ci * or Link-Down notification received. We recognize this condition 2828c2ecf20Sopenharmony_ci * with peer_sta being set to 0. 2838c2ecf20Sopenharmony_ci * 2848c2ecf20Sopenharmony_ci * If bit 1 of peer SIDEINFO register is not set, then we 2858c2ecf20Sopenharmony_ci * simply return 0 irrespective of the link up or down status 2868c2ecf20Sopenharmony_ci * set in peer_sta. 2878c2ecf20Sopenharmony_ci */ 2888c2ecf20Sopenharmony_ci ret = amd_poll_link(ndev); 2898c2ecf20Sopenharmony_ci if (ret) { 2908c2ecf20Sopenharmony_ci /* 2918c2ecf20Sopenharmony_ci * We need to check the below only for NTB primary. For NTB 2928c2ecf20Sopenharmony_ci * secondary, simply checking the result of PSIDE_INFO 2938c2ecf20Sopenharmony_ci * register will suffice. 2948c2ecf20Sopenharmony_ci */ 2958c2ecf20Sopenharmony_ci if (ndev->ntb.topo == NTB_TOPO_PRI) { 2968c2ecf20Sopenharmony_ci if ((ndev->peer_sta & AMD_LINK_UP_EVENT) || 2978c2ecf20Sopenharmony_ci (ndev->peer_sta == 0)) 2988c2ecf20Sopenharmony_ci return ret; 2998c2ecf20Sopenharmony_ci else if (ndev->peer_sta & AMD_LINK_DOWN_EVENT) { 3008c2ecf20Sopenharmony_ci /* Clear peer sideinfo register */ 3018c2ecf20Sopenharmony_ci amd_clear_side_info_reg(ndev, true); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ci return 0; 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci } else { /* NTB_TOPO_SEC */ 3068c2ecf20Sopenharmony_ci return ret; 3078c2ecf20Sopenharmony_ci } 3088c2ecf20Sopenharmony_ci } 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci return 0; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic u64 amd_ntb_link_is_up(struct ntb_dev *ntb, 3148c2ecf20Sopenharmony_ci enum ntb_speed *speed, 3158c2ecf20Sopenharmony_ci enum ntb_width *width) 3168c2ecf20Sopenharmony_ci{ 3178c2ecf20Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 3188c2ecf20Sopenharmony_ci int ret = 0; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci if (amd_link_is_up(ndev)) { 3218c2ecf20Sopenharmony_ci if (speed) 3228c2ecf20Sopenharmony_ci *speed = NTB_LNK_STA_SPEED(ndev->lnk_sta); 3238c2ecf20Sopenharmony_ci if (width) 3248c2ecf20Sopenharmony_ci *width = NTB_LNK_STA_WIDTH(ndev->lnk_sta); 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci dev_dbg(&ntb->pdev->dev, "link is up.\n"); 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_ci ret = 1; 3298c2ecf20Sopenharmony_ci } else { 3308c2ecf20Sopenharmony_ci if (speed) 3318c2ecf20Sopenharmony_ci *speed = NTB_SPEED_NONE; 3328c2ecf20Sopenharmony_ci if (width) 3338c2ecf20Sopenharmony_ci *width = NTB_WIDTH_NONE; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci dev_dbg(&ntb->pdev->dev, "link is down.\n"); 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci return ret; 3398c2ecf20Sopenharmony_ci} 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_cistatic int amd_ntb_link_enable(struct ntb_dev *ntb, 3428c2ecf20Sopenharmony_ci enum ntb_speed max_speed, 3438c2ecf20Sopenharmony_ci enum ntb_width max_width) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 3468c2ecf20Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci /* Enable event interrupt */ 3498c2ecf20Sopenharmony_ci ndev->int_mask &= ~AMD_EVENT_INTMASK; 3508c2ecf20Sopenharmony_ci writel(ndev->int_mask, mmio + AMD_INTMASK_OFFSET); 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci if (ndev->ntb.topo == NTB_TOPO_SEC) 3538c2ecf20Sopenharmony_ci return -EINVAL; 3548c2ecf20Sopenharmony_ci dev_dbg(&ntb->pdev->dev, "Enabling Link.\n"); 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci return 0; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic int amd_ntb_link_disable(struct ntb_dev *ntb) 3608c2ecf20Sopenharmony_ci{ 3618c2ecf20Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 3628c2ecf20Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci /* Disable event interrupt */ 3658c2ecf20Sopenharmony_ci ndev->int_mask |= AMD_EVENT_INTMASK; 3668c2ecf20Sopenharmony_ci writel(ndev->int_mask, mmio + AMD_INTMASK_OFFSET); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci if (ndev->ntb.topo == NTB_TOPO_SEC) 3698c2ecf20Sopenharmony_ci return -EINVAL; 3708c2ecf20Sopenharmony_ci dev_dbg(&ntb->pdev->dev, "Enabling Link.\n"); 3718c2ecf20Sopenharmony_ci 3728c2ecf20Sopenharmony_ci return 0; 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_cistatic int amd_ntb_peer_mw_count(struct ntb_dev *ntb) 3768c2ecf20Sopenharmony_ci{ 3778c2ecf20Sopenharmony_ci /* The same as for inbound MWs */ 3788c2ecf20Sopenharmony_ci return ntb_ndev(ntb)->mw_count; 3798c2ecf20Sopenharmony_ci} 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_cistatic int amd_ntb_peer_mw_get_addr(struct ntb_dev *ntb, int idx, 3828c2ecf20Sopenharmony_ci phys_addr_t *base, resource_size_t *size) 3838c2ecf20Sopenharmony_ci{ 3848c2ecf20Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 3858c2ecf20Sopenharmony_ci int bar; 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci bar = ndev_mw_to_bar(ndev, idx); 3888c2ecf20Sopenharmony_ci if (bar < 0) 3898c2ecf20Sopenharmony_ci return bar; 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci if (base) 3928c2ecf20Sopenharmony_ci *base = pci_resource_start(ndev->ntb.pdev, bar); 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci if (size) 3958c2ecf20Sopenharmony_ci *size = pci_resource_len(ndev->ntb.pdev, bar); 3968c2ecf20Sopenharmony_ci 3978c2ecf20Sopenharmony_ci return 0; 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic u64 amd_ntb_db_valid_mask(struct ntb_dev *ntb) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci return ntb_ndev(ntb)->db_valid_mask; 4038c2ecf20Sopenharmony_ci} 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_cistatic int amd_ntb_db_vector_count(struct ntb_dev *ntb) 4068c2ecf20Sopenharmony_ci{ 4078c2ecf20Sopenharmony_ci return ntb_ndev(ntb)->db_count; 4088c2ecf20Sopenharmony_ci} 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_cistatic u64 amd_ntb_db_vector_mask(struct ntb_dev *ntb, int db_vector) 4118c2ecf20Sopenharmony_ci{ 4128c2ecf20Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci if (db_vector < 0 || db_vector > ndev->db_count) 4158c2ecf20Sopenharmony_ci return 0; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci return ntb_ndev(ntb)->db_valid_mask & (1ULL << db_vector); 4188c2ecf20Sopenharmony_ci} 4198c2ecf20Sopenharmony_ci 4208c2ecf20Sopenharmony_cistatic u64 amd_ntb_db_read(struct ntb_dev *ntb) 4218c2ecf20Sopenharmony_ci{ 4228c2ecf20Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 4238c2ecf20Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci return (u64)readw(mmio + AMD_DBSTAT_OFFSET); 4268c2ecf20Sopenharmony_ci} 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_cistatic int amd_ntb_db_clear(struct ntb_dev *ntb, u64 db_bits) 4298c2ecf20Sopenharmony_ci{ 4308c2ecf20Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 4318c2ecf20Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci writew((u16)db_bits, mmio + AMD_DBSTAT_OFFSET); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci return 0; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_cistatic int amd_ntb_db_set_mask(struct ntb_dev *ntb, u64 db_bits) 4398c2ecf20Sopenharmony_ci{ 4408c2ecf20Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 4418c2ecf20Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 4428c2ecf20Sopenharmony_ci unsigned long flags; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (db_bits & ~ndev->db_valid_mask) 4458c2ecf20Sopenharmony_ci return -EINVAL; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci spin_lock_irqsave(&ndev->db_mask_lock, flags); 4488c2ecf20Sopenharmony_ci ndev->db_mask |= db_bits; 4498c2ecf20Sopenharmony_ci writew((u16)ndev->db_mask, mmio + AMD_DBMASK_OFFSET); 4508c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ndev->db_mask_lock, flags); 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci return 0; 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic int amd_ntb_db_clear_mask(struct ntb_dev *ntb, u64 db_bits) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 4588c2ecf20Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 4598c2ecf20Sopenharmony_ci unsigned long flags; 4608c2ecf20Sopenharmony_ci 4618c2ecf20Sopenharmony_ci if (db_bits & ~ndev->db_valid_mask) 4628c2ecf20Sopenharmony_ci return -EINVAL; 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci spin_lock_irqsave(&ndev->db_mask_lock, flags); 4658c2ecf20Sopenharmony_ci ndev->db_mask &= ~db_bits; 4668c2ecf20Sopenharmony_ci writew((u16)ndev->db_mask, mmio + AMD_DBMASK_OFFSET); 4678c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&ndev->db_mask_lock, flags); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci return 0; 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic int amd_ntb_peer_db_set(struct ntb_dev *ntb, u64 db_bits) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 4758c2ecf20Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci writew((u16)db_bits, mmio + AMD_DBREQ_OFFSET); 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci return 0; 4808c2ecf20Sopenharmony_ci} 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_cistatic int amd_ntb_spad_count(struct ntb_dev *ntb) 4838c2ecf20Sopenharmony_ci{ 4848c2ecf20Sopenharmony_ci return ntb_ndev(ntb)->spad_count; 4858c2ecf20Sopenharmony_ci} 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_cistatic u32 amd_ntb_spad_read(struct ntb_dev *ntb, int idx) 4888c2ecf20Sopenharmony_ci{ 4898c2ecf20Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 4908c2ecf20Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 4918c2ecf20Sopenharmony_ci u32 offset; 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci if (idx < 0 || idx >= ndev->spad_count) 4948c2ecf20Sopenharmony_ci return 0; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci offset = ndev->self_spad + (idx << 2); 4978c2ecf20Sopenharmony_ci return readl(mmio + AMD_SPAD_OFFSET + offset); 4988c2ecf20Sopenharmony_ci} 4998c2ecf20Sopenharmony_ci 5008c2ecf20Sopenharmony_cistatic int amd_ntb_spad_write(struct ntb_dev *ntb, 5018c2ecf20Sopenharmony_ci int idx, u32 val) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 5048c2ecf20Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 5058c2ecf20Sopenharmony_ci u32 offset; 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ci if (idx < 0 || idx >= ndev->spad_count) 5088c2ecf20Sopenharmony_ci return -EINVAL; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_ci offset = ndev->self_spad + (idx << 2); 5118c2ecf20Sopenharmony_ci writel(val, mmio + AMD_SPAD_OFFSET + offset); 5128c2ecf20Sopenharmony_ci 5138c2ecf20Sopenharmony_ci return 0; 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_cistatic u32 amd_ntb_peer_spad_read(struct ntb_dev *ntb, int pidx, int sidx) 5178c2ecf20Sopenharmony_ci{ 5188c2ecf20Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 5198c2ecf20Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 5208c2ecf20Sopenharmony_ci u32 offset; 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (sidx < 0 || sidx >= ndev->spad_count) 5238c2ecf20Sopenharmony_ci return -EINVAL; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci offset = ndev->peer_spad + (sidx << 2); 5268c2ecf20Sopenharmony_ci return readl(mmio + AMD_SPAD_OFFSET + offset); 5278c2ecf20Sopenharmony_ci} 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_cistatic int amd_ntb_peer_spad_write(struct ntb_dev *ntb, int pidx, 5308c2ecf20Sopenharmony_ci int sidx, u32 val) 5318c2ecf20Sopenharmony_ci{ 5328c2ecf20Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 5338c2ecf20Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 5348c2ecf20Sopenharmony_ci u32 offset; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (sidx < 0 || sidx >= ndev->spad_count) 5378c2ecf20Sopenharmony_ci return -EINVAL; 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci offset = ndev->peer_spad + (sidx << 2); 5408c2ecf20Sopenharmony_ci writel(val, mmio + AMD_SPAD_OFFSET + offset); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci return 0; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ci 5458c2ecf20Sopenharmony_cistatic const struct ntb_dev_ops amd_ntb_ops = { 5468c2ecf20Sopenharmony_ci .mw_count = amd_ntb_mw_count, 5478c2ecf20Sopenharmony_ci .mw_get_align = amd_ntb_mw_get_align, 5488c2ecf20Sopenharmony_ci .mw_set_trans = amd_ntb_mw_set_trans, 5498c2ecf20Sopenharmony_ci .peer_mw_count = amd_ntb_peer_mw_count, 5508c2ecf20Sopenharmony_ci .peer_mw_get_addr = amd_ntb_peer_mw_get_addr, 5518c2ecf20Sopenharmony_ci .link_is_up = amd_ntb_link_is_up, 5528c2ecf20Sopenharmony_ci .link_enable = amd_ntb_link_enable, 5538c2ecf20Sopenharmony_ci .link_disable = amd_ntb_link_disable, 5548c2ecf20Sopenharmony_ci .db_valid_mask = amd_ntb_db_valid_mask, 5558c2ecf20Sopenharmony_ci .db_vector_count = amd_ntb_db_vector_count, 5568c2ecf20Sopenharmony_ci .db_vector_mask = amd_ntb_db_vector_mask, 5578c2ecf20Sopenharmony_ci .db_read = amd_ntb_db_read, 5588c2ecf20Sopenharmony_ci .db_clear = amd_ntb_db_clear, 5598c2ecf20Sopenharmony_ci .db_set_mask = amd_ntb_db_set_mask, 5608c2ecf20Sopenharmony_ci .db_clear_mask = amd_ntb_db_clear_mask, 5618c2ecf20Sopenharmony_ci .peer_db_set = amd_ntb_peer_db_set, 5628c2ecf20Sopenharmony_ci .spad_count = amd_ntb_spad_count, 5638c2ecf20Sopenharmony_ci .spad_read = amd_ntb_spad_read, 5648c2ecf20Sopenharmony_ci .spad_write = amd_ntb_spad_write, 5658c2ecf20Sopenharmony_ci .peer_spad_read = amd_ntb_peer_spad_read, 5668c2ecf20Sopenharmony_ci .peer_spad_write = amd_ntb_peer_spad_write, 5678c2ecf20Sopenharmony_ci}; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_cistatic void amd_ack_smu(struct amd_ntb_dev *ndev, u32 bit) 5708c2ecf20Sopenharmony_ci{ 5718c2ecf20Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 5728c2ecf20Sopenharmony_ci int reg; 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci reg = readl(mmio + AMD_SMUACK_OFFSET); 5758c2ecf20Sopenharmony_ci reg |= bit; 5768c2ecf20Sopenharmony_ci writel(reg, mmio + AMD_SMUACK_OFFSET); 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_cistatic void amd_handle_event(struct amd_ntb_dev *ndev, int vec) 5808c2ecf20Sopenharmony_ci{ 5818c2ecf20Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 5828c2ecf20Sopenharmony_ci struct device *dev = &ndev->ntb.pdev->dev; 5838c2ecf20Sopenharmony_ci u32 status; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci status = readl(mmio + AMD_INTSTAT_OFFSET); 5868c2ecf20Sopenharmony_ci if (!(status & AMD_EVENT_INTMASK)) 5878c2ecf20Sopenharmony_ci return; 5888c2ecf20Sopenharmony_ci 5898c2ecf20Sopenharmony_ci dev_dbg(dev, "status = 0x%x and vec = %d\n", status, vec); 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci status &= AMD_EVENT_INTMASK; 5928c2ecf20Sopenharmony_ci switch (status) { 5938c2ecf20Sopenharmony_ci case AMD_PEER_FLUSH_EVENT: 5948c2ecf20Sopenharmony_ci ndev->peer_sta |= AMD_PEER_FLUSH_EVENT; 5958c2ecf20Sopenharmony_ci dev_info(dev, "Flush is done.\n"); 5968c2ecf20Sopenharmony_ci break; 5978c2ecf20Sopenharmony_ci case AMD_PEER_RESET_EVENT: 5988c2ecf20Sopenharmony_ci case AMD_LINK_DOWN_EVENT: 5998c2ecf20Sopenharmony_ci ndev->peer_sta |= status; 6008c2ecf20Sopenharmony_ci if (status == AMD_LINK_DOWN_EVENT) 6018c2ecf20Sopenharmony_ci ndev->peer_sta &= ~AMD_LINK_UP_EVENT; 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_ci amd_ack_smu(ndev, status); 6048c2ecf20Sopenharmony_ci 6058c2ecf20Sopenharmony_ci /* link down first */ 6068c2ecf20Sopenharmony_ci ntb_link_event(&ndev->ntb); 6078c2ecf20Sopenharmony_ci /* polling peer status */ 6088c2ecf20Sopenharmony_ci schedule_delayed_work(&ndev->hb_timer, AMD_LINK_HB_TIMEOUT); 6098c2ecf20Sopenharmony_ci 6108c2ecf20Sopenharmony_ci break; 6118c2ecf20Sopenharmony_ci case AMD_PEER_D3_EVENT: 6128c2ecf20Sopenharmony_ci case AMD_PEER_PMETO_EVENT: 6138c2ecf20Sopenharmony_ci case AMD_LINK_UP_EVENT: 6148c2ecf20Sopenharmony_ci ndev->peer_sta |= status; 6158c2ecf20Sopenharmony_ci if (status == AMD_LINK_UP_EVENT) 6168c2ecf20Sopenharmony_ci ndev->peer_sta &= ~AMD_LINK_DOWN_EVENT; 6178c2ecf20Sopenharmony_ci else if (status == AMD_PEER_D3_EVENT) 6188c2ecf20Sopenharmony_ci ndev->peer_sta &= ~AMD_PEER_D0_EVENT; 6198c2ecf20Sopenharmony_ci 6208c2ecf20Sopenharmony_ci amd_ack_smu(ndev, status); 6218c2ecf20Sopenharmony_ci 6228c2ecf20Sopenharmony_ci /* link down */ 6238c2ecf20Sopenharmony_ci ntb_link_event(&ndev->ntb); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci break; 6268c2ecf20Sopenharmony_ci case AMD_PEER_D0_EVENT: 6278c2ecf20Sopenharmony_ci mmio = ndev->peer_mmio; 6288c2ecf20Sopenharmony_ci status = readl(mmio + AMD_PMESTAT_OFFSET); 6298c2ecf20Sopenharmony_ci /* check if this is WAKEUP event */ 6308c2ecf20Sopenharmony_ci if (status & 0x1) 6318c2ecf20Sopenharmony_ci dev_info(dev, "Wakeup is done.\n"); 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci ndev->peer_sta |= AMD_PEER_D0_EVENT; 6348c2ecf20Sopenharmony_ci ndev->peer_sta &= ~AMD_PEER_D3_EVENT; 6358c2ecf20Sopenharmony_ci amd_ack_smu(ndev, AMD_PEER_D0_EVENT); 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci /* start a timer to poll link status */ 6388c2ecf20Sopenharmony_ci schedule_delayed_work(&ndev->hb_timer, 6398c2ecf20Sopenharmony_ci AMD_LINK_HB_TIMEOUT); 6408c2ecf20Sopenharmony_ci break; 6418c2ecf20Sopenharmony_ci default: 6428c2ecf20Sopenharmony_ci dev_info(dev, "event status = 0x%x.\n", status); 6438c2ecf20Sopenharmony_ci break; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci /* Clear the interrupt status */ 6478c2ecf20Sopenharmony_ci writel(status, mmio + AMD_INTSTAT_OFFSET); 6488c2ecf20Sopenharmony_ci} 6498c2ecf20Sopenharmony_ci 6508c2ecf20Sopenharmony_cistatic void amd_handle_db_event(struct amd_ntb_dev *ndev, int vec) 6518c2ecf20Sopenharmony_ci{ 6528c2ecf20Sopenharmony_ci struct device *dev = &ndev->ntb.pdev->dev; 6538c2ecf20Sopenharmony_ci u64 status; 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci status = amd_ntb_db_read(&ndev->ntb); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci dev_dbg(dev, "status = 0x%llx and vec = %d\n", status, vec); 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci /* 6608c2ecf20Sopenharmony_ci * Since we had reserved highest order bit of DB for signaling peer of 6618c2ecf20Sopenharmony_ci * a special event, this is the only status bit we should be concerned 6628c2ecf20Sopenharmony_ci * here now. 6638c2ecf20Sopenharmony_ci */ 6648c2ecf20Sopenharmony_ci if (status & BIT(ndev->db_last_bit)) { 6658c2ecf20Sopenharmony_ci ntb_db_clear(&ndev->ntb, BIT(ndev->db_last_bit)); 6668c2ecf20Sopenharmony_ci /* send link down event notification */ 6678c2ecf20Sopenharmony_ci ntb_link_event(&ndev->ntb); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci /* 6708c2ecf20Sopenharmony_ci * If we are here, that means the peer has signalled a special 6718c2ecf20Sopenharmony_ci * event which notifies that the peer driver has been 6728c2ecf20Sopenharmony_ci * un-loaded for some reason. Since there is a chance that the 6738c2ecf20Sopenharmony_ci * peer will load its driver again sometime, we schedule link 6748c2ecf20Sopenharmony_ci * polling routine. 6758c2ecf20Sopenharmony_ci */ 6768c2ecf20Sopenharmony_ci schedule_delayed_work(&ndev->hb_timer, AMD_LINK_HB_TIMEOUT); 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci} 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_cistatic irqreturn_t ndev_interrupt(struct amd_ntb_dev *ndev, int vec) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci dev_dbg(&ndev->ntb.pdev->dev, "vec %d\n", vec); 6838c2ecf20Sopenharmony_ci 6848c2ecf20Sopenharmony_ci if (vec > (AMD_DB_CNT - 1) || (ndev->msix_vec_count == 1)) 6858c2ecf20Sopenharmony_ci amd_handle_event(ndev, vec); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_ci if (vec < AMD_DB_CNT) { 6888c2ecf20Sopenharmony_ci amd_handle_db_event(ndev, vec); 6898c2ecf20Sopenharmony_ci ntb_db_event(&ndev->ntb, vec); 6908c2ecf20Sopenharmony_ci } 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci return IRQ_HANDLED; 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_cistatic irqreturn_t ndev_vec_isr(int irq, void *dev) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci struct amd_ntb_vec *nvec = dev; 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci return ndev_interrupt(nvec->ndev, nvec->num); 7008c2ecf20Sopenharmony_ci} 7018c2ecf20Sopenharmony_ci 7028c2ecf20Sopenharmony_cistatic irqreturn_t ndev_irq_isr(int irq, void *dev) 7038c2ecf20Sopenharmony_ci{ 7048c2ecf20Sopenharmony_ci struct amd_ntb_dev *ndev = dev; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci return ndev_interrupt(ndev, irq - ndev->ntb.pdev->irq); 7078c2ecf20Sopenharmony_ci} 7088c2ecf20Sopenharmony_ci 7098c2ecf20Sopenharmony_cistatic int ndev_init_isr(struct amd_ntb_dev *ndev, 7108c2ecf20Sopenharmony_ci int msix_min, int msix_max) 7118c2ecf20Sopenharmony_ci{ 7128c2ecf20Sopenharmony_ci struct pci_dev *pdev; 7138c2ecf20Sopenharmony_ci int rc, i, msix_count, node; 7148c2ecf20Sopenharmony_ci 7158c2ecf20Sopenharmony_ci pdev = ndev->ntb.pdev; 7168c2ecf20Sopenharmony_ci 7178c2ecf20Sopenharmony_ci node = dev_to_node(&pdev->dev); 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci ndev->db_mask = ndev->db_valid_mask; 7208c2ecf20Sopenharmony_ci 7218c2ecf20Sopenharmony_ci /* Try to set up msix irq */ 7228c2ecf20Sopenharmony_ci ndev->vec = kcalloc_node(msix_max, sizeof(*ndev->vec), 7238c2ecf20Sopenharmony_ci GFP_KERNEL, node); 7248c2ecf20Sopenharmony_ci if (!ndev->vec) 7258c2ecf20Sopenharmony_ci goto err_msix_vec_alloc; 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci ndev->msix = kcalloc_node(msix_max, sizeof(*ndev->msix), 7288c2ecf20Sopenharmony_ci GFP_KERNEL, node); 7298c2ecf20Sopenharmony_ci if (!ndev->msix) 7308c2ecf20Sopenharmony_ci goto err_msix_alloc; 7318c2ecf20Sopenharmony_ci 7328c2ecf20Sopenharmony_ci for (i = 0; i < msix_max; ++i) 7338c2ecf20Sopenharmony_ci ndev->msix[i].entry = i; 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci msix_count = pci_enable_msix_range(pdev, ndev->msix, 7368c2ecf20Sopenharmony_ci msix_min, msix_max); 7378c2ecf20Sopenharmony_ci if (msix_count < 0) 7388c2ecf20Sopenharmony_ci goto err_msix_enable; 7398c2ecf20Sopenharmony_ci 7408c2ecf20Sopenharmony_ci /* NOTE: Disable MSIX if msix count is less than 16 because of 7418c2ecf20Sopenharmony_ci * hardware limitation. 7428c2ecf20Sopenharmony_ci */ 7438c2ecf20Sopenharmony_ci if (msix_count < msix_min) { 7448c2ecf20Sopenharmony_ci pci_disable_msix(pdev); 7458c2ecf20Sopenharmony_ci goto err_msix_enable; 7468c2ecf20Sopenharmony_ci } 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci for (i = 0; i < msix_count; ++i) { 7498c2ecf20Sopenharmony_ci ndev->vec[i].ndev = ndev; 7508c2ecf20Sopenharmony_ci ndev->vec[i].num = i; 7518c2ecf20Sopenharmony_ci rc = request_irq(ndev->msix[i].vector, ndev_vec_isr, 0, 7528c2ecf20Sopenharmony_ci "ndev_vec_isr", &ndev->vec[i]); 7538c2ecf20Sopenharmony_ci if (rc) 7548c2ecf20Sopenharmony_ci goto err_msix_request; 7558c2ecf20Sopenharmony_ci } 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Using msix interrupts\n"); 7588c2ecf20Sopenharmony_ci ndev->db_count = msix_min; 7598c2ecf20Sopenharmony_ci ndev->msix_vec_count = msix_max; 7608c2ecf20Sopenharmony_ci return 0; 7618c2ecf20Sopenharmony_ci 7628c2ecf20Sopenharmony_cierr_msix_request: 7638c2ecf20Sopenharmony_ci while (i-- > 0) 7648c2ecf20Sopenharmony_ci free_irq(ndev->msix[i].vector, &ndev->vec[i]); 7658c2ecf20Sopenharmony_ci pci_disable_msix(pdev); 7668c2ecf20Sopenharmony_cierr_msix_enable: 7678c2ecf20Sopenharmony_ci kfree(ndev->msix); 7688c2ecf20Sopenharmony_cierr_msix_alloc: 7698c2ecf20Sopenharmony_ci kfree(ndev->vec); 7708c2ecf20Sopenharmony_cierr_msix_vec_alloc: 7718c2ecf20Sopenharmony_ci ndev->msix = NULL; 7728c2ecf20Sopenharmony_ci ndev->vec = NULL; 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci /* Try to set up msi irq */ 7758c2ecf20Sopenharmony_ci rc = pci_enable_msi(pdev); 7768c2ecf20Sopenharmony_ci if (rc) 7778c2ecf20Sopenharmony_ci goto err_msi_enable; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci rc = request_irq(pdev->irq, ndev_irq_isr, 0, 7808c2ecf20Sopenharmony_ci "ndev_irq_isr", ndev); 7818c2ecf20Sopenharmony_ci if (rc) 7828c2ecf20Sopenharmony_ci goto err_msi_request; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Using msi interrupts\n"); 7858c2ecf20Sopenharmony_ci ndev->db_count = 1; 7868c2ecf20Sopenharmony_ci ndev->msix_vec_count = 1; 7878c2ecf20Sopenharmony_ci return 0; 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_cierr_msi_request: 7908c2ecf20Sopenharmony_ci pci_disable_msi(pdev); 7918c2ecf20Sopenharmony_cierr_msi_enable: 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci /* Try to set up intx irq */ 7948c2ecf20Sopenharmony_ci pci_intx(pdev, 1); 7958c2ecf20Sopenharmony_ci 7968c2ecf20Sopenharmony_ci rc = request_irq(pdev->irq, ndev_irq_isr, IRQF_SHARED, 7978c2ecf20Sopenharmony_ci "ndev_irq_isr", ndev); 7988c2ecf20Sopenharmony_ci if (rc) 7998c2ecf20Sopenharmony_ci goto err_intx_request; 8008c2ecf20Sopenharmony_ci 8018c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "Using intx interrupts\n"); 8028c2ecf20Sopenharmony_ci ndev->db_count = 1; 8038c2ecf20Sopenharmony_ci ndev->msix_vec_count = 1; 8048c2ecf20Sopenharmony_ci return 0; 8058c2ecf20Sopenharmony_ci 8068c2ecf20Sopenharmony_cierr_intx_request: 8078c2ecf20Sopenharmony_ci return rc; 8088c2ecf20Sopenharmony_ci} 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_cistatic void ndev_deinit_isr(struct amd_ntb_dev *ndev) 8118c2ecf20Sopenharmony_ci{ 8128c2ecf20Sopenharmony_ci struct pci_dev *pdev; 8138c2ecf20Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 8148c2ecf20Sopenharmony_ci int i; 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_ci pdev = ndev->ntb.pdev; 8178c2ecf20Sopenharmony_ci 8188c2ecf20Sopenharmony_ci /* Mask all doorbell interrupts */ 8198c2ecf20Sopenharmony_ci ndev->db_mask = ndev->db_valid_mask; 8208c2ecf20Sopenharmony_ci writel(ndev->db_mask, mmio + AMD_DBMASK_OFFSET); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci if (ndev->msix) { 8238c2ecf20Sopenharmony_ci i = ndev->msix_vec_count; 8248c2ecf20Sopenharmony_ci while (i--) 8258c2ecf20Sopenharmony_ci free_irq(ndev->msix[i].vector, &ndev->vec[i]); 8268c2ecf20Sopenharmony_ci pci_disable_msix(pdev); 8278c2ecf20Sopenharmony_ci kfree(ndev->msix); 8288c2ecf20Sopenharmony_ci kfree(ndev->vec); 8298c2ecf20Sopenharmony_ci } else { 8308c2ecf20Sopenharmony_ci free_irq(pdev->irq, ndev); 8318c2ecf20Sopenharmony_ci if (pci_dev_msi_enabled(pdev)) 8328c2ecf20Sopenharmony_ci pci_disable_msi(pdev); 8338c2ecf20Sopenharmony_ci else 8348c2ecf20Sopenharmony_ci pci_intx(pdev, 0); 8358c2ecf20Sopenharmony_ci } 8368c2ecf20Sopenharmony_ci} 8378c2ecf20Sopenharmony_ci 8388c2ecf20Sopenharmony_cistatic ssize_t ndev_debugfs_read(struct file *filp, char __user *ubuf, 8398c2ecf20Sopenharmony_ci size_t count, loff_t *offp) 8408c2ecf20Sopenharmony_ci{ 8418c2ecf20Sopenharmony_ci struct amd_ntb_dev *ndev; 8428c2ecf20Sopenharmony_ci void __iomem *mmio; 8438c2ecf20Sopenharmony_ci char *buf; 8448c2ecf20Sopenharmony_ci size_t buf_size; 8458c2ecf20Sopenharmony_ci ssize_t ret, off; 8468c2ecf20Sopenharmony_ci union { u64 v64; u32 v32; u16 v16; } u; 8478c2ecf20Sopenharmony_ci 8488c2ecf20Sopenharmony_ci ndev = filp->private_data; 8498c2ecf20Sopenharmony_ci mmio = ndev->self_mmio; 8508c2ecf20Sopenharmony_ci 8518c2ecf20Sopenharmony_ci buf_size = min(count, 0x800ul); 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci buf = kmalloc(buf_size, GFP_KERNEL); 8548c2ecf20Sopenharmony_ci if (!buf) 8558c2ecf20Sopenharmony_ci return -ENOMEM; 8568c2ecf20Sopenharmony_ci 8578c2ecf20Sopenharmony_ci off = 0; 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 8608c2ecf20Sopenharmony_ci "NTB Device Information:\n"); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 8638c2ecf20Sopenharmony_ci "Connection Topology -\t%s\n", 8648c2ecf20Sopenharmony_ci ntb_topo_string(ndev->ntb.topo)); 8658c2ecf20Sopenharmony_ci 8668c2ecf20Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 8678c2ecf20Sopenharmony_ci "LNK STA -\t\t%#06x\n", ndev->lnk_sta); 8688c2ecf20Sopenharmony_ci 8698c2ecf20Sopenharmony_ci if (!amd_link_is_up(ndev)) { 8708c2ecf20Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 8718c2ecf20Sopenharmony_ci "Link Status -\t\tDown\n"); 8728c2ecf20Sopenharmony_ci } else { 8738c2ecf20Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 8748c2ecf20Sopenharmony_ci "Link Status -\t\tUp\n"); 8758c2ecf20Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 8768c2ecf20Sopenharmony_ci "Link Speed -\t\tPCI-E Gen %u\n", 8778c2ecf20Sopenharmony_ci NTB_LNK_STA_SPEED(ndev->lnk_sta)); 8788c2ecf20Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 8798c2ecf20Sopenharmony_ci "Link Width -\t\tx%u\n", 8808c2ecf20Sopenharmony_ci NTB_LNK_STA_WIDTH(ndev->lnk_sta)); 8818c2ecf20Sopenharmony_ci } 8828c2ecf20Sopenharmony_ci 8838c2ecf20Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 8848c2ecf20Sopenharmony_ci "Memory Window Count -\t%u\n", ndev->mw_count); 8858c2ecf20Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 8868c2ecf20Sopenharmony_ci "Scratchpad Count -\t%u\n", ndev->spad_count); 8878c2ecf20Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 8888c2ecf20Sopenharmony_ci "Doorbell Count -\t%u\n", ndev->db_count); 8898c2ecf20Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 8908c2ecf20Sopenharmony_ci "MSIX Vector Count -\t%u\n", ndev->msix_vec_count); 8918c2ecf20Sopenharmony_ci 8928c2ecf20Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 8938c2ecf20Sopenharmony_ci "Doorbell Valid Mask -\t%#llx\n", ndev->db_valid_mask); 8948c2ecf20Sopenharmony_ci 8958c2ecf20Sopenharmony_ci u.v32 = readl(ndev->self_mmio + AMD_DBMASK_OFFSET); 8968c2ecf20Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 8978c2ecf20Sopenharmony_ci "Doorbell Mask -\t\t\t%#06x\n", u.v32); 8988c2ecf20Sopenharmony_ci 8998c2ecf20Sopenharmony_ci u.v32 = readl(mmio + AMD_DBSTAT_OFFSET); 9008c2ecf20Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 9018c2ecf20Sopenharmony_ci "Doorbell Bell -\t\t\t%#06x\n", u.v32); 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 9048c2ecf20Sopenharmony_ci "\nNTB Incoming XLAT:\n"); 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci u.v64 = read64(mmio + AMD_BAR1XLAT_OFFSET); 9078c2ecf20Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 9088c2ecf20Sopenharmony_ci "XLAT1 -\t\t%#018llx\n", u.v64); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci u.v64 = read64(ndev->self_mmio + AMD_BAR23XLAT_OFFSET); 9118c2ecf20Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 9128c2ecf20Sopenharmony_ci "XLAT23 -\t\t%#018llx\n", u.v64); 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci u.v64 = read64(ndev->self_mmio + AMD_BAR45XLAT_OFFSET); 9158c2ecf20Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 9168c2ecf20Sopenharmony_ci "XLAT45 -\t\t%#018llx\n", u.v64); 9178c2ecf20Sopenharmony_ci 9188c2ecf20Sopenharmony_ci u.v32 = readl(mmio + AMD_BAR1LMT_OFFSET); 9198c2ecf20Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 9208c2ecf20Sopenharmony_ci "LMT1 -\t\t\t%#06x\n", u.v32); 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci u.v64 = read64(ndev->self_mmio + AMD_BAR23LMT_OFFSET); 9238c2ecf20Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 9248c2ecf20Sopenharmony_ci "LMT23 -\t\t\t%#018llx\n", u.v64); 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci u.v64 = read64(ndev->self_mmio + AMD_BAR45LMT_OFFSET); 9278c2ecf20Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 9288c2ecf20Sopenharmony_ci "LMT45 -\t\t\t%#018llx\n", u.v64); 9298c2ecf20Sopenharmony_ci 9308c2ecf20Sopenharmony_ci ret = simple_read_from_buffer(ubuf, count, offp, buf, off); 9318c2ecf20Sopenharmony_ci kfree(buf); 9328c2ecf20Sopenharmony_ci return ret; 9338c2ecf20Sopenharmony_ci} 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_cistatic void ndev_init_debugfs(struct amd_ntb_dev *ndev) 9368c2ecf20Sopenharmony_ci{ 9378c2ecf20Sopenharmony_ci if (!debugfs_dir) { 9388c2ecf20Sopenharmony_ci ndev->debugfs_dir = NULL; 9398c2ecf20Sopenharmony_ci ndev->debugfs_info = NULL; 9408c2ecf20Sopenharmony_ci } else { 9418c2ecf20Sopenharmony_ci ndev->debugfs_dir = 9428c2ecf20Sopenharmony_ci debugfs_create_dir(pci_name(ndev->ntb.pdev), 9438c2ecf20Sopenharmony_ci debugfs_dir); 9448c2ecf20Sopenharmony_ci if (!ndev->debugfs_dir) 9458c2ecf20Sopenharmony_ci ndev->debugfs_info = NULL; 9468c2ecf20Sopenharmony_ci else 9478c2ecf20Sopenharmony_ci ndev->debugfs_info = 9488c2ecf20Sopenharmony_ci debugfs_create_file("info", S_IRUSR, 9498c2ecf20Sopenharmony_ci ndev->debugfs_dir, ndev, 9508c2ecf20Sopenharmony_ci &amd_ntb_debugfs_info); 9518c2ecf20Sopenharmony_ci } 9528c2ecf20Sopenharmony_ci} 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_cistatic void ndev_deinit_debugfs(struct amd_ntb_dev *ndev) 9558c2ecf20Sopenharmony_ci{ 9568c2ecf20Sopenharmony_ci debugfs_remove_recursive(ndev->debugfs_dir); 9578c2ecf20Sopenharmony_ci} 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_cistatic inline void ndev_init_struct(struct amd_ntb_dev *ndev, 9608c2ecf20Sopenharmony_ci struct pci_dev *pdev) 9618c2ecf20Sopenharmony_ci{ 9628c2ecf20Sopenharmony_ci ndev->ntb.pdev = pdev; 9638c2ecf20Sopenharmony_ci ndev->ntb.topo = NTB_TOPO_NONE; 9648c2ecf20Sopenharmony_ci ndev->ntb.ops = &amd_ntb_ops; 9658c2ecf20Sopenharmony_ci ndev->int_mask = AMD_EVENT_INTMASK; 9668c2ecf20Sopenharmony_ci spin_lock_init(&ndev->db_mask_lock); 9678c2ecf20Sopenharmony_ci} 9688c2ecf20Sopenharmony_ci 9698c2ecf20Sopenharmony_cistatic int amd_poll_link(struct amd_ntb_dev *ndev) 9708c2ecf20Sopenharmony_ci{ 9718c2ecf20Sopenharmony_ci void __iomem *mmio = ndev->peer_mmio; 9728c2ecf20Sopenharmony_ci u32 reg; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci reg = readl(mmio + AMD_SIDEINFO_OFFSET); 9758c2ecf20Sopenharmony_ci reg &= AMD_SIDE_READY; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci dev_dbg(&ndev->ntb.pdev->dev, "%s: reg_val = 0x%x.\n", __func__, reg); 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci ndev->cntl_sta = reg; 9808c2ecf20Sopenharmony_ci 9818c2ecf20Sopenharmony_ci amd_ntb_get_link_status(ndev); 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci return ndev->cntl_sta; 9848c2ecf20Sopenharmony_ci} 9858c2ecf20Sopenharmony_ci 9868c2ecf20Sopenharmony_cistatic void amd_link_hb(struct work_struct *work) 9878c2ecf20Sopenharmony_ci{ 9888c2ecf20Sopenharmony_ci struct amd_ntb_dev *ndev = hb_ndev(work); 9898c2ecf20Sopenharmony_ci 9908c2ecf20Sopenharmony_ci if (amd_poll_link(ndev)) 9918c2ecf20Sopenharmony_ci ntb_link_event(&ndev->ntb); 9928c2ecf20Sopenharmony_ci 9938c2ecf20Sopenharmony_ci if (!amd_link_is_up(ndev)) 9948c2ecf20Sopenharmony_ci schedule_delayed_work(&ndev->hb_timer, AMD_LINK_HB_TIMEOUT); 9958c2ecf20Sopenharmony_ci} 9968c2ecf20Sopenharmony_ci 9978c2ecf20Sopenharmony_cistatic int amd_init_isr(struct amd_ntb_dev *ndev) 9988c2ecf20Sopenharmony_ci{ 9998c2ecf20Sopenharmony_ci return ndev_init_isr(ndev, AMD_DB_CNT, AMD_MSIX_VECTOR_CNT); 10008c2ecf20Sopenharmony_ci} 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_cistatic void amd_set_side_info_reg(struct amd_ntb_dev *ndev, bool peer) 10038c2ecf20Sopenharmony_ci{ 10048c2ecf20Sopenharmony_ci void __iomem *mmio = NULL; 10058c2ecf20Sopenharmony_ci unsigned int reg; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci if (peer) 10088c2ecf20Sopenharmony_ci mmio = ndev->peer_mmio; 10098c2ecf20Sopenharmony_ci else 10108c2ecf20Sopenharmony_ci mmio = ndev->self_mmio; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci reg = readl(mmio + AMD_SIDEINFO_OFFSET); 10138c2ecf20Sopenharmony_ci if (!(reg & AMD_SIDE_READY)) { 10148c2ecf20Sopenharmony_ci reg |= AMD_SIDE_READY; 10158c2ecf20Sopenharmony_ci writel(reg, mmio + AMD_SIDEINFO_OFFSET); 10168c2ecf20Sopenharmony_ci } 10178c2ecf20Sopenharmony_ci} 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_cistatic void amd_clear_side_info_reg(struct amd_ntb_dev *ndev, bool peer) 10208c2ecf20Sopenharmony_ci{ 10218c2ecf20Sopenharmony_ci void __iomem *mmio = NULL; 10228c2ecf20Sopenharmony_ci unsigned int reg; 10238c2ecf20Sopenharmony_ci 10248c2ecf20Sopenharmony_ci if (peer) 10258c2ecf20Sopenharmony_ci mmio = ndev->peer_mmio; 10268c2ecf20Sopenharmony_ci else 10278c2ecf20Sopenharmony_ci mmio = ndev->self_mmio; 10288c2ecf20Sopenharmony_ci 10298c2ecf20Sopenharmony_ci reg = readl(mmio + AMD_SIDEINFO_OFFSET); 10308c2ecf20Sopenharmony_ci if (reg & AMD_SIDE_READY) { 10318c2ecf20Sopenharmony_ci reg &= ~AMD_SIDE_READY; 10328c2ecf20Sopenharmony_ci writel(reg, mmio + AMD_SIDEINFO_OFFSET); 10338c2ecf20Sopenharmony_ci readl(mmio + AMD_SIDEINFO_OFFSET); 10348c2ecf20Sopenharmony_ci } 10358c2ecf20Sopenharmony_ci} 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_cistatic void amd_init_side_info(struct amd_ntb_dev *ndev) 10388c2ecf20Sopenharmony_ci{ 10398c2ecf20Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 10408c2ecf20Sopenharmony_ci u32 ntb_ctl; 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_ci amd_set_side_info_reg(ndev, false); 10438c2ecf20Sopenharmony_ci 10448c2ecf20Sopenharmony_ci ntb_ctl = readl(mmio + AMD_CNTL_OFFSET); 10458c2ecf20Sopenharmony_ci ntb_ctl |= (PMM_REG_CTL | SMM_REG_CTL); 10468c2ecf20Sopenharmony_ci writel(ntb_ctl, mmio + AMD_CNTL_OFFSET); 10478c2ecf20Sopenharmony_ci} 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_cistatic void amd_deinit_side_info(struct amd_ntb_dev *ndev) 10508c2ecf20Sopenharmony_ci{ 10518c2ecf20Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 10528c2ecf20Sopenharmony_ci u32 ntb_ctl; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci amd_clear_side_info_reg(ndev, false); 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci ntb_ctl = readl(mmio + AMD_CNTL_OFFSET); 10578c2ecf20Sopenharmony_ci ntb_ctl &= ~(PMM_REG_CTL | SMM_REG_CTL); 10588c2ecf20Sopenharmony_ci writel(ntb_ctl, mmio + AMD_CNTL_OFFSET); 10598c2ecf20Sopenharmony_ci} 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_cistatic int amd_init_ntb(struct amd_ntb_dev *ndev) 10628c2ecf20Sopenharmony_ci{ 10638c2ecf20Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci ndev->mw_count = ndev->dev_data->mw_count; 10668c2ecf20Sopenharmony_ci ndev->spad_count = AMD_SPADS_CNT; 10678c2ecf20Sopenharmony_ci ndev->db_count = AMD_DB_CNT; 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci switch (ndev->ntb.topo) { 10708c2ecf20Sopenharmony_ci case NTB_TOPO_PRI: 10718c2ecf20Sopenharmony_ci case NTB_TOPO_SEC: 10728c2ecf20Sopenharmony_ci ndev->spad_count >>= 1; 10738c2ecf20Sopenharmony_ci if (ndev->ntb.topo == NTB_TOPO_PRI) { 10748c2ecf20Sopenharmony_ci ndev->self_spad = 0; 10758c2ecf20Sopenharmony_ci ndev->peer_spad = 0x20; 10768c2ecf20Sopenharmony_ci } else { 10778c2ecf20Sopenharmony_ci ndev->self_spad = 0x20; 10788c2ecf20Sopenharmony_ci ndev->peer_spad = 0; 10798c2ecf20Sopenharmony_ci } 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&ndev->hb_timer, amd_link_hb); 10828c2ecf20Sopenharmony_ci schedule_delayed_work(&ndev->hb_timer, AMD_LINK_HB_TIMEOUT); 10838c2ecf20Sopenharmony_ci 10848c2ecf20Sopenharmony_ci break; 10858c2ecf20Sopenharmony_ci default: 10868c2ecf20Sopenharmony_ci dev_err(&ndev->ntb.pdev->dev, 10878c2ecf20Sopenharmony_ci "AMD NTB does not support B2B mode.\n"); 10888c2ecf20Sopenharmony_ci return -EINVAL; 10898c2ecf20Sopenharmony_ci } 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_ci /* Mask event interrupts */ 10928c2ecf20Sopenharmony_ci writel(ndev->int_mask, mmio + AMD_INTMASK_OFFSET); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci return 0; 10958c2ecf20Sopenharmony_ci} 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_cistatic enum ntb_topo amd_get_topo(struct amd_ntb_dev *ndev) 10988c2ecf20Sopenharmony_ci{ 10998c2ecf20Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 11008c2ecf20Sopenharmony_ci u32 info; 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci info = readl(mmio + AMD_SIDEINFO_OFFSET); 11038c2ecf20Sopenharmony_ci if (info & AMD_SIDE_MASK) 11048c2ecf20Sopenharmony_ci return NTB_TOPO_SEC; 11058c2ecf20Sopenharmony_ci else 11068c2ecf20Sopenharmony_ci return NTB_TOPO_PRI; 11078c2ecf20Sopenharmony_ci} 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_cistatic int amd_init_dev(struct amd_ntb_dev *ndev) 11108c2ecf20Sopenharmony_ci{ 11118c2ecf20Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 11128c2ecf20Sopenharmony_ci struct pci_dev *pdev; 11138c2ecf20Sopenharmony_ci int rc = 0; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci pdev = ndev->ntb.pdev; 11168c2ecf20Sopenharmony_ci 11178c2ecf20Sopenharmony_ci ndev->ntb.topo = amd_get_topo(ndev); 11188c2ecf20Sopenharmony_ci dev_dbg(&pdev->dev, "AMD NTB topo is %s\n", 11198c2ecf20Sopenharmony_ci ntb_topo_string(ndev->ntb.topo)); 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci rc = amd_init_ntb(ndev); 11228c2ecf20Sopenharmony_ci if (rc) 11238c2ecf20Sopenharmony_ci return rc; 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci rc = amd_init_isr(ndev); 11268c2ecf20Sopenharmony_ci if (rc) { 11278c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "fail to init isr.\n"); 11288c2ecf20Sopenharmony_ci return rc; 11298c2ecf20Sopenharmony_ci } 11308c2ecf20Sopenharmony_ci 11318c2ecf20Sopenharmony_ci ndev->db_valid_mask = BIT_ULL(ndev->db_count) - 1; 11328c2ecf20Sopenharmony_ci /* 11338c2ecf20Sopenharmony_ci * We reserve the highest order bit of the DB register which will 11348c2ecf20Sopenharmony_ci * be used to notify peer when the driver on this side is being 11358c2ecf20Sopenharmony_ci * un-loaded. 11368c2ecf20Sopenharmony_ci */ 11378c2ecf20Sopenharmony_ci ndev->db_last_bit = 11388c2ecf20Sopenharmony_ci find_last_bit((unsigned long *)&ndev->db_valid_mask, 11398c2ecf20Sopenharmony_ci hweight64(ndev->db_valid_mask)); 11408c2ecf20Sopenharmony_ci writew((u16)~BIT(ndev->db_last_bit), mmio + AMD_DBMASK_OFFSET); 11418c2ecf20Sopenharmony_ci /* 11428c2ecf20Sopenharmony_ci * Since now there is one less bit to account for, the DB count 11438c2ecf20Sopenharmony_ci * and DB mask should be adjusted accordingly. 11448c2ecf20Sopenharmony_ci */ 11458c2ecf20Sopenharmony_ci ndev->db_count -= 1; 11468c2ecf20Sopenharmony_ci ndev->db_valid_mask = BIT_ULL(ndev->db_count) - 1; 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci /* Enable Link-Up and Link-Down event interrupts */ 11498c2ecf20Sopenharmony_ci ndev->int_mask &= ~(AMD_LINK_UP_EVENT | AMD_LINK_DOWN_EVENT); 11508c2ecf20Sopenharmony_ci writel(ndev->int_mask, mmio + AMD_INTMASK_OFFSET); 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_ci return 0; 11538c2ecf20Sopenharmony_ci} 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_cistatic void amd_deinit_dev(struct amd_ntb_dev *ndev) 11568c2ecf20Sopenharmony_ci{ 11578c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&ndev->hb_timer); 11588c2ecf20Sopenharmony_ci 11598c2ecf20Sopenharmony_ci ndev_deinit_isr(ndev); 11608c2ecf20Sopenharmony_ci} 11618c2ecf20Sopenharmony_ci 11628c2ecf20Sopenharmony_cistatic int amd_ntb_init_pci(struct amd_ntb_dev *ndev, 11638c2ecf20Sopenharmony_ci struct pci_dev *pdev) 11648c2ecf20Sopenharmony_ci{ 11658c2ecf20Sopenharmony_ci int rc; 11668c2ecf20Sopenharmony_ci 11678c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, ndev); 11688c2ecf20Sopenharmony_ci 11698c2ecf20Sopenharmony_ci rc = pci_enable_device(pdev); 11708c2ecf20Sopenharmony_ci if (rc) 11718c2ecf20Sopenharmony_ci goto err_pci_enable; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci rc = pci_request_regions(pdev, NTB_NAME); 11748c2ecf20Sopenharmony_ci if (rc) 11758c2ecf20Sopenharmony_ci goto err_pci_regions; 11768c2ecf20Sopenharmony_ci 11778c2ecf20Sopenharmony_ci pci_set_master(pdev); 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_ci rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); 11808c2ecf20Sopenharmony_ci if (rc) { 11818c2ecf20Sopenharmony_ci rc = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); 11828c2ecf20Sopenharmony_ci if (rc) 11838c2ecf20Sopenharmony_ci goto err_dma_mask; 11848c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "Cannot DMA highmem\n"); 11858c2ecf20Sopenharmony_ci } 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_ci rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); 11888c2ecf20Sopenharmony_ci if (rc) { 11898c2ecf20Sopenharmony_ci rc = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); 11908c2ecf20Sopenharmony_ci if (rc) 11918c2ecf20Sopenharmony_ci goto err_dma_mask; 11928c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "Cannot DMA consistent highmem\n"); 11938c2ecf20Sopenharmony_ci } 11948c2ecf20Sopenharmony_ci 11958c2ecf20Sopenharmony_ci ndev->self_mmio = pci_iomap(pdev, 0, 0); 11968c2ecf20Sopenharmony_ci if (!ndev->self_mmio) { 11978c2ecf20Sopenharmony_ci rc = -EIO; 11988c2ecf20Sopenharmony_ci goto err_dma_mask; 11998c2ecf20Sopenharmony_ci } 12008c2ecf20Sopenharmony_ci ndev->peer_mmio = ndev->self_mmio + AMD_PEER_OFFSET; 12018c2ecf20Sopenharmony_ci 12028c2ecf20Sopenharmony_ci return 0; 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_cierr_dma_mask: 12058c2ecf20Sopenharmony_ci pci_clear_master(pdev); 12068c2ecf20Sopenharmony_ci pci_release_regions(pdev); 12078c2ecf20Sopenharmony_cierr_pci_regions: 12088c2ecf20Sopenharmony_ci pci_disable_device(pdev); 12098c2ecf20Sopenharmony_cierr_pci_enable: 12108c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, NULL); 12118c2ecf20Sopenharmony_ci return rc; 12128c2ecf20Sopenharmony_ci} 12138c2ecf20Sopenharmony_ci 12148c2ecf20Sopenharmony_cistatic void amd_ntb_deinit_pci(struct amd_ntb_dev *ndev) 12158c2ecf20Sopenharmony_ci{ 12168c2ecf20Sopenharmony_ci struct pci_dev *pdev = ndev->ntb.pdev; 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci pci_iounmap(pdev, ndev->self_mmio); 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci pci_clear_master(pdev); 12218c2ecf20Sopenharmony_ci pci_release_regions(pdev); 12228c2ecf20Sopenharmony_ci pci_disable_device(pdev); 12238c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, NULL); 12248c2ecf20Sopenharmony_ci} 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_cistatic int amd_ntb_pci_probe(struct pci_dev *pdev, 12278c2ecf20Sopenharmony_ci const struct pci_device_id *id) 12288c2ecf20Sopenharmony_ci{ 12298c2ecf20Sopenharmony_ci struct amd_ntb_dev *ndev; 12308c2ecf20Sopenharmony_ci int rc, node; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci node = dev_to_node(&pdev->dev); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci ndev = kzalloc_node(sizeof(*ndev), GFP_KERNEL, node); 12358c2ecf20Sopenharmony_ci if (!ndev) { 12368c2ecf20Sopenharmony_ci rc = -ENOMEM; 12378c2ecf20Sopenharmony_ci goto err_ndev; 12388c2ecf20Sopenharmony_ci } 12398c2ecf20Sopenharmony_ci 12408c2ecf20Sopenharmony_ci ndev->dev_data = (struct ntb_dev_data *)id->driver_data; 12418c2ecf20Sopenharmony_ci 12428c2ecf20Sopenharmony_ci ndev_init_struct(ndev, pdev); 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci rc = amd_ntb_init_pci(ndev, pdev); 12458c2ecf20Sopenharmony_ci if (rc) 12468c2ecf20Sopenharmony_ci goto err_init_pci; 12478c2ecf20Sopenharmony_ci 12488c2ecf20Sopenharmony_ci rc = amd_init_dev(ndev); 12498c2ecf20Sopenharmony_ci if (rc) 12508c2ecf20Sopenharmony_ci goto err_init_dev; 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci /* write side info */ 12538c2ecf20Sopenharmony_ci amd_init_side_info(ndev); 12548c2ecf20Sopenharmony_ci 12558c2ecf20Sopenharmony_ci amd_poll_link(ndev); 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci ndev_init_debugfs(ndev); 12588c2ecf20Sopenharmony_ci 12598c2ecf20Sopenharmony_ci rc = ntb_register_device(&ndev->ntb); 12608c2ecf20Sopenharmony_ci if (rc) 12618c2ecf20Sopenharmony_ci goto err_register; 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "NTB device registered.\n"); 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci return 0; 12668c2ecf20Sopenharmony_ci 12678c2ecf20Sopenharmony_cierr_register: 12688c2ecf20Sopenharmony_ci ndev_deinit_debugfs(ndev); 12698c2ecf20Sopenharmony_ci amd_deinit_dev(ndev); 12708c2ecf20Sopenharmony_cierr_init_dev: 12718c2ecf20Sopenharmony_ci amd_ntb_deinit_pci(ndev); 12728c2ecf20Sopenharmony_cierr_init_pci: 12738c2ecf20Sopenharmony_ci kfree(ndev); 12748c2ecf20Sopenharmony_cierr_ndev: 12758c2ecf20Sopenharmony_ci return rc; 12768c2ecf20Sopenharmony_ci} 12778c2ecf20Sopenharmony_ci 12788c2ecf20Sopenharmony_cistatic void amd_ntb_pci_remove(struct pci_dev *pdev) 12798c2ecf20Sopenharmony_ci{ 12808c2ecf20Sopenharmony_ci struct amd_ntb_dev *ndev = pci_get_drvdata(pdev); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci /* 12838c2ecf20Sopenharmony_ci * Clear the READY bit in SIDEINFO register before sending DB event 12848c2ecf20Sopenharmony_ci * to the peer. This will make sure that when the peer handles the 12858c2ecf20Sopenharmony_ci * DB event, it correctly reads this bit as being 0. 12868c2ecf20Sopenharmony_ci */ 12878c2ecf20Sopenharmony_ci amd_deinit_side_info(ndev); 12888c2ecf20Sopenharmony_ci ntb_peer_db_set(&ndev->ntb, BIT_ULL(ndev->db_last_bit)); 12898c2ecf20Sopenharmony_ci ntb_unregister_device(&ndev->ntb); 12908c2ecf20Sopenharmony_ci ndev_deinit_debugfs(ndev); 12918c2ecf20Sopenharmony_ci amd_deinit_dev(ndev); 12928c2ecf20Sopenharmony_ci amd_ntb_deinit_pci(ndev); 12938c2ecf20Sopenharmony_ci kfree(ndev); 12948c2ecf20Sopenharmony_ci} 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_cistatic void amd_ntb_pci_shutdown(struct pci_dev *pdev) 12978c2ecf20Sopenharmony_ci{ 12988c2ecf20Sopenharmony_ci struct amd_ntb_dev *ndev = pci_get_drvdata(pdev); 12998c2ecf20Sopenharmony_ci 13008c2ecf20Sopenharmony_ci /* Send link down notification */ 13018c2ecf20Sopenharmony_ci ntb_link_event(&ndev->ntb); 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci amd_deinit_side_info(ndev); 13048c2ecf20Sopenharmony_ci ntb_peer_db_set(&ndev->ntb, BIT_ULL(ndev->db_last_bit)); 13058c2ecf20Sopenharmony_ci ntb_unregister_device(&ndev->ntb); 13068c2ecf20Sopenharmony_ci ndev_deinit_debugfs(ndev); 13078c2ecf20Sopenharmony_ci amd_deinit_dev(ndev); 13088c2ecf20Sopenharmony_ci amd_ntb_deinit_pci(ndev); 13098c2ecf20Sopenharmony_ci kfree(ndev); 13108c2ecf20Sopenharmony_ci} 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_cistatic const struct file_operations amd_ntb_debugfs_info = { 13138c2ecf20Sopenharmony_ci .owner = THIS_MODULE, 13148c2ecf20Sopenharmony_ci .open = simple_open, 13158c2ecf20Sopenharmony_ci .read = ndev_debugfs_read, 13168c2ecf20Sopenharmony_ci}; 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_cistatic const struct ntb_dev_data dev_data[] = { 13198c2ecf20Sopenharmony_ci { /* for device 145b */ 13208c2ecf20Sopenharmony_ci .mw_count = 3, 13218c2ecf20Sopenharmony_ci .mw_idx = 1, 13228c2ecf20Sopenharmony_ci }, 13238c2ecf20Sopenharmony_ci { /* for device 148b */ 13248c2ecf20Sopenharmony_ci .mw_count = 2, 13258c2ecf20Sopenharmony_ci .mw_idx = 2, 13268c2ecf20Sopenharmony_ci }, 13278c2ecf20Sopenharmony_ci}; 13288c2ecf20Sopenharmony_ci 13298c2ecf20Sopenharmony_cistatic const struct pci_device_id amd_ntb_pci_tbl[] = { 13308c2ecf20Sopenharmony_ci { PCI_VDEVICE(AMD, 0x145b), (kernel_ulong_t)&dev_data[0] }, 13318c2ecf20Sopenharmony_ci { PCI_VDEVICE(AMD, 0x148b), (kernel_ulong_t)&dev_data[1] }, 13328c2ecf20Sopenharmony_ci { PCI_VDEVICE(HYGON, 0x145b), (kernel_ulong_t)&dev_data[0] }, 13338c2ecf20Sopenharmony_ci { 0, } 13348c2ecf20Sopenharmony_ci}; 13358c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, amd_ntb_pci_tbl); 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_cistatic struct pci_driver amd_ntb_pci_driver = { 13388c2ecf20Sopenharmony_ci .name = KBUILD_MODNAME, 13398c2ecf20Sopenharmony_ci .id_table = amd_ntb_pci_tbl, 13408c2ecf20Sopenharmony_ci .probe = amd_ntb_pci_probe, 13418c2ecf20Sopenharmony_ci .remove = amd_ntb_pci_remove, 13428c2ecf20Sopenharmony_ci .shutdown = amd_ntb_pci_shutdown, 13438c2ecf20Sopenharmony_ci}; 13448c2ecf20Sopenharmony_ci 13458c2ecf20Sopenharmony_cistatic int __init amd_ntb_pci_driver_init(void) 13468c2ecf20Sopenharmony_ci{ 13478c2ecf20Sopenharmony_ci int ret; 13488c2ecf20Sopenharmony_ci pr_info("%s %s\n", NTB_DESC, NTB_VER); 13498c2ecf20Sopenharmony_ci 13508c2ecf20Sopenharmony_ci if (debugfs_initialized()) 13518c2ecf20Sopenharmony_ci debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, NULL); 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci ret = pci_register_driver(&amd_ntb_pci_driver); 13548c2ecf20Sopenharmony_ci if (ret) 13558c2ecf20Sopenharmony_ci debugfs_remove_recursive(debugfs_dir); 13568c2ecf20Sopenharmony_ci 13578c2ecf20Sopenharmony_ci return ret; 13588c2ecf20Sopenharmony_ci} 13598c2ecf20Sopenharmony_cimodule_init(amd_ntb_pci_driver_init); 13608c2ecf20Sopenharmony_ci 13618c2ecf20Sopenharmony_cistatic void __exit amd_ntb_pci_driver_exit(void) 13628c2ecf20Sopenharmony_ci{ 13638c2ecf20Sopenharmony_ci pci_unregister_driver(&amd_ntb_pci_driver); 13648c2ecf20Sopenharmony_ci debugfs_remove_recursive(debugfs_dir); 13658c2ecf20Sopenharmony_ci} 13668c2ecf20Sopenharmony_cimodule_exit(amd_ntb_pci_driver_exit); 1367