162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license. When using or 362306a36Sopenharmony_ci * redistributing this file, you may do so under either license. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * GPL LICENSE SUMMARY 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * Copyright (C) 2016 Advanced Micro Devices, Inc. All Rights Reserved. 862306a36Sopenharmony_ci * Copyright (C) 2016 T-Platforms. All Rights Reserved. 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 1162306a36Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as 1262306a36Sopenharmony_ci * published by the Free Software Foundation. 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * BSD LICENSE 1562306a36Sopenharmony_ci * 1662306a36Sopenharmony_ci * Copyright (C) 2016 Advanced Micro Devices, Inc. All Rights Reserved. 1762306a36Sopenharmony_ci * Copyright (C) 2016 T-Platforms. All Rights Reserved. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 2062306a36Sopenharmony_ci * modification, are permitted provided that the following conditions 2162306a36Sopenharmony_ci * are met: 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * * Redistributions of source code must retain the above copyright 2462306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 2562306a36Sopenharmony_ci * * Redistributions in binary form must reproduce the above copy 2662306a36Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 2762306a36Sopenharmony_ci * the documentation and/or other materials provided with the 2862306a36Sopenharmony_ci * distribution. 2962306a36Sopenharmony_ci * * Neither the name of AMD Corporation nor the names of its 3062306a36Sopenharmony_ci * contributors may be used to endorse or promote products derived 3162306a36Sopenharmony_ci * from this software without specific prior written permission. 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 3462306a36Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 3562306a36Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 3662306a36Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 3762306a36Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 3862306a36Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 3962306a36Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 4062306a36Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 4162306a36Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 4262306a36Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 4362306a36Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * AMD PCIe NTB Linux driver 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * Contact Information: 4862306a36Sopenharmony_ci * Xiangliang Yu <Xiangliang.Yu@amd.com> 4962306a36Sopenharmony_ci */ 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci#include <linux/debugfs.h> 5262306a36Sopenharmony_ci#include <linux/delay.h> 5362306a36Sopenharmony_ci#include <linux/init.h> 5462306a36Sopenharmony_ci#include <linux/interrupt.h> 5562306a36Sopenharmony_ci#include <linux/module.h> 5662306a36Sopenharmony_ci#include <linux/acpi.h> 5762306a36Sopenharmony_ci#include <linux/pci.h> 5862306a36Sopenharmony_ci#include <linux/random.h> 5962306a36Sopenharmony_ci#include <linux/slab.h> 6062306a36Sopenharmony_ci#include <linux/ntb.h> 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci#include "ntb_hw_amd.h" 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci#define NTB_NAME "ntb_hw_amd" 6562306a36Sopenharmony_ci#define NTB_DESC "AMD(R) PCI-E Non-Transparent Bridge Driver" 6662306a36Sopenharmony_ci#define NTB_VER "1.0" 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ciMODULE_DESCRIPTION(NTB_DESC); 6962306a36Sopenharmony_ciMODULE_VERSION(NTB_VER); 7062306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 7162306a36Sopenharmony_ciMODULE_AUTHOR("AMD Inc."); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_cistatic const struct file_operations amd_ntb_debugfs_info; 7462306a36Sopenharmony_cistatic struct dentry *debugfs_dir; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_cistatic int ndev_mw_to_bar(struct amd_ntb_dev *ndev, int idx) 7762306a36Sopenharmony_ci{ 7862306a36Sopenharmony_ci if (idx < 0 || idx > ndev->mw_count) 7962306a36Sopenharmony_ci return -EINVAL; 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci return ndev->dev_data->mw_idx << idx; 8262306a36Sopenharmony_ci} 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cistatic int amd_ntb_mw_count(struct ntb_dev *ntb, int pidx) 8562306a36Sopenharmony_ci{ 8662306a36Sopenharmony_ci if (pidx != NTB_DEF_PEER_IDX) 8762306a36Sopenharmony_ci return -EINVAL; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci return ntb_ndev(ntb)->mw_count; 9062306a36Sopenharmony_ci} 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_cistatic int amd_ntb_mw_get_align(struct ntb_dev *ntb, int pidx, int idx, 9362306a36Sopenharmony_ci resource_size_t *addr_align, 9462306a36Sopenharmony_ci resource_size_t *size_align, 9562306a36Sopenharmony_ci resource_size_t *size_max) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 9862306a36Sopenharmony_ci int bar; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (pidx != NTB_DEF_PEER_IDX) 10162306a36Sopenharmony_ci return -EINVAL; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci bar = ndev_mw_to_bar(ndev, idx); 10462306a36Sopenharmony_ci if (bar < 0) 10562306a36Sopenharmony_ci return bar; 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_ci if (addr_align) 10862306a36Sopenharmony_ci *addr_align = SZ_4K; 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_ci if (size_align) 11162306a36Sopenharmony_ci *size_align = 1; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (size_max) 11462306a36Sopenharmony_ci *size_max = pci_resource_len(ndev->ntb.pdev, bar); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic int amd_ntb_mw_set_trans(struct ntb_dev *ntb, int pidx, int idx, 12062306a36Sopenharmony_ci dma_addr_t addr, resource_size_t size) 12162306a36Sopenharmony_ci{ 12262306a36Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 12362306a36Sopenharmony_ci unsigned long xlat_reg, limit_reg = 0; 12462306a36Sopenharmony_ci resource_size_t mw_size; 12562306a36Sopenharmony_ci void __iomem *mmio, *peer_mmio; 12662306a36Sopenharmony_ci u64 base_addr, limit, reg_val; 12762306a36Sopenharmony_ci int bar; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci if (pidx != NTB_DEF_PEER_IDX) 13062306a36Sopenharmony_ci return -EINVAL; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci bar = ndev_mw_to_bar(ndev, idx); 13362306a36Sopenharmony_ci if (bar < 0) 13462306a36Sopenharmony_ci return bar; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci mw_size = pci_resource_len(ntb->pdev, bar); 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci /* make sure the range fits in the usable mw size */ 13962306a36Sopenharmony_ci if (size > mw_size) 14062306a36Sopenharmony_ci return -EINVAL; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci mmio = ndev->self_mmio; 14362306a36Sopenharmony_ci peer_mmio = ndev->peer_mmio; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci base_addr = pci_resource_start(ntb->pdev, bar); 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci if (bar != 1) { 14862306a36Sopenharmony_ci xlat_reg = AMD_BAR23XLAT_OFFSET + ((bar - 2) << 2); 14962306a36Sopenharmony_ci limit_reg = AMD_BAR23LMT_OFFSET + ((bar - 2) << 2); 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* Set the limit if supported */ 15262306a36Sopenharmony_ci limit = size; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci /* set and verify setting the translation address */ 15562306a36Sopenharmony_ci write64(addr, peer_mmio + xlat_reg); 15662306a36Sopenharmony_ci reg_val = read64(peer_mmio + xlat_reg); 15762306a36Sopenharmony_ci if (reg_val != addr) { 15862306a36Sopenharmony_ci write64(0, peer_mmio + xlat_reg); 15962306a36Sopenharmony_ci return -EIO; 16062306a36Sopenharmony_ci } 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci /* set and verify setting the limit */ 16362306a36Sopenharmony_ci write64(limit, peer_mmio + limit_reg); 16462306a36Sopenharmony_ci reg_val = read64(peer_mmio + limit_reg); 16562306a36Sopenharmony_ci if (reg_val != limit) { 16662306a36Sopenharmony_ci write64(base_addr, mmio + limit_reg); 16762306a36Sopenharmony_ci write64(0, peer_mmio + xlat_reg); 16862306a36Sopenharmony_ci return -EIO; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci } else { 17162306a36Sopenharmony_ci xlat_reg = AMD_BAR1XLAT_OFFSET; 17262306a36Sopenharmony_ci limit_reg = AMD_BAR1LMT_OFFSET; 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci /* Set the limit if supported */ 17562306a36Sopenharmony_ci limit = size; 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci /* set and verify setting the translation address */ 17862306a36Sopenharmony_ci write64(addr, peer_mmio + xlat_reg); 17962306a36Sopenharmony_ci reg_val = read64(peer_mmio + xlat_reg); 18062306a36Sopenharmony_ci if (reg_val != addr) { 18162306a36Sopenharmony_ci write64(0, peer_mmio + xlat_reg); 18262306a36Sopenharmony_ci return -EIO; 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci /* set and verify setting the limit */ 18662306a36Sopenharmony_ci writel(limit, peer_mmio + limit_reg); 18762306a36Sopenharmony_ci reg_val = readl(peer_mmio + limit_reg); 18862306a36Sopenharmony_ci if (reg_val != limit) { 18962306a36Sopenharmony_ci writel(base_addr, mmio + limit_reg); 19062306a36Sopenharmony_ci writel(0, peer_mmio + xlat_reg); 19162306a36Sopenharmony_ci return -EIO; 19262306a36Sopenharmony_ci } 19362306a36Sopenharmony_ci } 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci return 0; 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_cistatic int amd_ntb_get_link_status(struct amd_ntb_dev *ndev) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci struct pci_dev *pdev = NULL; 20162306a36Sopenharmony_ci struct pci_dev *pci_swds = NULL; 20262306a36Sopenharmony_ci struct pci_dev *pci_swus = NULL; 20362306a36Sopenharmony_ci u32 stat; 20462306a36Sopenharmony_ci int rc; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (ndev->ntb.topo == NTB_TOPO_SEC) { 20762306a36Sopenharmony_ci /* Locate the pointer to Downstream Switch for this device */ 20862306a36Sopenharmony_ci pci_swds = pci_upstream_bridge(ndev->ntb.pdev); 20962306a36Sopenharmony_ci if (pci_swds) { 21062306a36Sopenharmony_ci /* 21162306a36Sopenharmony_ci * Locate the pointer to Upstream Switch for 21262306a36Sopenharmony_ci * the Downstream Switch. 21362306a36Sopenharmony_ci */ 21462306a36Sopenharmony_ci pci_swus = pci_upstream_bridge(pci_swds); 21562306a36Sopenharmony_ci if (pci_swus) { 21662306a36Sopenharmony_ci rc = pcie_capability_read_dword(pci_swus, 21762306a36Sopenharmony_ci PCI_EXP_LNKCTL, 21862306a36Sopenharmony_ci &stat); 21962306a36Sopenharmony_ci if (rc) 22062306a36Sopenharmony_ci return 0; 22162306a36Sopenharmony_ci } else { 22262306a36Sopenharmony_ci return 0; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci } else { 22562306a36Sopenharmony_ci return 0; 22662306a36Sopenharmony_ci } 22762306a36Sopenharmony_ci } else if (ndev->ntb.topo == NTB_TOPO_PRI) { 22862306a36Sopenharmony_ci /* 22962306a36Sopenharmony_ci * For NTB primary, we simply read the Link Status and control 23062306a36Sopenharmony_ci * register of the NTB device itself. 23162306a36Sopenharmony_ci */ 23262306a36Sopenharmony_ci pdev = ndev->ntb.pdev; 23362306a36Sopenharmony_ci rc = pcie_capability_read_dword(pdev, PCI_EXP_LNKCTL, &stat); 23462306a36Sopenharmony_ci if (rc) 23562306a36Sopenharmony_ci return 0; 23662306a36Sopenharmony_ci } else { 23762306a36Sopenharmony_ci /* Catch all for everything else */ 23862306a36Sopenharmony_ci return 0; 23962306a36Sopenharmony_ci } 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci ndev->lnk_sta = stat; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci return 1; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic int amd_link_is_up(struct amd_ntb_dev *ndev) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci int ret; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci /* 25162306a36Sopenharmony_ci * We consider the link to be up under two conditions: 25262306a36Sopenharmony_ci * 25362306a36Sopenharmony_ci * - When a link-up event is received. This is indicated by 25462306a36Sopenharmony_ci * AMD_LINK_UP_EVENT set in peer_sta. 25562306a36Sopenharmony_ci * - When driver on both sides of the link have been loaded. 25662306a36Sopenharmony_ci * This is indicated by bit 1 being set in the peer 25762306a36Sopenharmony_ci * SIDEINFO register. 25862306a36Sopenharmony_ci * 25962306a36Sopenharmony_ci * This function should return 1 when the latter of the above 26062306a36Sopenharmony_ci * two conditions is true. 26162306a36Sopenharmony_ci * 26262306a36Sopenharmony_ci * Now consider the sequence of events - Link-Up event occurs, 26362306a36Sopenharmony_ci * then the peer side driver loads. In this case, we would have 26462306a36Sopenharmony_ci * received LINK_UP event and bit 1 of peer SIDEINFO is also 26562306a36Sopenharmony_ci * set. What happens now if the link goes down? Bit 1 of 26662306a36Sopenharmony_ci * peer SIDEINFO remains set, but LINK_DOWN bit is set in 26762306a36Sopenharmony_ci * peer_sta. So we should return 0 from this function. Not only 26862306a36Sopenharmony_ci * that, we clear bit 1 of peer SIDEINFO to 0, since the peer 26962306a36Sopenharmony_ci * side driver did not even get a chance to clear it before 27062306a36Sopenharmony_ci * the link went down. This can be the case of surprise link 27162306a36Sopenharmony_ci * removal. 27262306a36Sopenharmony_ci * 27362306a36Sopenharmony_ci * LINK_UP event will always occur before the peer side driver 27462306a36Sopenharmony_ci * gets loaded the very first time. So there can be a case when 27562306a36Sopenharmony_ci * the LINK_UP event has occurred, but the peer side driver hasn't 27662306a36Sopenharmony_ci * yet loaded. We return 0 in that case. 27762306a36Sopenharmony_ci * 27862306a36Sopenharmony_ci * There is also a special case when the primary side driver is 27962306a36Sopenharmony_ci * unloaded and then loaded again. Since there is no change in 28062306a36Sopenharmony_ci * the status of NTB secondary in this case, there is no Link-Up 28162306a36Sopenharmony_ci * or Link-Down notification received. We recognize this condition 28262306a36Sopenharmony_ci * with peer_sta being set to 0. 28362306a36Sopenharmony_ci * 28462306a36Sopenharmony_ci * If bit 1 of peer SIDEINFO register is not set, then we 28562306a36Sopenharmony_ci * simply return 0 irrespective of the link up or down status 28662306a36Sopenharmony_ci * set in peer_sta. 28762306a36Sopenharmony_ci */ 28862306a36Sopenharmony_ci ret = amd_poll_link(ndev); 28962306a36Sopenharmony_ci if (ret) { 29062306a36Sopenharmony_ci /* 29162306a36Sopenharmony_ci * We need to check the below only for NTB primary. For NTB 29262306a36Sopenharmony_ci * secondary, simply checking the result of PSIDE_INFO 29362306a36Sopenharmony_ci * register will suffice. 29462306a36Sopenharmony_ci */ 29562306a36Sopenharmony_ci if (ndev->ntb.topo == NTB_TOPO_PRI) { 29662306a36Sopenharmony_ci if ((ndev->peer_sta & AMD_LINK_UP_EVENT) || 29762306a36Sopenharmony_ci (ndev->peer_sta == 0)) 29862306a36Sopenharmony_ci return ret; 29962306a36Sopenharmony_ci else if (ndev->peer_sta & AMD_LINK_DOWN_EVENT) { 30062306a36Sopenharmony_ci /* Clear peer sideinfo register */ 30162306a36Sopenharmony_ci amd_clear_side_info_reg(ndev, true); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci return 0; 30462306a36Sopenharmony_ci } 30562306a36Sopenharmony_ci } else { /* NTB_TOPO_SEC */ 30662306a36Sopenharmony_ci return ret; 30762306a36Sopenharmony_ci } 30862306a36Sopenharmony_ci } 30962306a36Sopenharmony_ci 31062306a36Sopenharmony_ci return 0; 31162306a36Sopenharmony_ci} 31262306a36Sopenharmony_ci 31362306a36Sopenharmony_cistatic u64 amd_ntb_link_is_up(struct ntb_dev *ntb, 31462306a36Sopenharmony_ci enum ntb_speed *speed, 31562306a36Sopenharmony_ci enum ntb_width *width) 31662306a36Sopenharmony_ci{ 31762306a36Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 31862306a36Sopenharmony_ci int ret = 0; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci if (amd_link_is_up(ndev)) { 32162306a36Sopenharmony_ci if (speed) 32262306a36Sopenharmony_ci *speed = NTB_LNK_STA_SPEED(ndev->lnk_sta); 32362306a36Sopenharmony_ci if (width) 32462306a36Sopenharmony_ci *width = NTB_LNK_STA_WIDTH(ndev->lnk_sta); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci dev_dbg(&ntb->pdev->dev, "link is up.\n"); 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci ret = 1; 32962306a36Sopenharmony_ci } else { 33062306a36Sopenharmony_ci if (speed) 33162306a36Sopenharmony_ci *speed = NTB_SPEED_NONE; 33262306a36Sopenharmony_ci if (width) 33362306a36Sopenharmony_ci *width = NTB_WIDTH_NONE; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci dev_dbg(&ntb->pdev->dev, "link is down.\n"); 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci return ret; 33962306a36Sopenharmony_ci} 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_cistatic int amd_ntb_link_enable(struct ntb_dev *ntb, 34262306a36Sopenharmony_ci enum ntb_speed max_speed, 34362306a36Sopenharmony_ci enum ntb_width max_width) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 34662306a36Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci /* Enable event interrupt */ 34962306a36Sopenharmony_ci ndev->int_mask &= ~AMD_EVENT_INTMASK; 35062306a36Sopenharmony_ci writel(ndev->int_mask, mmio + AMD_INTMASK_OFFSET); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci if (ndev->ntb.topo == NTB_TOPO_SEC) 35362306a36Sopenharmony_ci return -EINVAL; 35462306a36Sopenharmony_ci dev_dbg(&ntb->pdev->dev, "Enabling Link.\n"); 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci return 0; 35762306a36Sopenharmony_ci} 35862306a36Sopenharmony_ci 35962306a36Sopenharmony_cistatic int amd_ntb_link_disable(struct ntb_dev *ntb) 36062306a36Sopenharmony_ci{ 36162306a36Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 36262306a36Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* Disable event interrupt */ 36562306a36Sopenharmony_ci ndev->int_mask |= AMD_EVENT_INTMASK; 36662306a36Sopenharmony_ci writel(ndev->int_mask, mmio + AMD_INTMASK_OFFSET); 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci if (ndev->ntb.topo == NTB_TOPO_SEC) 36962306a36Sopenharmony_ci return -EINVAL; 37062306a36Sopenharmony_ci dev_dbg(&ntb->pdev->dev, "Enabling Link.\n"); 37162306a36Sopenharmony_ci 37262306a36Sopenharmony_ci return 0; 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_cistatic int amd_ntb_peer_mw_count(struct ntb_dev *ntb) 37662306a36Sopenharmony_ci{ 37762306a36Sopenharmony_ci /* The same as for inbound MWs */ 37862306a36Sopenharmony_ci return ntb_ndev(ntb)->mw_count; 37962306a36Sopenharmony_ci} 38062306a36Sopenharmony_ci 38162306a36Sopenharmony_cistatic int amd_ntb_peer_mw_get_addr(struct ntb_dev *ntb, int idx, 38262306a36Sopenharmony_ci phys_addr_t *base, resource_size_t *size) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 38562306a36Sopenharmony_ci int bar; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci bar = ndev_mw_to_bar(ndev, idx); 38862306a36Sopenharmony_ci if (bar < 0) 38962306a36Sopenharmony_ci return bar; 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci if (base) 39262306a36Sopenharmony_ci *base = pci_resource_start(ndev->ntb.pdev, bar); 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci if (size) 39562306a36Sopenharmony_ci *size = pci_resource_len(ndev->ntb.pdev, bar); 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci return 0; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic u64 amd_ntb_db_valid_mask(struct ntb_dev *ntb) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci return ntb_ndev(ntb)->db_valid_mask; 40362306a36Sopenharmony_ci} 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_cistatic int amd_ntb_db_vector_count(struct ntb_dev *ntb) 40662306a36Sopenharmony_ci{ 40762306a36Sopenharmony_ci return ntb_ndev(ntb)->db_count; 40862306a36Sopenharmony_ci} 40962306a36Sopenharmony_ci 41062306a36Sopenharmony_cistatic u64 amd_ntb_db_vector_mask(struct ntb_dev *ntb, int db_vector) 41162306a36Sopenharmony_ci{ 41262306a36Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci if (db_vector < 0 || db_vector > ndev->db_count) 41562306a36Sopenharmony_ci return 0; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_ci return ntb_ndev(ntb)->db_valid_mask & (1ULL << db_vector); 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_cistatic u64 amd_ntb_db_read(struct ntb_dev *ntb) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 42362306a36Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci return (u64)readw(mmio + AMD_DBSTAT_OFFSET); 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic int amd_ntb_db_clear(struct ntb_dev *ntb, u64 db_bits) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 43162306a36Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 43262306a36Sopenharmony_ci 43362306a36Sopenharmony_ci writew((u16)db_bits, mmio + AMD_DBSTAT_OFFSET); 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci return 0; 43662306a36Sopenharmony_ci} 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_cistatic int amd_ntb_db_set_mask(struct ntb_dev *ntb, u64 db_bits) 43962306a36Sopenharmony_ci{ 44062306a36Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 44162306a36Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 44262306a36Sopenharmony_ci unsigned long flags; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci if (db_bits & ~ndev->db_valid_mask) 44562306a36Sopenharmony_ci return -EINVAL; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci spin_lock_irqsave(&ndev->db_mask_lock, flags); 44862306a36Sopenharmony_ci ndev->db_mask |= db_bits; 44962306a36Sopenharmony_ci writew((u16)ndev->db_mask, mmio + AMD_DBMASK_OFFSET); 45062306a36Sopenharmony_ci spin_unlock_irqrestore(&ndev->db_mask_lock, flags); 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_ci return 0; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic int amd_ntb_db_clear_mask(struct ntb_dev *ntb, u64 db_bits) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 45862306a36Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 45962306a36Sopenharmony_ci unsigned long flags; 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci if (db_bits & ~ndev->db_valid_mask) 46262306a36Sopenharmony_ci return -EINVAL; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci spin_lock_irqsave(&ndev->db_mask_lock, flags); 46562306a36Sopenharmony_ci ndev->db_mask &= ~db_bits; 46662306a36Sopenharmony_ci writew((u16)ndev->db_mask, mmio + AMD_DBMASK_OFFSET); 46762306a36Sopenharmony_ci spin_unlock_irqrestore(&ndev->db_mask_lock, flags); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci return 0; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic int amd_ntb_peer_db_set(struct ntb_dev *ntb, u64 db_bits) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 47562306a36Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 47662306a36Sopenharmony_ci 47762306a36Sopenharmony_ci writew((u16)db_bits, mmio + AMD_DBREQ_OFFSET); 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci return 0; 48062306a36Sopenharmony_ci} 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_cistatic int amd_ntb_spad_count(struct ntb_dev *ntb) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci return ntb_ndev(ntb)->spad_count; 48562306a36Sopenharmony_ci} 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_cistatic u32 amd_ntb_spad_read(struct ntb_dev *ntb, int idx) 48862306a36Sopenharmony_ci{ 48962306a36Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 49062306a36Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 49162306a36Sopenharmony_ci u32 offset; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci if (idx < 0 || idx >= ndev->spad_count) 49462306a36Sopenharmony_ci return 0; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci offset = ndev->self_spad + (idx << 2); 49762306a36Sopenharmony_ci return readl(mmio + AMD_SPAD_OFFSET + offset); 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_cistatic int amd_ntb_spad_write(struct ntb_dev *ntb, 50162306a36Sopenharmony_ci int idx, u32 val) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 50462306a36Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 50562306a36Sopenharmony_ci u32 offset; 50662306a36Sopenharmony_ci 50762306a36Sopenharmony_ci if (idx < 0 || idx >= ndev->spad_count) 50862306a36Sopenharmony_ci return -EINVAL; 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_ci offset = ndev->self_spad + (idx << 2); 51162306a36Sopenharmony_ci writel(val, mmio + AMD_SPAD_OFFSET + offset); 51262306a36Sopenharmony_ci 51362306a36Sopenharmony_ci return 0; 51462306a36Sopenharmony_ci} 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_cistatic u32 amd_ntb_peer_spad_read(struct ntb_dev *ntb, int pidx, int sidx) 51762306a36Sopenharmony_ci{ 51862306a36Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 51962306a36Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 52062306a36Sopenharmony_ci u32 offset; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (sidx < 0 || sidx >= ndev->spad_count) 52362306a36Sopenharmony_ci return -EINVAL; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci offset = ndev->peer_spad + (sidx << 2); 52662306a36Sopenharmony_ci return readl(mmio + AMD_SPAD_OFFSET + offset); 52762306a36Sopenharmony_ci} 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_cistatic int amd_ntb_peer_spad_write(struct ntb_dev *ntb, int pidx, 53062306a36Sopenharmony_ci int sidx, u32 val) 53162306a36Sopenharmony_ci{ 53262306a36Sopenharmony_ci struct amd_ntb_dev *ndev = ntb_ndev(ntb); 53362306a36Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 53462306a36Sopenharmony_ci u32 offset; 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci if (sidx < 0 || sidx >= ndev->spad_count) 53762306a36Sopenharmony_ci return -EINVAL; 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci offset = ndev->peer_spad + (sidx << 2); 54062306a36Sopenharmony_ci writel(val, mmio + AMD_SPAD_OFFSET + offset); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci return 0; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_cistatic const struct ntb_dev_ops amd_ntb_ops = { 54662306a36Sopenharmony_ci .mw_count = amd_ntb_mw_count, 54762306a36Sopenharmony_ci .mw_get_align = amd_ntb_mw_get_align, 54862306a36Sopenharmony_ci .mw_set_trans = amd_ntb_mw_set_trans, 54962306a36Sopenharmony_ci .peer_mw_count = amd_ntb_peer_mw_count, 55062306a36Sopenharmony_ci .peer_mw_get_addr = amd_ntb_peer_mw_get_addr, 55162306a36Sopenharmony_ci .link_is_up = amd_ntb_link_is_up, 55262306a36Sopenharmony_ci .link_enable = amd_ntb_link_enable, 55362306a36Sopenharmony_ci .link_disable = amd_ntb_link_disable, 55462306a36Sopenharmony_ci .db_valid_mask = amd_ntb_db_valid_mask, 55562306a36Sopenharmony_ci .db_vector_count = amd_ntb_db_vector_count, 55662306a36Sopenharmony_ci .db_vector_mask = amd_ntb_db_vector_mask, 55762306a36Sopenharmony_ci .db_read = amd_ntb_db_read, 55862306a36Sopenharmony_ci .db_clear = amd_ntb_db_clear, 55962306a36Sopenharmony_ci .db_set_mask = amd_ntb_db_set_mask, 56062306a36Sopenharmony_ci .db_clear_mask = amd_ntb_db_clear_mask, 56162306a36Sopenharmony_ci .peer_db_set = amd_ntb_peer_db_set, 56262306a36Sopenharmony_ci .spad_count = amd_ntb_spad_count, 56362306a36Sopenharmony_ci .spad_read = amd_ntb_spad_read, 56462306a36Sopenharmony_ci .spad_write = amd_ntb_spad_write, 56562306a36Sopenharmony_ci .peer_spad_read = amd_ntb_peer_spad_read, 56662306a36Sopenharmony_ci .peer_spad_write = amd_ntb_peer_spad_write, 56762306a36Sopenharmony_ci}; 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_cistatic void amd_ack_smu(struct amd_ntb_dev *ndev, u32 bit) 57062306a36Sopenharmony_ci{ 57162306a36Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 57262306a36Sopenharmony_ci int reg; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci reg = readl(mmio + AMD_SMUACK_OFFSET); 57562306a36Sopenharmony_ci reg |= bit; 57662306a36Sopenharmony_ci writel(reg, mmio + AMD_SMUACK_OFFSET); 57762306a36Sopenharmony_ci} 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_cistatic void amd_handle_event(struct amd_ntb_dev *ndev, int vec) 58062306a36Sopenharmony_ci{ 58162306a36Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 58262306a36Sopenharmony_ci struct device *dev = &ndev->ntb.pdev->dev; 58362306a36Sopenharmony_ci u32 status; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci status = readl(mmio + AMD_INTSTAT_OFFSET); 58662306a36Sopenharmony_ci if (!(status & AMD_EVENT_INTMASK)) 58762306a36Sopenharmony_ci return; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci dev_dbg(dev, "status = 0x%x and vec = %d\n", status, vec); 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci status &= AMD_EVENT_INTMASK; 59262306a36Sopenharmony_ci switch (status) { 59362306a36Sopenharmony_ci case AMD_PEER_FLUSH_EVENT: 59462306a36Sopenharmony_ci ndev->peer_sta |= AMD_PEER_FLUSH_EVENT; 59562306a36Sopenharmony_ci dev_info(dev, "Flush is done.\n"); 59662306a36Sopenharmony_ci break; 59762306a36Sopenharmony_ci case AMD_PEER_RESET_EVENT: 59862306a36Sopenharmony_ci case AMD_LINK_DOWN_EVENT: 59962306a36Sopenharmony_ci ndev->peer_sta |= status; 60062306a36Sopenharmony_ci if (status == AMD_LINK_DOWN_EVENT) 60162306a36Sopenharmony_ci ndev->peer_sta &= ~AMD_LINK_UP_EVENT; 60262306a36Sopenharmony_ci 60362306a36Sopenharmony_ci amd_ack_smu(ndev, status); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci /* link down first */ 60662306a36Sopenharmony_ci ntb_link_event(&ndev->ntb); 60762306a36Sopenharmony_ci /* polling peer status */ 60862306a36Sopenharmony_ci schedule_delayed_work(&ndev->hb_timer, AMD_LINK_HB_TIMEOUT); 60962306a36Sopenharmony_ci 61062306a36Sopenharmony_ci break; 61162306a36Sopenharmony_ci case AMD_PEER_D3_EVENT: 61262306a36Sopenharmony_ci case AMD_PEER_PMETO_EVENT: 61362306a36Sopenharmony_ci case AMD_LINK_UP_EVENT: 61462306a36Sopenharmony_ci ndev->peer_sta |= status; 61562306a36Sopenharmony_ci if (status == AMD_LINK_UP_EVENT) 61662306a36Sopenharmony_ci ndev->peer_sta &= ~AMD_LINK_DOWN_EVENT; 61762306a36Sopenharmony_ci else if (status == AMD_PEER_D3_EVENT) 61862306a36Sopenharmony_ci ndev->peer_sta &= ~AMD_PEER_D0_EVENT; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_ci amd_ack_smu(ndev, status); 62162306a36Sopenharmony_ci 62262306a36Sopenharmony_ci /* link down */ 62362306a36Sopenharmony_ci ntb_link_event(&ndev->ntb); 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci break; 62662306a36Sopenharmony_ci case AMD_PEER_D0_EVENT: 62762306a36Sopenharmony_ci mmio = ndev->peer_mmio; 62862306a36Sopenharmony_ci status = readl(mmio + AMD_PMESTAT_OFFSET); 62962306a36Sopenharmony_ci /* check if this is WAKEUP event */ 63062306a36Sopenharmony_ci if (status & 0x1) 63162306a36Sopenharmony_ci dev_info(dev, "Wakeup is done.\n"); 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci ndev->peer_sta |= AMD_PEER_D0_EVENT; 63462306a36Sopenharmony_ci ndev->peer_sta &= ~AMD_PEER_D3_EVENT; 63562306a36Sopenharmony_ci amd_ack_smu(ndev, AMD_PEER_D0_EVENT); 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci /* start a timer to poll link status */ 63862306a36Sopenharmony_ci schedule_delayed_work(&ndev->hb_timer, 63962306a36Sopenharmony_ci AMD_LINK_HB_TIMEOUT); 64062306a36Sopenharmony_ci break; 64162306a36Sopenharmony_ci default: 64262306a36Sopenharmony_ci dev_info(dev, "event status = 0x%x.\n", status); 64362306a36Sopenharmony_ci break; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci /* Clear the interrupt status */ 64762306a36Sopenharmony_ci writel(status, mmio + AMD_INTSTAT_OFFSET); 64862306a36Sopenharmony_ci} 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_cistatic void amd_handle_db_event(struct amd_ntb_dev *ndev, int vec) 65162306a36Sopenharmony_ci{ 65262306a36Sopenharmony_ci struct device *dev = &ndev->ntb.pdev->dev; 65362306a36Sopenharmony_ci u64 status; 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_ci status = amd_ntb_db_read(&ndev->ntb); 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_ci dev_dbg(dev, "status = 0x%llx and vec = %d\n", status, vec); 65862306a36Sopenharmony_ci 65962306a36Sopenharmony_ci /* 66062306a36Sopenharmony_ci * Since we had reserved highest order bit of DB for signaling peer of 66162306a36Sopenharmony_ci * a special event, this is the only status bit we should be concerned 66262306a36Sopenharmony_ci * here now. 66362306a36Sopenharmony_ci */ 66462306a36Sopenharmony_ci if (status & BIT(ndev->db_last_bit)) { 66562306a36Sopenharmony_ci ntb_db_clear(&ndev->ntb, BIT(ndev->db_last_bit)); 66662306a36Sopenharmony_ci /* send link down event notification */ 66762306a36Sopenharmony_ci ntb_link_event(&ndev->ntb); 66862306a36Sopenharmony_ci 66962306a36Sopenharmony_ci /* 67062306a36Sopenharmony_ci * If we are here, that means the peer has signalled a special 67162306a36Sopenharmony_ci * event which notifies that the peer driver has been 67262306a36Sopenharmony_ci * un-loaded for some reason. Since there is a chance that the 67362306a36Sopenharmony_ci * peer will load its driver again sometime, we schedule link 67462306a36Sopenharmony_ci * polling routine. 67562306a36Sopenharmony_ci */ 67662306a36Sopenharmony_ci schedule_delayed_work(&ndev->hb_timer, AMD_LINK_HB_TIMEOUT); 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cistatic irqreturn_t ndev_interrupt(struct amd_ntb_dev *ndev, int vec) 68162306a36Sopenharmony_ci{ 68262306a36Sopenharmony_ci dev_dbg(&ndev->ntb.pdev->dev, "vec %d\n", vec); 68362306a36Sopenharmony_ci 68462306a36Sopenharmony_ci if (vec > (AMD_DB_CNT - 1) || (ndev->msix_vec_count == 1)) 68562306a36Sopenharmony_ci amd_handle_event(ndev, vec); 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (vec < AMD_DB_CNT) { 68862306a36Sopenharmony_ci amd_handle_db_event(ndev, vec); 68962306a36Sopenharmony_ci ntb_db_event(&ndev->ntb, vec); 69062306a36Sopenharmony_ci } 69162306a36Sopenharmony_ci 69262306a36Sopenharmony_ci return IRQ_HANDLED; 69362306a36Sopenharmony_ci} 69462306a36Sopenharmony_ci 69562306a36Sopenharmony_cistatic irqreturn_t ndev_vec_isr(int irq, void *dev) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci struct amd_ntb_vec *nvec = dev; 69862306a36Sopenharmony_ci 69962306a36Sopenharmony_ci return ndev_interrupt(nvec->ndev, nvec->num); 70062306a36Sopenharmony_ci} 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_cistatic irqreturn_t ndev_irq_isr(int irq, void *dev) 70362306a36Sopenharmony_ci{ 70462306a36Sopenharmony_ci struct amd_ntb_dev *ndev = dev; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_ci return ndev_interrupt(ndev, irq - ndev->ntb.pdev->irq); 70762306a36Sopenharmony_ci} 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_cistatic int ndev_init_isr(struct amd_ntb_dev *ndev, 71062306a36Sopenharmony_ci int msix_min, int msix_max) 71162306a36Sopenharmony_ci{ 71262306a36Sopenharmony_ci struct pci_dev *pdev; 71362306a36Sopenharmony_ci int rc, i, msix_count, node; 71462306a36Sopenharmony_ci 71562306a36Sopenharmony_ci pdev = ndev->ntb.pdev; 71662306a36Sopenharmony_ci 71762306a36Sopenharmony_ci node = dev_to_node(&pdev->dev); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci ndev->db_mask = ndev->db_valid_mask; 72062306a36Sopenharmony_ci 72162306a36Sopenharmony_ci /* Try to set up msix irq */ 72262306a36Sopenharmony_ci ndev->vec = kcalloc_node(msix_max, sizeof(*ndev->vec), 72362306a36Sopenharmony_ci GFP_KERNEL, node); 72462306a36Sopenharmony_ci if (!ndev->vec) 72562306a36Sopenharmony_ci goto err_msix_vec_alloc; 72662306a36Sopenharmony_ci 72762306a36Sopenharmony_ci ndev->msix = kcalloc_node(msix_max, sizeof(*ndev->msix), 72862306a36Sopenharmony_ci GFP_KERNEL, node); 72962306a36Sopenharmony_ci if (!ndev->msix) 73062306a36Sopenharmony_ci goto err_msix_alloc; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci for (i = 0; i < msix_max; ++i) 73362306a36Sopenharmony_ci ndev->msix[i].entry = i; 73462306a36Sopenharmony_ci 73562306a36Sopenharmony_ci msix_count = pci_enable_msix_range(pdev, ndev->msix, 73662306a36Sopenharmony_ci msix_min, msix_max); 73762306a36Sopenharmony_ci if (msix_count < 0) 73862306a36Sopenharmony_ci goto err_msix_enable; 73962306a36Sopenharmony_ci 74062306a36Sopenharmony_ci /* NOTE: Disable MSIX if msix count is less than 16 because of 74162306a36Sopenharmony_ci * hardware limitation. 74262306a36Sopenharmony_ci */ 74362306a36Sopenharmony_ci if (msix_count < msix_min) { 74462306a36Sopenharmony_ci pci_disable_msix(pdev); 74562306a36Sopenharmony_ci goto err_msix_enable; 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci 74862306a36Sopenharmony_ci for (i = 0; i < msix_count; ++i) { 74962306a36Sopenharmony_ci ndev->vec[i].ndev = ndev; 75062306a36Sopenharmony_ci ndev->vec[i].num = i; 75162306a36Sopenharmony_ci rc = request_irq(ndev->msix[i].vector, ndev_vec_isr, 0, 75262306a36Sopenharmony_ci "ndev_vec_isr", &ndev->vec[i]); 75362306a36Sopenharmony_ci if (rc) 75462306a36Sopenharmony_ci goto err_msix_request; 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Using msix interrupts\n"); 75862306a36Sopenharmony_ci ndev->db_count = msix_min; 75962306a36Sopenharmony_ci ndev->msix_vec_count = msix_max; 76062306a36Sopenharmony_ci return 0; 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_cierr_msix_request: 76362306a36Sopenharmony_ci while (i-- > 0) 76462306a36Sopenharmony_ci free_irq(ndev->msix[i].vector, &ndev->vec[i]); 76562306a36Sopenharmony_ci pci_disable_msix(pdev); 76662306a36Sopenharmony_cierr_msix_enable: 76762306a36Sopenharmony_ci kfree(ndev->msix); 76862306a36Sopenharmony_cierr_msix_alloc: 76962306a36Sopenharmony_ci kfree(ndev->vec); 77062306a36Sopenharmony_cierr_msix_vec_alloc: 77162306a36Sopenharmony_ci ndev->msix = NULL; 77262306a36Sopenharmony_ci ndev->vec = NULL; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci /* Try to set up msi irq */ 77562306a36Sopenharmony_ci rc = pci_enable_msi(pdev); 77662306a36Sopenharmony_ci if (rc) 77762306a36Sopenharmony_ci goto err_msi_enable; 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci rc = request_irq(pdev->irq, ndev_irq_isr, 0, 78062306a36Sopenharmony_ci "ndev_irq_isr", ndev); 78162306a36Sopenharmony_ci if (rc) 78262306a36Sopenharmony_ci goto err_msi_request; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Using msi interrupts\n"); 78562306a36Sopenharmony_ci ndev->db_count = 1; 78662306a36Sopenharmony_ci ndev->msix_vec_count = 1; 78762306a36Sopenharmony_ci return 0; 78862306a36Sopenharmony_ci 78962306a36Sopenharmony_cierr_msi_request: 79062306a36Sopenharmony_ci pci_disable_msi(pdev); 79162306a36Sopenharmony_cierr_msi_enable: 79262306a36Sopenharmony_ci 79362306a36Sopenharmony_ci /* Try to set up intx irq */ 79462306a36Sopenharmony_ci pci_intx(pdev, 1); 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci rc = request_irq(pdev->irq, ndev_irq_isr, IRQF_SHARED, 79762306a36Sopenharmony_ci "ndev_irq_isr", ndev); 79862306a36Sopenharmony_ci if (rc) 79962306a36Sopenharmony_ci goto err_intx_request; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Using intx interrupts\n"); 80262306a36Sopenharmony_ci ndev->db_count = 1; 80362306a36Sopenharmony_ci ndev->msix_vec_count = 1; 80462306a36Sopenharmony_ci return 0; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_cierr_intx_request: 80762306a36Sopenharmony_ci return rc; 80862306a36Sopenharmony_ci} 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_cistatic void ndev_deinit_isr(struct amd_ntb_dev *ndev) 81162306a36Sopenharmony_ci{ 81262306a36Sopenharmony_ci struct pci_dev *pdev; 81362306a36Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 81462306a36Sopenharmony_ci int i; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci pdev = ndev->ntb.pdev; 81762306a36Sopenharmony_ci 81862306a36Sopenharmony_ci /* Mask all doorbell interrupts */ 81962306a36Sopenharmony_ci ndev->db_mask = ndev->db_valid_mask; 82062306a36Sopenharmony_ci writel(ndev->db_mask, mmio + AMD_DBMASK_OFFSET); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci if (ndev->msix) { 82362306a36Sopenharmony_ci i = ndev->msix_vec_count; 82462306a36Sopenharmony_ci while (i--) 82562306a36Sopenharmony_ci free_irq(ndev->msix[i].vector, &ndev->vec[i]); 82662306a36Sopenharmony_ci pci_disable_msix(pdev); 82762306a36Sopenharmony_ci kfree(ndev->msix); 82862306a36Sopenharmony_ci kfree(ndev->vec); 82962306a36Sopenharmony_ci } else { 83062306a36Sopenharmony_ci free_irq(pdev->irq, ndev); 83162306a36Sopenharmony_ci if (pci_dev_msi_enabled(pdev)) 83262306a36Sopenharmony_ci pci_disable_msi(pdev); 83362306a36Sopenharmony_ci else 83462306a36Sopenharmony_ci pci_intx(pdev, 0); 83562306a36Sopenharmony_ci } 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_cistatic ssize_t ndev_debugfs_read(struct file *filp, char __user *ubuf, 83962306a36Sopenharmony_ci size_t count, loff_t *offp) 84062306a36Sopenharmony_ci{ 84162306a36Sopenharmony_ci struct amd_ntb_dev *ndev; 84262306a36Sopenharmony_ci void __iomem *mmio; 84362306a36Sopenharmony_ci char *buf; 84462306a36Sopenharmony_ci size_t buf_size; 84562306a36Sopenharmony_ci ssize_t ret, off; 84662306a36Sopenharmony_ci union { u64 v64; u32 v32; u16 v16; } u; 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci ndev = filp->private_data; 84962306a36Sopenharmony_ci mmio = ndev->self_mmio; 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci buf_size = min(count, 0x800ul); 85262306a36Sopenharmony_ci 85362306a36Sopenharmony_ci buf = kmalloc(buf_size, GFP_KERNEL); 85462306a36Sopenharmony_ci if (!buf) 85562306a36Sopenharmony_ci return -ENOMEM; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci off = 0; 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 86062306a36Sopenharmony_ci "NTB Device Information:\n"); 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 86362306a36Sopenharmony_ci "Connection Topology -\t%s\n", 86462306a36Sopenharmony_ci ntb_topo_string(ndev->ntb.topo)); 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 86762306a36Sopenharmony_ci "LNK STA -\t\t%#06x\n", ndev->lnk_sta); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci if (!amd_link_is_up(ndev)) { 87062306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 87162306a36Sopenharmony_ci "Link Status -\t\tDown\n"); 87262306a36Sopenharmony_ci } else { 87362306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 87462306a36Sopenharmony_ci "Link Status -\t\tUp\n"); 87562306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 87662306a36Sopenharmony_ci "Link Speed -\t\tPCI-E Gen %u\n", 87762306a36Sopenharmony_ci NTB_LNK_STA_SPEED(ndev->lnk_sta)); 87862306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 87962306a36Sopenharmony_ci "Link Width -\t\tx%u\n", 88062306a36Sopenharmony_ci NTB_LNK_STA_WIDTH(ndev->lnk_sta)); 88162306a36Sopenharmony_ci } 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 88462306a36Sopenharmony_ci "Memory Window Count -\t%u\n", ndev->mw_count); 88562306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 88662306a36Sopenharmony_ci "Scratchpad Count -\t%u\n", ndev->spad_count); 88762306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 88862306a36Sopenharmony_ci "Doorbell Count -\t%u\n", ndev->db_count); 88962306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 89062306a36Sopenharmony_ci "MSIX Vector Count -\t%u\n", ndev->msix_vec_count); 89162306a36Sopenharmony_ci 89262306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 89362306a36Sopenharmony_ci "Doorbell Valid Mask -\t%#llx\n", ndev->db_valid_mask); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci u.v32 = readl(ndev->self_mmio + AMD_DBMASK_OFFSET); 89662306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 89762306a36Sopenharmony_ci "Doorbell Mask -\t\t\t%#06x\n", u.v32); 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_ci u.v32 = readl(mmio + AMD_DBSTAT_OFFSET); 90062306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 90162306a36Sopenharmony_ci "Doorbell Bell -\t\t\t%#06x\n", u.v32); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 90462306a36Sopenharmony_ci "\nNTB Incoming XLAT:\n"); 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci u.v64 = read64(mmio + AMD_BAR1XLAT_OFFSET); 90762306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 90862306a36Sopenharmony_ci "XLAT1 -\t\t%#018llx\n", u.v64); 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci u.v64 = read64(ndev->self_mmio + AMD_BAR23XLAT_OFFSET); 91162306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 91262306a36Sopenharmony_ci "XLAT23 -\t\t%#018llx\n", u.v64); 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci u.v64 = read64(ndev->self_mmio + AMD_BAR45XLAT_OFFSET); 91562306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 91662306a36Sopenharmony_ci "XLAT45 -\t\t%#018llx\n", u.v64); 91762306a36Sopenharmony_ci 91862306a36Sopenharmony_ci u.v32 = readl(mmio + AMD_BAR1LMT_OFFSET); 91962306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 92062306a36Sopenharmony_ci "LMT1 -\t\t\t%#06x\n", u.v32); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci u.v64 = read64(ndev->self_mmio + AMD_BAR23LMT_OFFSET); 92362306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 92462306a36Sopenharmony_ci "LMT23 -\t\t\t%#018llx\n", u.v64); 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci u.v64 = read64(ndev->self_mmio + AMD_BAR45LMT_OFFSET); 92762306a36Sopenharmony_ci off += scnprintf(buf + off, buf_size - off, 92862306a36Sopenharmony_ci "LMT45 -\t\t\t%#018llx\n", u.v64); 92962306a36Sopenharmony_ci 93062306a36Sopenharmony_ci ret = simple_read_from_buffer(ubuf, count, offp, buf, off); 93162306a36Sopenharmony_ci kfree(buf); 93262306a36Sopenharmony_ci return ret; 93362306a36Sopenharmony_ci} 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_cistatic void ndev_init_debugfs(struct amd_ntb_dev *ndev) 93662306a36Sopenharmony_ci{ 93762306a36Sopenharmony_ci if (!debugfs_dir) { 93862306a36Sopenharmony_ci ndev->debugfs_dir = NULL; 93962306a36Sopenharmony_ci ndev->debugfs_info = NULL; 94062306a36Sopenharmony_ci } else { 94162306a36Sopenharmony_ci ndev->debugfs_dir = 94262306a36Sopenharmony_ci debugfs_create_dir(pci_name(ndev->ntb.pdev), 94362306a36Sopenharmony_ci debugfs_dir); 94462306a36Sopenharmony_ci ndev->debugfs_info = 94562306a36Sopenharmony_ci debugfs_create_file("info", S_IRUSR, 94662306a36Sopenharmony_ci ndev->debugfs_dir, ndev, 94762306a36Sopenharmony_ci &amd_ntb_debugfs_info); 94862306a36Sopenharmony_ci } 94962306a36Sopenharmony_ci} 95062306a36Sopenharmony_ci 95162306a36Sopenharmony_cistatic void ndev_deinit_debugfs(struct amd_ntb_dev *ndev) 95262306a36Sopenharmony_ci{ 95362306a36Sopenharmony_ci debugfs_remove_recursive(ndev->debugfs_dir); 95462306a36Sopenharmony_ci} 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_cistatic inline void ndev_init_struct(struct amd_ntb_dev *ndev, 95762306a36Sopenharmony_ci struct pci_dev *pdev) 95862306a36Sopenharmony_ci{ 95962306a36Sopenharmony_ci ndev->ntb.pdev = pdev; 96062306a36Sopenharmony_ci ndev->ntb.topo = NTB_TOPO_NONE; 96162306a36Sopenharmony_ci ndev->ntb.ops = &amd_ntb_ops; 96262306a36Sopenharmony_ci ndev->int_mask = AMD_EVENT_INTMASK; 96362306a36Sopenharmony_ci spin_lock_init(&ndev->db_mask_lock); 96462306a36Sopenharmony_ci} 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_cistatic int amd_poll_link(struct amd_ntb_dev *ndev) 96762306a36Sopenharmony_ci{ 96862306a36Sopenharmony_ci void __iomem *mmio = ndev->peer_mmio; 96962306a36Sopenharmony_ci u32 reg; 97062306a36Sopenharmony_ci 97162306a36Sopenharmony_ci reg = readl(mmio + AMD_SIDEINFO_OFFSET); 97262306a36Sopenharmony_ci reg &= AMD_SIDE_READY; 97362306a36Sopenharmony_ci 97462306a36Sopenharmony_ci dev_dbg(&ndev->ntb.pdev->dev, "%s: reg_val = 0x%x.\n", __func__, reg); 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci ndev->cntl_sta = reg; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci amd_ntb_get_link_status(ndev); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci return ndev->cntl_sta; 98162306a36Sopenharmony_ci} 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_cistatic void amd_link_hb(struct work_struct *work) 98462306a36Sopenharmony_ci{ 98562306a36Sopenharmony_ci struct amd_ntb_dev *ndev = hb_ndev(work); 98662306a36Sopenharmony_ci 98762306a36Sopenharmony_ci if (amd_poll_link(ndev)) 98862306a36Sopenharmony_ci ntb_link_event(&ndev->ntb); 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci if (!amd_link_is_up(ndev)) 99162306a36Sopenharmony_ci schedule_delayed_work(&ndev->hb_timer, AMD_LINK_HB_TIMEOUT); 99262306a36Sopenharmony_ci} 99362306a36Sopenharmony_ci 99462306a36Sopenharmony_cistatic int amd_init_isr(struct amd_ntb_dev *ndev) 99562306a36Sopenharmony_ci{ 99662306a36Sopenharmony_ci return ndev_init_isr(ndev, AMD_DB_CNT, AMD_MSIX_VECTOR_CNT); 99762306a36Sopenharmony_ci} 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_cistatic void amd_set_side_info_reg(struct amd_ntb_dev *ndev, bool peer) 100062306a36Sopenharmony_ci{ 100162306a36Sopenharmony_ci void __iomem *mmio = NULL; 100262306a36Sopenharmony_ci unsigned int reg; 100362306a36Sopenharmony_ci 100462306a36Sopenharmony_ci if (peer) 100562306a36Sopenharmony_ci mmio = ndev->peer_mmio; 100662306a36Sopenharmony_ci else 100762306a36Sopenharmony_ci mmio = ndev->self_mmio; 100862306a36Sopenharmony_ci 100962306a36Sopenharmony_ci reg = readl(mmio + AMD_SIDEINFO_OFFSET); 101062306a36Sopenharmony_ci if (!(reg & AMD_SIDE_READY)) { 101162306a36Sopenharmony_ci reg |= AMD_SIDE_READY; 101262306a36Sopenharmony_ci writel(reg, mmio + AMD_SIDEINFO_OFFSET); 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci} 101562306a36Sopenharmony_ci 101662306a36Sopenharmony_cistatic void amd_clear_side_info_reg(struct amd_ntb_dev *ndev, bool peer) 101762306a36Sopenharmony_ci{ 101862306a36Sopenharmony_ci void __iomem *mmio = NULL; 101962306a36Sopenharmony_ci unsigned int reg; 102062306a36Sopenharmony_ci 102162306a36Sopenharmony_ci if (peer) 102262306a36Sopenharmony_ci mmio = ndev->peer_mmio; 102362306a36Sopenharmony_ci else 102462306a36Sopenharmony_ci mmio = ndev->self_mmio; 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci reg = readl(mmio + AMD_SIDEINFO_OFFSET); 102762306a36Sopenharmony_ci if (reg & AMD_SIDE_READY) { 102862306a36Sopenharmony_ci reg &= ~AMD_SIDE_READY; 102962306a36Sopenharmony_ci writel(reg, mmio + AMD_SIDEINFO_OFFSET); 103062306a36Sopenharmony_ci readl(mmio + AMD_SIDEINFO_OFFSET); 103162306a36Sopenharmony_ci } 103262306a36Sopenharmony_ci} 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_cistatic void amd_init_side_info(struct amd_ntb_dev *ndev) 103562306a36Sopenharmony_ci{ 103662306a36Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 103762306a36Sopenharmony_ci u32 ntb_ctl; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci amd_set_side_info_reg(ndev, false); 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_ci ntb_ctl = readl(mmio + AMD_CNTL_OFFSET); 104262306a36Sopenharmony_ci ntb_ctl |= (PMM_REG_CTL | SMM_REG_CTL); 104362306a36Sopenharmony_ci writel(ntb_ctl, mmio + AMD_CNTL_OFFSET); 104462306a36Sopenharmony_ci} 104562306a36Sopenharmony_ci 104662306a36Sopenharmony_cistatic void amd_deinit_side_info(struct amd_ntb_dev *ndev) 104762306a36Sopenharmony_ci{ 104862306a36Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 104962306a36Sopenharmony_ci u32 ntb_ctl; 105062306a36Sopenharmony_ci 105162306a36Sopenharmony_ci amd_clear_side_info_reg(ndev, false); 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci ntb_ctl = readl(mmio + AMD_CNTL_OFFSET); 105462306a36Sopenharmony_ci ntb_ctl &= ~(PMM_REG_CTL | SMM_REG_CTL); 105562306a36Sopenharmony_ci writel(ntb_ctl, mmio + AMD_CNTL_OFFSET); 105662306a36Sopenharmony_ci} 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_cistatic int amd_init_ntb(struct amd_ntb_dev *ndev) 105962306a36Sopenharmony_ci{ 106062306a36Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 106162306a36Sopenharmony_ci 106262306a36Sopenharmony_ci ndev->mw_count = ndev->dev_data->mw_count; 106362306a36Sopenharmony_ci ndev->spad_count = AMD_SPADS_CNT; 106462306a36Sopenharmony_ci ndev->db_count = AMD_DB_CNT; 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_ci switch (ndev->ntb.topo) { 106762306a36Sopenharmony_ci case NTB_TOPO_PRI: 106862306a36Sopenharmony_ci case NTB_TOPO_SEC: 106962306a36Sopenharmony_ci ndev->spad_count >>= 1; 107062306a36Sopenharmony_ci if (ndev->ntb.topo == NTB_TOPO_PRI) { 107162306a36Sopenharmony_ci ndev->self_spad = 0; 107262306a36Sopenharmony_ci ndev->peer_spad = 0x20; 107362306a36Sopenharmony_ci } else { 107462306a36Sopenharmony_ci ndev->self_spad = 0x20; 107562306a36Sopenharmony_ci ndev->peer_spad = 0; 107662306a36Sopenharmony_ci } 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_ci INIT_DELAYED_WORK(&ndev->hb_timer, amd_link_hb); 107962306a36Sopenharmony_ci schedule_delayed_work(&ndev->hb_timer, AMD_LINK_HB_TIMEOUT); 108062306a36Sopenharmony_ci 108162306a36Sopenharmony_ci break; 108262306a36Sopenharmony_ci default: 108362306a36Sopenharmony_ci dev_err(&ndev->ntb.pdev->dev, 108462306a36Sopenharmony_ci "AMD NTB does not support B2B mode.\n"); 108562306a36Sopenharmony_ci return -EINVAL; 108662306a36Sopenharmony_ci } 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci /* Mask event interrupts */ 108962306a36Sopenharmony_ci writel(ndev->int_mask, mmio + AMD_INTMASK_OFFSET); 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_ci return 0; 109262306a36Sopenharmony_ci} 109362306a36Sopenharmony_ci 109462306a36Sopenharmony_cistatic enum ntb_topo amd_get_topo(struct amd_ntb_dev *ndev) 109562306a36Sopenharmony_ci{ 109662306a36Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 109762306a36Sopenharmony_ci u32 info; 109862306a36Sopenharmony_ci 109962306a36Sopenharmony_ci info = readl(mmio + AMD_SIDEINFO_OFFSET); 110062306a36Sopenharmony_ci if (info & AMD_SIDE_MASK) 110162306a36Sopenharmony_ci return NTB_TOPO_SEC; 110262306a36Sopenharmony_ci else 110362306a36Sopenharmony_ci return NTB_TOPO_PRI; 110462306a36Sopenharmony_ci} 110562306a36Sopenharmony_ci 110662306a36Sopenharmony_cistatic int amd_init_dev(struct amd_ntb_dev *ndev) 110762306a36Sopenharmony_ci{ 110862306a36Sopenharmony_ci void __iomem *mmio = ndev->self_mmio; 110962306a36Sopenharmony_ci struct pci_dev *pdev; 111062306a36Sopenharmony_ci int rc = 0; 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_ci pdev = ndev->ntb.pdev; 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_ci ndev->ntb.topo = amd_get_topo(ndev); 111562306a36Sopenharmony_ci dev_dbg(&pdev->dev, "AMD NTB topo is %s\n", 111662306a36Sopenharmony_ci ntb_topo_string(ndev->ntb.topo)); 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci rc = amd_init_ntb(ndev); 111962306a36Sopenharmony_ci if (rc) 112062306a36Sopenharmony_ci return rc; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci rc = amd_init_isr(ndev); 112362306a36Sopenharmony_ci if (rc) { 112462306a36Sopenharmony_ci dev_err(&pdev->dev, "fail to init isr.\n"); 112562306a36Sopenharmony_ci return rc; 112662306a36Sopenharmony_ci } 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci ndev->db_valid_mask = BIT_ULL(ndev->db_count) - 1; 112962306a36Sopenharmony_ci /* 113062306a36Sopenharmony_ci * We reserve the highest order bit of the DB register which will 113162306a36Sopenharmony_ci * be used to notify peer when the driver on this side is being 113262306a36Sopenharmony_ci * un-loaded. 113362306a36Sopenharmony_ci */ 113462306a36Sopenharmony_ci ndev->db_last_bit = 113562306a36Sopenharmony_ci find_last_bit((unsigned long *)&ndev->db_valid_mask, 113662306a36Sopenharmony_ci hweight64(ndev->db_valid_mask)); 113762306a36Sopenharmony_ci writew((u16)~BIT(ndev->db_last_bit), mmio + AMD_DBMASK_OFFSET); 113862306a36Sopenharmony_ci /* 113962306a36Sopenharmony_ci * Since now there is one less bit to account for, the DB count 114062306a36Sopenharmony_ci * and DB mask should be adjusted accordingly. 114162306a36Sopenharmony_ci */ 114262306a36Sopenharmony_ci ndev->db_count -= 1; 114362306a36Sopenharmony_ci ndev->db_valid_mask = BIT_ULL(ndev->db_count) - 1; 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci /* Enable Link-Up and Link-Down event interrupts */ 114662306a36Sopenharmony_ci ndev->int_mask &= ~(AMD_LINK_UP_EVENT | AMD_LINK_DOWN_EVENT); 114762306a36Sopenharmony_ci writel(ndev->int_mask, mmio + AMD_INTMASK_OFFSET); 114862306a36Sopenharmony_ci 114962306a36Sopenharmony_ci return 0; 115062306a36Sopenharmony_ci} 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_cistatic void amd_deinit_dev(struct amd_ntb_dev *ndev) 115362306a36Sopenharmony_ci{ 115462306a36Sopenharmony_ci cancel_delayed_work_sync(&ndev->hb_timer); 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci ndev_deinit_isr(ndev); 115762306a36Sopenharmony_ci} 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_cistatic int amd_ntb_init_pci(struct amd_ntb_dev *ndev, 116062306a36Sopenharmony_ci struct pci_dev *pdev) 116162306a36Sopenharmony_ci{ 116262306a36Sopenharmony_ci int rc; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci pci_set_drvdata(pdev, ndev); 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci rc = pci_enable_device(pdev); 116762306a36Sopenharmony_ci if (rc) 116862306a36Sopenharmony_ci goto err_pci_enable; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci rc = pci_request_regions(pdev, NTB_NAME); 117162306a36Sopenharmony_ci if (rc) 117262306a36Sopenharmony_ci goto err_pci_regions; 117362306a36Sopenharmony_ci 117462306a36Sopenharmony_ci pci_set_master(pdev); 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_ci rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 117762306a36Sopenharmony_ci if (rc) { 117862306a36Sopenharmony_ci rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 117962306a36Sopenharmony_ci if (rc) 118062306a36Sopenharmony_ci goto err_dma_mask; 118162306a36Sopenharmony_ci dev_warn(&pdev->dev, "Cannot DMA highmem\n"); 118262306a36Sopenharmony_ci } 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci ndev->self_mmio = pci_iomap(pdev, 0, 0); 118562306a36Sopenharmony_ci if (!ndev->self_mmio) { 118662306a36Sopenharmony_ci rc = -EIO; 118762306a36Sopenharmony_ci goto err_dma_mask; 118862306a36Sopenharmony_ci } 118962306a36Sopenharmony_ci ndev->peer_mmio = ndev->self_mmio + AMD_PEER_OFFSET; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci return 0; 119262306a36Sopenharmony_ci 119362306a36Sopenharmony_cierr_dma_mask: 119462306a36Sopenharmony_ci pci_release_regions(pdev); 119562306a36Sopenharmony_cierr_pci_regions: 119662306a36Sopenharmony_ci pci_disable_device(pdev); 119762306a36Sopenharmony_cierr_pci_enable: 119862306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 119962306a36Sopenharmony_ci return rc; 120062306a36Sopenharmony_ci} 120162306a36Sopenharmony_ci 120262306a36Sopenharmony_cistatic void amd_ntb_deinit_pci(struct amd_ntb_dev *ndev) 120362306a36Sopenharmony_ci{ 120462306a36Sopenharmony_ci struct pci_dev *pdev = ndev->ntb.pdev; 120562306a36Sopenharmony_ci 120662306a36Sopenharmony_ci pci_iounmap(pdev, ndev->self_mmio); 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci pci_release_regions(pdev); 120962306a36Sopenharmony_ci pci_disable_device(pdev); 121062306a36Sopenharmony_ci pci_set_drvdata(pdev, NULL); 121162306a36Sopenharmony_ci} 121262306a36Sopenharmony_ci 121362306a36Sopenharmony_cistatic int amd_ntb_pci_probe(struct pci_dev *pdev, 121462306a36Sopenharmony_ci const struct pci_device_id *id) 121562306a36Sopenharmony_ci{ 121662306a36Sopenharmony_ci struct amd_ntb_dev *ndev; 121762306a36Sopenharmony_ci int rc, node; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci node = dev_to_node(&pdev->dev); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci ndev = kzalloc_node(sizeof(*ndev), GFP_KERNEL, node); 122262306a36Sopenharmony_ci if (!ndev) { 122362306a36Sopenharmony_ci rc = -ENOMEM; 122462306a36Sopenharmony_ci goto err_ndev; 122562306a36Sopenharmony_ci } 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci ndev->dev_data = (struct ntb_dev_data *)id->driver_data; 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci ndev_init_struct(ndev, pdev); 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci rc = amd_ntb_init_pci(ndev, pdev); 123262306a36Sopenharmony_ci if (rc) 123362306a36Sopenharmony_ci goto err_init_pci; 123462306a36Sopenharmony_ci 123562306a36Sopenharmony_ci rc = amd_init_dev(ndev); 123662306a36Sopenharmony_ci if (rc) 123762306a36Sopenharmony_ci goto err_init_dev; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci /* write side info */ 124062306a36Sopenharmony_ci amd_init_side_info(ndev); 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci amd_poll_link(ndev); 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci ndev_init_debugfs(ndev); 124562306a36Sopenharmony_ci 124662306a36Sopenharmony_ci rc = ntb_register_device(&ndev->ntb); 124762306a36Sopenharmony_ci if (rc) 124862306a36Sopenharmony_ci goto err_register; 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci dev_info(&pdev->dev, "NTB device registered.\n"); 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci return 0; 125362306a36Sopenharmony_ci 125462306a36Sopenharmony_cierr_register: 125562306a36Sopenharmony_ci ndev_deinit_debugfs(ndev); 125662306a36Sopenharmony_ci amd_deinit_dev(ndev); 125762306a36Sopenharmony_cierr_init_dev: 125862306a36Sopenharmony_ci amd_ntb_deinit_pci(ndev); 125962306a36Sopenharmony_cierr_init_pci: 126062306a36Sopenharmony_ci kfree(ndev); 126162306a36Sopenharmony_cierr_ndev: 126262306a36Sopenharmony_ci return rc; 126362306a36Sopenharmony_ci} 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_cistatic void amd_ntb_pci_remove(struct pci_dev *pdev) 126662306a36Sopenharmony_ci{ 126762306a36Sopenharmony_ci struct amd_ntb_dev *ndev = pci_get_drvdata(pdev); 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci /* 127062306a36Sopenharmony_ci * Clear the READY bit in SIDEINFO register before sending DB event 127162306a36Sopenharmony_ci * to the peer. This will make sure that when the peer handles the 127262306a36Sopenharmony_ci * DB event, it correctly reads this bit as being 0. 127362306a36Sopenharmony_ci */ 127462306a36Sopenharmony_ci amd_deinit_side_info(ndev); 127562306a36Sopenharmony_ci ntb_peer_db_set(&ndev->ntb, BIT_ULL(ndev->db_last_bit)); 127662306a36Sopenharmony_ci ntb_unregister_device(&ndev->ntb); 127762306a36Sopenharmony_ci ndev_deinit_debugfs(ndev); 127862306a36Sopenharmony_ci amd_deinit_dev(ndev); 127962306a36Sopenharmony_ci amd_ntb_deinit_pci(ndev); 128062306a36Sopenharmony_ci kfree(ndev); 128162306a36Sopenharmony_ci} 128262306a36Sopenharmony_ci 128362306a36Sopenharmony_cistatic void amd_ntb_pci_shutdown(struct pci_dev *pdev) 128462306a36Sopenharmony_ci{ 128562306a36Sopenharmony_ci struct amd_ntb_dev *ndev = pci_get_drvdata(pdev); 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci /* Send link down notification */ 128862306a36Sopenharmony_ci ntb_link_event(&ndev->ntb); 128962306a36Sopenharmony_ci 129062306a36Sopenharmony_ci amd_deinit_side_info(ndev); 129162306a36Sopenharmony_ci ntb_peer_db_set(&ndev->ntb, BIT_ULL(ndev->db_last_bit)); 129262306a36Sopenharmony_ci ntb_unregister_device(&ndev->ntb); 129362306a36Sopenharmony_ci ndev_deinit_debugfs(ndev); 129462306a36Sopenharmony_ci amd_deinit_dev(ndev); 129562306a36Sopenharmony_ci amd_ntb_deinit_pci(ndev); 129662306a36Sopenharmony_ci kfree(ndev); 129762306a36Sopenharmony_ci} 129862306a36Sopenharmony_ci 129962306a36Sopenharmony_cistatic const struct file_operations amd_ntb_debugfs_info = { 130062306a36Sopenharmony_ci .owner = THIS_MODULE, 130162306a36Sopenharmony_ci .open = simple_open, 130262306a36Sopenharmony_ci .read = ndev_debugfs_read, 130362306a36Sopenharmony_ci}; 130462306a36Sopenharmony_ci 130562306a36Sopenharmony_cistatic const struct ntb_dev_data dev_data[] = { 130662306a36Sopenharmony_ci { /* for device 145b */ 130762306a36Sopenharmony_ci .mw_count = 3, 130862306a36Sopenharmony_ci .mw_idx = 1, 130962306a36Sopenharmony_ci }, 131062306a36Sopenharmony_ci { /* for device 148b */ 131162306a36Sopenharmony_ci .mw_count = 2, 131262306a36Sopenharmony_ci .mw_idx = 2, 131362306a36Sopenharmony_ci }, 131462306a36Sopenharmony_ci}; 131562306a36Sopenharmony_ci 131662306a36Sopenharmony_cistatic const struct pci_device_id amd_ntb_pci_tbl[] = { 131762306a36Sopenharmony_ci { PCI_VDEVICE(AMD, 0x145b), (kernel_ulong_t)&dev_data[0] }, 131862306a36Sopenharmony_ci { PCI_VDEVICE(AMD, 0x148b), (kernel_ulong_t)&dev_data[1] }, 131962306a36Sopenharmony_ci { PCI_VDEVICE(AMD, 0x14c0), (kernel_ulong_t)&dev_data[1] }, 132062306a36Sopenharmony_ci { PCI_VDEVICE(AMD, 0x14c3), (kernel_ulong_t)&dev_data[1] }, 132162306a36Sopenharmony_ci { PCI_VDEVICE(HYGON, 0x145b), (kernel_ulong_t)&dev_data[0] }, 132262306a36Sopenharmony_ci { 0, } 132362306a36Sopenharmony_ci}; 132462306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, amd_ntb_pci_tbl); 132562306a36Sopenharmony_ci 132662306a36Sopenharmony_cistatic struct pci_driver amd_ntb_pci_driver = { 132762306a36Sopenharmony_ci .name = KBUILD_MODNAME, 132862306a36Sopenharmony_ci .id_table = amd_ntb_pci_tbl, 132962306a36Sopenharmony_ci .probe = amd_ntb_pci_probe, 133062306a36Sopenharmony_ci .remove = amd_ntb_pci_remove, 133162306a36Sopenharmony_ci .shutdown = amd_ntb_pci_shutdown, 133262306a36Sopenharmony_ci}; 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_cistatic int __init amd_ntb_pci_driver_init(void) 133562306a36Sopenharmony_ci{ 133662306a36Sopenharmony_ci int ret; 133762306a36Sopenharmony_ci pr_info("%s %s\n", NTB_DESC, NTB_VER); 133862306a36Sopenharmony_ci 133962306a36Sopenharmony_ci if (debugfs_initialized()) 134062306a36Sopenharmony_ci debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, NULL); 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci ret = pci_register_driver(&amd_ntb_pci_driver); 134362306a36Sopenharmony_ci if (ret) 134462306a36Sopenharmony_ci debugfs_remove_recursive(debugfs_dir); 134562306a36Sopenharmony_ci 134662306a36Sopenharmony_ci return ret; 134762306a36Sopenharmony_ci} 134862306a36Sopenharmony_cimodule_init(amd_ntb_pci_driver_init); 134962306a36Sopenharmony_ci 135062306a36Sopenharmony_cistatic void __exit amd_ntb_pci_driver_exit(void) 135162306a36Sopenharmony_ci{ 135262306a36Sopenharmony_ci pci_unregister_driver(&amd_ntb_pci_driver); 135362306a36Sopenharmony_ci debugfs_remove_recursive(debugfs_dir); 135462306a36Sopenharmony_ci} 135562306a36Sopenharmony_cimodule_exit(amd_ntb_pci_driver_exit); 1356