/* * This file is provided under a dual BSD/GPLv2 license. When using or * redistributing this file, you may do so under either license. * * GPL LICENSE SUMMARY * * Copyright (C) 2016 Advanced Micro Devices, Inc. All Rights Reserved. * Copyright (C) 2016 T-Platforms. All Rights Reserved. * * This program is free software; you can redistribute it and/or modify * it under the terms of version 2 of the GNU General Public License as * published by the Free Software Foundation. * * BSD LICENSE * * Copyright (C) 2016 Advanced Micro Devices, Inc. All Rights Reserved. * Copyright (C) 2016 T-Platforms. All Rights Reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copy * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * Neither the name of AMD Corporation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * AMD PCIe NTB Linux driver * * Contact Information: * Xiangliang Yu <Xiangliang.Yu@amd.com>
*/
if (ndev->ntb.topo == NTB_TOPO_SEC) { /* Locate the pointer to Downstream Switch for this device */
pci_swds = pci_upstream_bridge(ndev->ntb.pdev); if (pci_swds) { /* * Locate the pointer to Upstream Switch for * the Downstream Switch.
*/
pci_swus = pci_upstream_bridge(pci_swds); if (pci_swus) {
rc = pcie_capability_read_dword(pci_swus,
PCI_EXP_LNKCTL,
&stat); if (rc) return 0;
} else { return 0;
}
} else { return 0;
}
} elseif (ndev->ntb.topo == NTB_TOPO_PRI) { /* * For NTB primary, we simply read the Link Status and control * register of the NTB device itself.
*/
pdev = ndev->ntb.pdev;
rc = pcie_capability_read_dword(pdev, PCI_EXP_LNKCTL, &stat); if (rc) return 0;
} else { /* Catch all for everything else */ return 0;
}
ndev->lnk_sta = stat;
return 1;
}
staticint amd_link_is_up(struct amd_ntb_dev *ndev)
{ int ret;
/* * We consider the link to be up under two conditions: * * - When a link-up event is received. This is indicated by * AMD_LINK_UP_EVENT set in peer_sta. * - When driver on both sides of the link have been loaded. * This is indicated by bit 1 being set in the peer * SIDEINFO register. * * This function should return 1 when the latter of the above * two conditions is true. * * Now consider the sequence of events - Link-Up event occurs, * then the peer side driver loads. In this case, we would have * received LINK_UP event and bit 1 of peer SIDEINFO is also * set. What happens now if the link goes down? Bit 1 of * peer SIDEINFO remains set, but LINK_DOWN bit is set in * peer_sta. So we should return 0 from this function. Not only * that, we clear bit 1 of peer SIDEINFO to 0, since the peer * side driver did not even get a chance to clear it before * the link went down. This can be the case of surprise link * removal. * * LINK_UP event will always occur before the peer side driver * gets loaded the very first time. So there can be a case when * the LINK_UP event has occurred, but the peer side driver hasn't * yet loaded. We return 0 in that case. * * There is also a special case when the primary side driver is * unloaded and then loaded again. Since there is no change in * the status of NTB secondary in this case, there is no Link-Up * or Link-Down notification received. We recognize this condition * with peer_sta being set to 0. * * If bit 1 of peer SIDEINFO register is not set, then we * simply return 0 irrespective of the link up or down status * set in peer_sta.
*/
ret = amd_poll_link(ndev); if (ret) { /* * We need to check the below only for NTB primary. For NTB * secondary, simply checking the result of PSIDE_INFO * register will suffice.
*/ if (ndev->ntb.topo == NTB_TOPO_PRI) { if ((ndev->peer_sta & AMD_LINK_UP_EVENT) ||
(ndev->peer_sta == 0)) return ret; elseif (ndev->peer_sta & AMD_LINK_DOWN_EVENT) { /* Clear peer sideinfo register */
amd_clear_side_info_reg(ndev, true);
status = readl(mmio + AMD_INTSTAT_OFFSET); if (!(status & AMD_EVENT_INTMASK)) return;
dev_dbg(dev, "status = 0x%x and vec = %d\n", status, vec);
status &= AMD_EVENT_INTMASK; switch (status) { case AMD_PEER_FLUSH_EVENT:
ndev->peer_sta |= AMD_PEER_FLUSH_EVENT;
dev_info(dev, "Flush is done.\n"); break; case AMD_PEER_RESET_EVENT: case AMD_LINK_DOWN_EVENT:
ndev->peer_sta |= status; if (status == AMD_LINK_DOWN_EVENT)
ndev->peer_sta &= ~AMD_LINK_UP_EVENT;
amd_ack_smu(ndev, status);
/* link down first */
ntb_link_event(&ndev->ntb); /* polling peer status */
schedule_delayed_work(&ndev->hb_timer, AMD_LINK_HB_TIMEOUT);
break; case AMD_PEER_D3_EVENT: case AMD_PEER_PMETO_EVENT: case AMD_LINK_UP_EVENT:
ndev->peer_sta |= status; if (status == AMD_LINK_UP_EVENT)
ndev->peer_sta &= ~AMD_LINK_DOWN_EVENT; elseif (status == AMD_PEER_D3_EVENT)
ndev->peer_sta &= ~AMD_PEER_D0_EVENT;
amd_ack_smu(ndev, status);
/* link down */
ntb_link_event(&ndev->ntb);
break; case AMD_PEER_D0_EVENT:
mmio = ndev->peer_mmio;
status = readl(mmio + AMD_PMESTAT_OFFSET); /* check if this is WAKEUP event */ if (status & 0x1)
dev_info(dev, "Wakeup is done.\n");
/* start a timer to poll link status */
schedule_delayed_work(&ndev->hb_timer,
AMD_LINK_HB_TIMEOUT); break; default:
dev_info(dev, "event status = 0x%x.\n", status); break;
}
/* Clear the interrupt status */
writel(status, mmio + AMD_INTSTAT_OFFSET);
}
dev_dbg(dev, "status = 0x%llx and vec = %d\n", status, vec);
/* * Since we had reserved highest order bit of DB for signaling peer of * a special event, this is the only status bit we should be concerned * here now.
*/ if (status & BIT(ndev->db_last_bit)) {
ntb_db_clear(&ndev->ntb, BIT(ndev->db_last_bit)); /* send link down event notification */
ntb_link_event(&ndev->ntb);
/* * If we are here, that means the peer has signalled a special * event which notifies that the peer driver has been * un-loaded for some reason. Since there is a chance that the * peer will load its driver again sometime, we schedule link * polling routine.
*/
schedule_delayed_work(&ndev->hb_timer, AMD_LINK_HB_TIMEOUT);
}
}
/* NOTE: Disable MSIX if msix count is less than 16 because of * hardware limitation.
*/ if (msix_count < msix_min) {
pci_disable_msix(pdev); goto err_msix_enable;
}
for (i = 0; i < msix_count; ++i) {
ndev->vec[i].ndev = ndev;
ndev->vec[i].num = i;
rc = request_irq(ndev->msix[i].vector, ndev_vec_isr, 0, "ndev_vec_isr", &ndev->vec[i]); if (rc) goto err_msix_request;
}
ndev->ntb.topo = amd_get_topo(ndev);
dev_dbg(&pdev->dev, "AMD NTB topo is %s\n",
ntb_topo_string(ndev->ntb.topo));
rc = amd_init_ntb(ndev); if (rc) return rc;
rc = amd_init_isr(ndev); if (rc) {
dev_err(&pdev->dev, "fail to init isr.\n"); return rc;
}
ndev->db_valid_mask = BIT_ULL(ndev->db_count) - 1; /* * We reserve the highest order bit of the DB register which will * be used to notify peer when the driver on this side is being * un-loaded.
*/
ndev->db_last_bit =
find_last_bit((unsignedlong *)&ndev->db_valid_mask,
hweight64(ndev->db_valid_mask));
writew((u16)~BIT(ndev->db_last_bit), mmio + AMD_DBMASK_OFFSET); /* * Since now there is one less bit to account for, the DB count * and DB mask should be adjusted accordingly.
*/
ndev->db_count -= 1;
ndev->db_valid_mask = BIT_ULL(ndev->db_count) - 1;
/* * Clear the READY bit in SIDEINFO register before sending DB event * to the peer. This will make sure that when the peer handles the * DB event, it correctly reads this bit as being 0.
*/
amd_deinit_side_info(ndev);
ntb_peer_db_set(&ndev->ntb, BIT_ULL(ndev->db_last_bit));
ntb_unregister_device(&ndev->ntb);
ndev_deinit_debugfs(ndev);
amd_deinit_dev(ndev);
amd_ntb_deinit_pci(ndev);
kfree(ndev);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.