18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright(c) 2015 - 2019 Intel Corporation. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This file is provided under a dual BSD/GPLv2 license. When using or 58c2ecf20Sopenharmony_ci * redistributing this file, you may do so under either license. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * GPL LICENSE SUMMARY 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 108c2ecf20Sopenharmony_ci * it under the terms of version 2 of the GNU General Public License as 118c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * This program is distributed in the hope that it will be useful, but 148c2ecf20Sopenharmony_ci * WITHOUT ANY WARRANTY; without even the implied warranty of 158c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 168c2ecf20Sopenharmony_ci * General Public License for more details. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * BSD LICENSE 198c2ecf20Sopenharmony_ci * 208c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or without 218c2ecf20Sopenharmony_ci * modification, are permitted provided that the following conditions 228c2ecf20Sopenharmony_ci * are met: 238c2ecf20Sopenharmony_ci * 248c2ecf20Sopenharmony_ci * - Redistributions of source code must retain the above copyright 258c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer. 268c2ecf20Sopenharmony_ci * - Redistributions in binary form must reproduce the above copyright 278c2ecf20Sopenharmony_ci * notice, this list of conditions and the following disclaimer in 288c2ecf20Sopenharmony_ci * the documentation and/or other materials provided with the 298c2ecf20Sopenharmony_ci * distribution. 308c2ecf20Sopenharmony_ci * - Neither the name of Intel Corporation nor the names of its 318c2ecf20Sopenharmony_ci * contributors may be used to endorse or promote products derived 328c2ecf20Sopenharmony_ci * from this software without specific prior written permission. 338c2ecf20Sopenharmony_ci * 348c2ecf20Sopenharmony_ci * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 358c2ecf20Sopenharmony_ci * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 368c2ecf20Sopenharmony_ci * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 378c2ecf20Sopenharmony_ci * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 388c2ecf20Sopenharmony_ci * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 398c2ecf20Sopenharmony_ci * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 408c2ecf20Sopenharmony_ci * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 418c2ecf20Sopenharmony_ci * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 428c2ecf20Sopenharmony_ci * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 438c2ecf20Sopenharmony_ci * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 448c2ecf20Sopenharmony_ci * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 458c2ecf20Sopenharmony_ci * 468c2ecf20Sopenharmony_ci */ 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci#include <linux/bitfield.h> 498c2ecf20Sopenharmony_ci#include <linux/pci.h> 508c2ecf20Sopenharmony_ci#include <linux/io.h> 518c2ecf20Sopenharmony_ci#include <linux/delay.h> 528c2ecf20Sopenharmony_ci#include <linux/vmalloc.h> 538c2ecf20Sopenharmony_ci#include <linux/aer.h> 548c2ecf20Sopenharmony_ci#include <linux/module.h> 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci#include "hfi.h" 578c2ecf20Sopenharmony_ci#include "chip_registers.h" 588c2ecf20Sopenharmony_ci#include "aspm.h" 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci/* 618c2ecf20Sopenharmony_ci * This file contains PCIe utility routines. 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci/* 658c2ecf20Sopenharmony_ci * Do all the common PCIe setup and initialization. 668c2ecf20Sopenharmony_ci */ 678c2ecf20Sopenharmony_ciint hfi1_pcie_init(struct hfi1_devdata *dd) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci int ret; 708c2ecf20Sopenharmony_ci struct pci_dev *pdev = dd->pcidev; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci ret = pci_enable_device(pdev); 738c2ecf20Sopenharmony_ci if (ret) { 748c2ecf20Sopenharmony_ci /* 758c2ecf20Sopenharmony_ci * This can happen (in theory) iff: 768c2ecf20Sopenharmony_ci * We did a chip reset, and then failed to reprogram the 778c2ecf20Sopenharmony_ci * BAR, or the chip reset due to an internal error. We then 788c2ecf20Sopenharmony_ci * unloaded the driver and reloaded it. 798c2ecf20Sopenharmony_ci * 808c2ecf20Sopenharmony_ci * Both reset cases set the BAR back to initial state. For 818c2ecf20Sopenharmony_ci * the latter case, the AER sticky error bit at offset 0x718 828c2ecf20Sopenharmony_ci * should be set, but the Linux kernel doesn't yet know 838c2ecf20Sopenharmony_ci * about that, it appears. If the original BAR was retained 848c2ecf20Sopenharmony_ci * in the kernel data structures, this may be OK. 858c2ecf20Sopenharmony_ci */ 868c2ecf20Sopenharmony_ci dd_dev_err(dd, "pci enable failed: error %d\n", -ret); 878c2ecf20Sopenharmony_ci return ret; 888c2ecf20Sopenharmony_ci } 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci ret = pci_request_regions(pdev, DRIVER_NAME); 918c2ecf20Sopenharmony_ci if (ret) { 928c2ecf20Sopenharmony_ci dd_dev_err(dd, "pci_request_regions fails: err %d\n", -ret); 938c2ecf20Sopenharmony_ci goto bail; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(64)); 978c2ecf20Sopenharmony_ci if (ret) { 988c2ecf20Sopenharmony_ci /* 998c2ecf20Sopenharmony_ci * If the 64 bit setup fails, try 32 bit. Some systems 1008c2ecf20Sopenharmony_ci * do not setup 64 bit maps on systems with 2GB or less 1018c2ecf20Sopenharmony_ci * memory installed. 1028c2ecf20Sopenharmony_ci */ 1038c2ecf20Sopenharmony_ci ret = pci_set_dma_mask(pdev, DMA_BIT_MASK(32)); 1048c2ecf20Sopenharmony_ci if (ret) { 1058c2ecf20Sopenharmony_ci dd_dev_err(dd, "Unable to set DMA mask: %d\n", ret); 1068c2ecf20Sopenharmony_ci goto bail; 1078c2ecf20Sopenharmony_ci } 1088c2ecf20Sopenharmony_ci ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(32)); 1098c2ecf20Sopenharmony_ci } else { 1108c2ecf20Sopenharmony_ci ret = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci if (ret) { 1138c2ecf20Sopenharmony_ci dd_dev_err(dd, "Unable to set DMA consistent mask: %d\n", ret); 1148c2ecf20Sopenharmony_ci goto bail; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci pci_set_master(pdev); 1188c2ecf20Sopenharmony_ci (void)pci_enable_pcie_error_reporting(pdev); 1198c2ecf20Sopenharmony_ci return 0; 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cibail: 1228c2ecf20Sopenharmony_ci hfi1_pcie_cleanup(pdev); 1238c2ecf20Sopenharmony_ci return ret; 1248c2ecf20Sopenharmony_ci} 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci/* 1278c2ecf20Sopenharmony_ci * Clean what was done in hfi1_pcie_init() 1288c2ecf20Sopenharmony_ci */ 1298c2ecf20Sopenharmony_civoid hfi1_pcie_cleanup(struct pci_dev *pdev) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci pci_disable_device(pdev); 1328c2ecf20Sopenharmony_ci /* 1338c2ecf20Sopenharmony_ci * Release regions should be called after the disable. OK to 1348c2ecf20Sopenharmony_ci * call if request regions has not been called or failed. 1358c2ecf20Sopenharmony_ci */ 1368c2ecf20Sopenharmony_ci pci_release_regions(pdev); 1378c2ecf20Sopenharmony_ci} 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci/* 1408c2ecf20Sopenharmony_ci * Do remaining PCIe setup, once dd is allocated, and save away 1418c2ecf20Sopenharmony_ci * fields required to re-initialize after a chip reset, or for 1428c2ecf20Sopenharmony_ci * various other purposes 1438c2ecf20Sopenharmony_ci */ 1448c2ecf20Sopenharmony_ciint hfi1_pcie_ddinit(struct hfi1_devdata *dd, struct pci_dev *pdev) 1458c2ecf20Sopenharmony_ci{ 1468c2ecf20Sopenharmony_ci unsigned long len; 1478c2ecf20Sopenharmony_ci resource_size_t addr; 1488c2ecf20Sopenharmony_ci int ret = 0; 1498c2ecf20Sopenharmony_ci u32 rcv_array_count; 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_ci addr = pci_resource_start(pdev, 0); 1528c2ecf20Sopenharmony_ci len = pci_resource_len(pdev, 0); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci /* 1558c2ecf20Sopenharmony_ci * The TXE PIO buffers are at the tail end of the chip space. 1568c2ecf20Sopenharmony_ci * Cut them off and map them separately. 1578c2ecf20Sopenharmony_ci */ 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci /* sanity check vs expectations */ 1608c2ecf20Sopenharmony_ci if (len != TXE_PIO_SEND + TXE_PIO_SIZE) { 1618c2ecf20Sopenharmony_ci dd_dev_err(dd, "chip PIO range does not match\n"); 1628c2ecf20Sopenharmony_ci return -EINVAL; 1638c2ecf20Sopenharmony_ci } 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci dd->kregbase1 = ioremap(addr, RCV_ARRAY); 1668c2ecf20Sopenharmony_ci if (!dd->kregbase1) { 1678c2ecf20Sopenharmony_ci dd_dev_err(dd, "UC mapping of kregbase1 failed\n"); 1688c2ecf20Sopenharmony_ci return -ENOMEM; 1698c2ecf20Sopenharmony_ci } 1708c2ecf20Sopenharmony_ci dd_dev_info(dd, "UC base1: %p for %x\n", dd->kregbase1, RCV_ARRAY); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* verify that reads actually work, save revision for reset check */ 1738c2ecf20Sopenharmony_ci dd->revision = readq(dd->kregbase1 + CCE_REVISION); 1748c2ecf20Sopenharmony_ci if (dd->revision == ~(u64)0) { 1758c2ecf20Sopenharmony_ci dd_dev_err(dd, "Cannot read chip CSRs\n"); 1768c2ecf20Sopenharmony_ci goto nomem; 1778c2ecf20Sopenharmony_ci } 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_ci rcv_array_count = readq(dd->kregbase1 + RCV_ARRAY_CNT); 1808c2ecf20Sopenharmony_ci dd_dev_info(dd, "RcvArray count: %u\n", rcv_array_count); 1818c2ecf20Sopenharmony_ci dd->base2_start = RCV_ARRAY + rcv_array_count * 8; 1828c2ecf20Sopenharmony_ci 1838c2ecf20Sopenharmony_ci dd->kregbase2 = ioremap( 1848c2ecf20Sopenharmony_ci addr + dd->base2_start, 1858c2ecf20Sopenharmony_ci TXE_PIO_SEND - dd->base2_start); 1868c2ecf20Sopenharmony_ci if (!dd->kregbase2) { 1878c2ecf20Sopenharmony_ci dd_dev_err(dd, "UC mapping of kregbase2 failed\n"); 1888c2ecf20Sopenharmony_ci goto nomem; 1898c2ecf20Sopenharmony_ci } 1908c2ecf20Sopenharmony_ci dd_dev_info(dd, "UC base2: %p for %x\n", dd->kregbase2, 1918c2ecf20Sopenharmony_ci TXE_PIO_SEND - dd->base2_start); 1928c2ecf20Sopenharmony_ci 1938c2ecf20Sopenharmony_ci dd->piobase = ioremap_wc(addr + TXE_PIO_SEND, TXE_PIO_SIZE); 1948c2ecf20Sopenharmony_ci if (!dd->piobase) { 1958c2ecf20Sopenharmony_ci dd_dev_err(dd, "WC mapping of send buffers failed\n"); 1968c2ecf20Sopenharmony_ci goto nomem; 1978c2ecf20Sopenharmony_ci } 1988c2ecf20Sopenharmony_ci dd_dev_info(dd, "WC piobase: %p for %x\n", dd->piobase, TXE_PIO_SIZE); 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci dd->physaddr = addr; /* used for io_remap, etc. */ 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci /* 2038c2ecf20Sopenharmony_ci * Map the chip's RcvArray as write-combining to allow us 2048c2ecf20Sopenharmony_ci * to write an entire cacheline worth of entries in one shot. 2058c2ecf20Sopenharmony_ci */ 2068c2ecf20Sopenharmony_ci dd->rcvarray_wc = ioremap_wc(addr + RCV_ARRAY, 2078c2ecf20Sopenharmony_ci rcv_array_count * 8); 2088c2ecf20Sopenharmony_ci if (!dd->rcvarray_wc) { 2098c2ecf20Sopenharmony_ci dd_dev_err(dd, "WC mapping of receive array failed\n"); 2108c2ecf20Sopenharmony_ci goto nomem; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci dd_dev_info(dd, "WC RcvArray: %p for %x\n", 2138c2ecf20Sopenharmony_ci dd->rcvarray_wc, rcv_array_count * 8); 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci dd->flags |= HFI1_PRESENT; /* chip.c CSR routines now work */ 2168c2ecf20Sopenharmony_ci return 0; 2178c2ecf20Sopenharmony_cinomem: 2188c2ecf20Sopenharmony_ci ret = -ENOMEM; 2198c2ecf20Sopenharmony_ci hfi1_pcie_ddcleanup(dd); 2208c2ecf20Sopenharmony_ci return ret; 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_ci/* 2248c2ecf20Sopenharmony_ci * Do PCIe cleanup related to dd, after chip-specific cleanup, etc. Just prior 2258c2ecf20Sopenharmony_ci * to releasing the dd memory. 2268c2ecf20Sopenharmony_ci * Void because all of the core pcie cleanup functions are void. 2278c2ecf20Sopenharmony_ci */ 2288c2ecf20Sopenharmony_civoid hfi1_pcie_ddcleanup(struct hfi1_devdata *dd) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci dd->flags &= ~HFI1_PRESENT; 2318c2ecf20Sopenharmony_ci if (dd->kregbase1) 2328c2ecf20Sopenharmony_ci iounmap(dd->kregbase1); 2338c2ecf20Sopenharmony_ci dd->kregbase1 = NULL; 2348c2ecf20Sopenharmony_ci if (dd->kregbase2) 2358c2ecf20Sopenharmony_ci iounmap(dd->kregbase2); 2368c2ecf20Sopenharmony_ci dd->kregbase2 = NULL; 2378c2ecf20Sopenharmony_ci if (dd->rcvarray_wc) 2388c2ecf20Sopenharmony_ci iounmap(dd->rcvarray_wc); 2398c2ecf20Sopenharmony_ci dd->rcvarray_wc = NULL; 2408c2ecf20Sopenharmony_ci if (dd->piobase) 2418c2ecf20Sopenharmony_ci iounmap(dd->piobase); 2428c2ecf20Sopenharmony_ci dd->piobase = NULL; 2438c2ecf20Sopenharmony_ci} 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci/* return the PCIe link speed from the given link status */ 2468c2ecf20Sopenharmony_cistatic u32 extract_speed(u16 linkstat) 2478c2ecf20Sopenharmony_ci{ 2488c2ecf20Sopenharmony_ci u32 speed; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci switch (linkstat & PCI_EXP_LNKSTA_CLS) { 2518c2ecf20Sopenharmony_ci default: /* not defined, assume Gen1 */ 2528c2ecf20Sopenharmony_ci case PCI_EXP_LNKSTA_CLS_2_5GB: 2538c2ecf20Sopenharmony_ci speed = 2500; /* Gen 1, 2.5GHz */ 2548c2ecf20Sopenharmony_ci break; 2558c2ecf20Sopenharmony_ci case PCI_EXP_LNKSTA_CLS_5_0GB: 2568c2ecf20Sopenharmony_ci speed = 5000; /* Gen 2, 5GHz */ 2578c2ecf20Sopenharmony_ci break; 2588c2ecf20Sopenharmony_ci case PCI_EXP_LNKSTA_CLS_8_0GB: 2598c2ecf20Sopenharmony_ci speed = 8000; /* Gen 3, 8GHz */ 2608c2ecf20Sopenharmony_ci break; 2618c2ecf20Sopenharmony_ci } 2628c2ecf20Sopenharmony_ci return speed; 2638c2ecf20Sopenharmony_ci} 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci/* read the link status and set dd->{lbus_width,lbus_speed,lbus_info} */ 2668c2ecf20Sopenharmony_cistatic void update_lbus_info(struct hfi1_devdata *dd) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci u16 linkstat; 2698c2ecf20Sopenharmony_ci int ret; 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_ci ret = pcie_capability_read_word(dd->pcidev, PCI_EXP_LNKSTA, &linkstat); 2728c2ecf20Sopenharmony_ci if (ret) { 2738c2ecf20Sopenharmony_ci dd_dev_err(dd, "Unable to read from PCI config\n"); 2748c2ecf20Sopenharmony_ci return; 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci dd->lbus_width = FIELD_GET(PCI_EXP_LNKSTA_NLW, linkstat); 2788c2ecf20Sopenharmony_ci dd->lbus_speed = extract_speed(linkstat); 2798c2ecf20Sopenharmony_ci snprintf(dd->lbus_info, sizeof(dd->lbus_info), 2808c2ecf20Sopenharmony_ci "PCIe,%uMHz,x%u", dd->lbus_speed, dd->lbus_width); 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci/* 2848c2ecf20Sopenharmony_ci * Read in the current PCIe link width and speed. Find if the link is 2858c2ecf20Sopenharmony_ci * Gen3 capable. 2868c2ecf20Sopenharmony_ci */ 2878c2ecf20Sopenharmony_ciint pcie_speeds(struct hfi1_devdata *dd) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci u32 linkcap; 2908c2ecf20Sopenharmony_ci struct pci_dev *parent = dd->pcidev->bus->self; 2918c2ecf20Sopenharmony_ci int ret; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci if (!pci_is_pcie(dd->pcidev)) { 2948c2ecf20Sopenharmony_ci dd_dev_err(dd, "Can't find PCI Express capability!\n"); 2958c2ecf20Sopenharmony_ci return -EINVAL; 2968c2ecf20Sopenharmony_ci } 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci /* find if our max speed is Gen3 and parent supports Gen3 speeds */ 2998c2ecf20Sopenharmony_ci dd->link_gen3_capable = 1; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci ret = pcie_capability_read_dword(dd->pcidev, PCI_EXP_LNKCAP, &linkcap); 3028c2ecf20Sopenharmony_ci if (ret) { 3038c2ecf20Sopenharmony_ci dd_dev_err(dd, "Unable to read from PCI config\n"); 3048c2ecf20Sopenharmony_ci return pcibios_err_to_errno(ret); 3058c2ecf20Sopenharmony_ci } 3068c2ecf20Sopenharmony_ci 3078c2ecf20Sopenharmony_ci if ((linkcap & PCI_EXP_LNKCAP_SLS) != PCI_EXP_LNKCAP_SLS_8_0GB) { 3088c2ecf20Sopenharmony_ci dd_dev_info(dd, 3098c2ecf20Sopenharmony_ci "This HFI is not Gen3 capable, max speed 0x%x, need 0x3\n", 3108c2ecf20Sopenharmony_ci linkcap & PCI_EXP_LNKCAP_SLS); 3118c2ecf20Sopenharmony_ci dd->link_gen3_capable = 0; 3128c2ecf20Sopenharmony_ci } 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci /* 3158c2ecf20Sopenharmony_ci * bus->max_bus_speed is set from the bridge's linkcap Max Link Speed 3168c2ecf20Sopenharmony_ci */ 3178c2ecf20Sopenharmony_ci if (parent && 3188c2ecf20Sopenharmony_ci (dd->pcidev->bus->max_bus_speed == PCIE_SPEED_2_5GT || 3198c2ecf20Sopenharmony_ci dd->pcidev->bus->max_bus_speed == PCIE_SPEED_5_0GT)) { 3208c2ecf20Sopenharmony_ci dd_dev_info(dd, "Parent PCIe bridge does not support Gen3\n"); 3218c2ecf20Sopenharmony_ci dd->link_gen3_capable = 0; 3228c2ecf20Sopenharmony_ci } 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* obtain the link width and current speed */ 3258c2ecf20Sopenharmony_ci update_lbus_info(dd); 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s\n", dd->lbus_info); 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_ci return 0; 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci/** 3338c2ecf20Sopenharmony_ci * Restore command and BARs after a reset has wiped them out 3348c2ecf20Sopenharmony_ci * 3358c2ecf20Sopenharmony_ci * Returns 0 on success, otherwise a negative error value 3368c2ecf20Sopenharmony_ci */ 3378c2ecf20Sopenharmony_ciint restore_pci_variables(struct hfi1_devdata *dd) 3388c2ecf20Sopenharmony_ci{ 3398c2ecf20Sopenharmony_ci int ret; 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci ret = pci_write_config_word(dd->pcidev, PCI_COMMAND, dd->pci_command); 3428c2ecf20Sopenharmony_ci if (ret) 3438c2ecf20Sopenharmony_ci goto error; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci ret = pci_write_config_dword(dd->pcidev, PCI_BASE_ADDRESS_0, 3468c2ecf20Sopenharmony_ci dd->pcibar0); 3478c2ecf20Sopenharmony_ci if (ret) 3488c2ecf20Sopenharmony_ci goto error; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci ret = pci_write_config_dword(dd->pcidev, PCI_BASE_ADDRESS_1, 3518c2ecf20Sopenharmony_ci dd->pcibar1); 3528c2ecf20Sopenharmony_ci if (ret) 3538c2ecf20Sopenharmony_ci goto error; 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci ret = pci_write_config_dword(dd->pcidev, PCI_ROM_ADDRESS, dd->pci_rom); 3568c2ecf20Sopenharmony_ci if (ret) 3578c2ecf20Sopenharmony_ci goto error; 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci ret = pcie_capability_write_word(dd->pcidev, PCI_EXP_DEVCTL, 3608c2ecf20Sopenharmony_ci dd->pcie_devctl); 3618c2ecf20Sopenharmony_ci if (ret) 3628c2ecf20Sopenharmony_ci goto error; 3638c2ecf20Sopenharmony_ci 3648c2ecf20Sopenharmony_ci ret = pcie_capability_write_word(dd->pcidev, PCI_EXP_LNKCTL, 3658c2ecf20Sopenharmony_ci dd->pcie_lnkctl); 3668c2ecf20Sopenharmony_ci if (ret) 3678c2ecf20Sopenharmony_ci goto error; 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci ret = pcie_capability_write_word(dd->pcidev, PCI_EXP_DEVCTL2, 3708c2ecf20Sopenharmony_ci dd->pcie_devctl2); 3718c2ecf20Sopenharmony_ci if (ret) 3728c2ecf20Sopenharmony_ci goto error; 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ci ret = pci_write_config_dword(dd->pcidev, PCI_CFG_MSIX0, dd->pci_msix0); 3758c2ecf20Sopenharmony_ci if (ret) 3768c2ecf20Sopenharmony_ci goto error; 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci if (pci_find_ext_capability(dd->pcidev, PCI_EXT_CAP_ID_TPH)) { 3798c2ecf20Sopenharmony_ci ret = pci_write_config_dword(dd->pcidev, PCIE_CFG_TPH2, 3808c2ecf20Sopenharmony_ci dd->pci_tph2); 3818c2ecf20Sopenharmony_ci if (ret) 3828c2ecf20Sopenharmony_ci goto error; 3838c2ecf20Sopenharmony_ci } 3848c2ecf20Sopenharmony_ci return 0; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_cierror: 3878c2ecf20Sopenharmony_ci dd_dev_err(dd, "Unable to write to PCI config\n"); 3888c2ecf20Sopenharmony_ci return pcibios_err_to_errno(ret); 3898c2ecf20Sopenharmony_ci} 3908c2ecf20Sopenharmony_ci 3918c2ecf20Sopenharmony_ci/** 3928c2ecf20Sopenharmony_ci * Save BARs and command to rewrite after device reset 3938c2ecf20Sopenharmony_ci * 3948c2ecf20Sopenharmony_ci * Returns 0 on success, otherwise a negative error value 3958c2ecf20Sopenharmony_ci */ 3968c2ecf20Sopenharmony_ciint save_pci_variables(struct hfi1_devdata *dd) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci int ret; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci ret = pci_read_config_dword(dd->pcidev, PCI_BASE_ADDRESS_0, 4018c2ecf20Sopenharmony_ci &dd->pcibar0); 4028c2ecf20Sopenharmony_ci if (ret) 4038c2ecf20Sopenharmony_ci goto error; 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci ret = pci_read_config_dword(dd->pcidev, PCI_BASE_ADDRESS_1, 4068c2ecf20Sopenharmony_ci &dd->pcibar1); 4078c2ecf20Sopenharmony_ci if (ret) 4088c2ecf20Sopenharmony_ci goto error; 4098c2ecf20Sopenharmony_ci 4108c2ecf20Sopenharmony_ci ret = pci_read_config_dword(dd->pcidev, PCI_ROM_ADDRESS, &dd->pci_rom); 4118c2ecf20Sopenharmony_ci if (ret) 4128c2ecf20Sopenharmony_ci goto error; 4138c2ecf20Sopenharmony_ci 4148c2ecf20Sopenharmony_ci ret = pci_read_config_word(dd->pcidev, PCI_COMMAND, &dd->pci_command); 4158c2ecf20Sopenharmony_ci if (ret) 4168c2ecf20Sopenharmony_ci goto error; 4178c2ecf20Sopenharmony_ci 4188c2ecf20Sopenharmony_ci ret = pcie_capability_read_word(dd->pcidev, PCI_EXP_DEVCTL, 4198c2ecf20Sopenharmony_ci &dd->pcie_devctl); 4208c2ecf20Sopenharmony_ci if (ret) 4218c2ecf20Sopenharmony_ci goto error; 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_ci ret = pcie_capability_read_word(dd->pcidev, PCI_EXP_LNKCTL, 4248c2ecf20Sopenharmony_ci &dd->pcie_lnkctl); 4258c2ecf20Sopenharmony_ci if (ret) 4268c2ecf20Sopenharmony_ci goto error; 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci ret = pcie_capability_read_word(dd->pcidev, PCI_EXP_DEVCTL2, 4298c2ecf20Sopenharmony_ci &dd->pcie_devctl2); 4308c2ecf20Sopenharmony_ci if (ret) 4318c2ecf20Sopenharmony_ci goto error; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci ret = pci_read_config_dword(dd->pcidev, PCI_CFG_MSIX0, &dd->pci_msix0); 4348c2ecf20Sopenharmony_ci if (ret) 4358c2ecf20Sopenharmony_ci goto error; 4368c2ecf20Sopenharmony_ci 4378c2ecf20Sopenharmony_ci if (pci_find_ext_capability(dd->pcidev, PCI_EXT_CAP_ID_TPH)) { 4388c2ecf20Sopenharmony_ci ret = pci_read_config_dword(dd->pcidev, PCIE_CFG_TPH2, 4398c2ecf20Sopenharmony_ci &dd->pci_tph2); 4408c2ecf20Sopenharmony_ci if (ret) 4418c2ecf20Sopenharmony_ci goto error; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci return 0; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_cierror: 4468c2ecf20Sopenharmony_ci dd_dev_err(dd, "Unable to read from PCI config\n"); 4478c2ecf20Sopenharmony_ci return pcibios_err_to_errno(ret); 4488c2ecf20Sopenharmony_ci} 4498c2ecf20Sopenharmony_ci 4508c2ecf20Sopenharmony_ci/* 4518c2ecf20Sopenharmony_ci * BIOS may not set PCIe bus-utilization parameters for best performance. 4528c2ecf20Sopenharmony_ci * Check and optionally adjust them to maximize our throughput. 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_cistatic int hfi1_pcie_caps; 4558c2ecf20Sopenharmony_cimodule_param_named(pcie_caps, hfi1_pcie_caps, int, 0444); 4568c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pcie_caps, "Max PCIe tuning: Payload (0..3), ReadReq (4..7)"); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci/** 4598c2ecf20Sopenharmony_ci * tune_pcie_caps() - Code to adjust PCIe capabilities. 4608c2ecf20Sopenharmony_ci * @dd: Valid device data structure 4618c2ecf20Sopenharmony_ci * 4628c2ecf20Sopenharmony_ci */ 4638c2ecf20Sopenharmony_civoid tune_pcie_caps(struct hfi1_devdata *dd) 4648c2ecf20Sopenharmony_ci{ 4658c2ecf20Sopenharmony_ci struct pci_dev *parent; 4668c2ecf20Sopenharmony_ci u16 rc_mpss, rc_mps, ep_mpss, ep_mps; 4678c2ecf20Sopenharmony_ci u16 rc_mrrs, ep_mrrs, max_mrrs, ectl; 4688c2ecf20Sopenharmony_ci int ret; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci /* 4718c2ecf20Sopenharmony_ci * Turn on extended tags in DevCtl in case the BIOS has turned it off 4728c2ecf20Sopenharmony_ci * to improve WFR SDMA bandwidth 4738c2ecf20Sopenharmony_ci */ 4748c2ecf20Sopenharmony_ci ret = pcie_capability_read_word(dd->pcidev, PCI_EXP_DEVCTL, &ectl); 4758c2ecf20Sopenharmony_ci if ((!ret) && !(ectl & PCI_EXP_DEVCTL_EXT_TAG)) { 4768c2ecf20Sopenharmony_ci dd_dev_info(dd, "Enabling PCIe extended tags\n"); 4778c2ecf20Sopenharmony_ci ectl |= PCI_EXP_DEVCTL_EXT_TAG; 4788c2ecf20Sopenharmony_ci ret = pcie_capability_write_word(dd->pcidev, 4798c2ecf20Sopenharmony_ci PCI_EXP_DEVCTL, ectl); 4808c2ecf20Sopenharmony_ci if (ret) 4818c2ecf20Sopenharmony_ci dd_dev_info(dd, "Unable to write to PCI config\n"); 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci /* Find out supported and configured values for parent (root) */ 4848c2ecf20Sopenharmony_ci parent = dd->pcidev->bus->self; 4858c2ecf20Sopenharmony_ci /* 4868c2ecf20Sopenharmony_ci * The driver cannot perform the tuning if it does not have 4878c2ecf20Sopenharmony_ci * access to the upstream component. 4888c2ecf20Sopenharmony_ci */ 4898c2ecf20Sopenharmony_ci if (!parent) { 4908c2ecf20Sopenharmony_ci dd_dev_info(dd, "Parent not found\n"); 4918c2ecf20Sopenharmony_ci return; 4928c2ecf20Sopenharmony_ci } 4938c2ecf20Sopenharmony_ci if (!pci_is_root_bus(parent->bus)) { 4948c2ecf20Sopenharmony_ci dd_dev_info(dd, "Parent not root\n"); 4958c2ecf20Sopenharmony_ci return; 4968c2ecf20Sopenharmony_ci } 4978c2ecf20Sopenharmony_ci if (!pci_is_pcie(parent)) { 4988c2ecf20Sopenharmony_ci dd_dev_info(dd, "Parent is not PCI Express capable\n"); 4998c2ecf20Sopenharmony_ci return; 5008c2ecf20Sopenharmony_ci } 5018c2ecf20Sopenharmony_ci if (!pci_is_pcie(dd->pcidev)) { 5028c2ecf20Sopenharmony_ci dd_dev_info(dd, "PCI device is not PCI Express capable\n"); 5038c2ecf20Sopenharmony_ci return; 5048c2ecf20Sopenharmony_ci } 5058c2ecf20Sopenharmony_ci rc_mpss = parent->pcie_mpss; 5068c2ecf20Sopenharmony_ci rc_mps = ffs(pcie_get_mps(parent)) - 8; 5078c2ecf20Sopenharmony_ci /* Find out supported and configured values for endpoint (us) */ 5088c2ecf20Sopenharmony_ci ep_mpss = dd->pcidev->pcie_mpss; 5098c2ecf20Sopenharmony_ci ep_mps = ffs(pcie_get_mps(dd->pcidev)) - 8; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci /* Find max payload supported by root, endpoint */ 5128c2ecf20Sopenharmony_ci if (rc_mpss > ep_mpss) 5138c2ecf20Sopenharmony_ci rc_mpss = ep_mpss; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci /* If Supported greater than limit in module param, limit it */ 5168c2ecf20Sopenharmony_ci if (rc_mpss > (hfi1_pcie_caps & 7)) 5178c2ecf20Sopenharmony_ci rc_mpss = hfi1_pcie_caps & 7; 5188c2ecf20Sopenharmony_ci /* If less than (allowed, supported), bump root payload */ 5198c2ecf20Sopenharmony_ci if (rc_mpss > rc_mps) { 5208c2ecf20Sopenharmony_ci rc_mps = rc_mpss; 5218c2ecf20Sopenharmony_ci pcie_set_mps(parent, 128 << rc_mps); 5228c2ecf20Sopenharmony_ci } 5238c2ecf20Sopenharmony_ci /* If less than (allowed, supported), bump endpoint payload */ 5248c2ecf20Sopenharmony_ci if (rc_mpss > ep_mps) { 5258c2ecf20Sopenharmony_ci ep_mps = rc_mpss; 5268c2ecf20Sopenharmony_ci pcie_set_mps(dd->pcidev, 128 << ep_mps); 5278c2ecf20Sopenharmony_ci } 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci /* 5308c2ecf20Sopenharmony_ci * Now the Read Request size. 5318c2ecf20Sopenharmony_ci * No field for max supported, but PCIe spec limits it to 4096, 5328c2ecf20Sopenharmony_ci * which is code '5' (log2(4096) - 7) 5338c2ecf20Sopenharmony_ci */ 5348c2ecf20Sopenharmony_ci max_mrrs = 5; 5358c2ecf20Sopenharmony_ci if (max_mrrs > ((hfi1_pcie_caps >> 4) & 7)) 5368c2ecf20Sopenharmony_ci max_mrrs = (hfi1_pcie_caps >> 4) & 7; 5378c2ecf20Sopenharmony_ci 5388c2ecf20Sopenharmony_ci max_mrrs = 128 << max_mrrs; 5398c2ecf20Sopenharmony_ci rc_mrrs = pcie_get_readrq(parent); 5408c2ecf20Sopenharmony_ci ep_mrrs = pcie_get_readrq(dd->pcidev); 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci if (max_mrrs > rc_mrrs) { 5438c2ecf20Sopenharmony_ci rc_mrrs = max_mrrs; 5448c2ecf20Sopenharmony_ci pcie_set_readrq(parent, rc_mrrs); 5458c2ecf20Sopenharmony_ci } 5468c2ecf20Sopenharmony_ci if (max_mrrs > ep_mrrs) { 5478c2ecf20Sopenharmony_ci ep_mrrs = max_mrrs; 5488c2ecf20Sopenharmony_ci pcie_set_readrq(dd->pcidev, ep_mrrs); 5498c2ecf20Sopenharmony_ci } 5508c2ecf20Sopenharmony_ci} 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci/* End of PCIe capability tuning */ 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci/* 5558c2ecf20Sopenharmony_ci * From here through hfi1_pci_err_handler definition is invoked via 5568c2ecf20Sopenharmony_ci * PCI error infrastructure, registered via pci 5578c2ecf20Sopenharmony_ci */ 5588c2ecf20Sopenharmony_cistatic pci_ers_result_t 5598c2ecf20Sopenharmony_cipci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) 5608c2ecf20Sopenharmony_ci{ 5618c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = pci_get_drvdata(pdev); 5628c2ecf20Sopenharmony_ci pci_ers_result_t ret = PCI_ERS_RESULT_RECOVERED; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci switch (state) { 5658c2ecf20Sopenharmony_ci case pci_channel_io_normal: 5668c2ecf20Sopenharmony_ci dd_dev_info(dd, "State Normal, ignoring\n"); 5678c2ecf20Sopenharmony_ci break; 5688c2ecf20Sopenharmony_ci 5698c2ecf20Sopenharmony_ci case pci_channel_io_frozen: 5708c2ecf20Sopenharmony_ci dd_dev_info(dd, "State Frozen, requesting reset\n"); 5718c2ecf20Sopenharmony_ci pci_disable_device(pdev); 5728c2ecf20Sopenharmony_ci ret = PCI_ERS_RESULT_NEED_RESET; 5738c2ecf20Sopenharmony_ci break; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci case pci_channel_io_perm_failure: 5768c2ecf20Sopenharmony_ci if (dd) { 5778c2ecf20Sopenharmony_ci dd_dev_info(dd, "State Permanent Failure, disabling\n"); 5788c2ecf20Sopenharmony_ci /* no more register accesses! */ 5798c2ecf20Sopenharmony_ci dd->flags &= ~HFI1_PRESENT; 5808c2ecf20Sopenharmony_ci hfi1_disable_after_error(dd); 5818c2ecf20Sopenharmony_ci } 5828c2ecf20Sopenharmony_ci /* else early, or other problem */ 5838c2ecf20Sopenharmony_ci ret = PCI_ERS_RESULT_DISCONNECT; 5848c2ecf20Sopenharmony_ci break; 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci default: /* shouldn't happen */ 5878c2ecf20Sopenharmony_ci dd_dev_info(dd, "HFI1 PCI errors detected (state %d)\n", 5888c2ecf20Sopenharmony_ci state); 5898c2ecf20Sopenharmony_ci break; 5908c2ecf20Sopenharmony_ci } 5918c2ecf20Sopenharmony_ci return ret; 5928c2ecf20Sopenharmony_ci} 5938c2ecf20Sopenharmony_ci 5948c2ecf20Sopenharmony_cistatic pci_ers_result_t 5958c2ecf20Sopenharmony_cipci_mmio_enabled(struct pci_dev *pdev) 5968c2ecf20Sopenharmony_ci{ 5978c2ecf20Sopenharmony_ci u64 words = 0U; 5988c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = pci_get_drvdata(pdev); 5998c2ecf20Sopenharmony_ci pci_ers_result_t ret = PCI_ERS_RESULT_RECOVERED; 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci if (dd && dd->pport) { 6028c2ecf20Sopenharmony_ci words = read_port_cntr(dd->pport, C_RX_WORDS, CNTR_INVALID_VL); 6038c2ecf20Sopenharmony_ci if (words == ~0ULL) 6048c2ecf20Sopenharmony_ci ret = PCI_ERS_RESULT_NEED_RESET; 6058c2ecf20Sopenharmony_ci dd_dev_info(dd, 6068c2ecf20Sopenharmony_ci "HFI1 mmio_enabled function called, read wordscntr %llx, returning %d\n", 6078c2ecf20Sopenharmony_ci words, ret); 6088c2ecf20Sopenharmony_ci } 6098c2ecf20Sopenharmony_ci return ret; 6108c2ecf20Sopenharmony_ci} 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_cistatic pci_ers_result_t 6138c2ecf20Sopenharmony_cipci_slot_reset(struct pci_dev *pdev) 6148c2ecf20Sopenharmony_ci{ 6158c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = pci_get_drvdata(pdev); 6168c2ecf20Sopenharmony_ci 6178c2ecf20Sopenharmony_ci dd_dev_info(dd, "HFI1 slot_reset function called, ignored\n"); 6188c2ecf20Sopenharmony_ci return PCI_ERS_RESULT_CAN_RECOVER; 6198c2ecf20Sopenharmony_ci} 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_cistatic void 6228c2ecf20Sopenharmony_cipci_resume(struct pci_dev *pdev) 6238c2ecf20Sopenharmony_ci{ 6248c2ecf20Sopenharmony_ci struct hfi1_devdata *dd = pci_get_drvdata(pdev); 6258c2ecf20Sopenharmony_ci 6268c2ecf20Sopenharmony_ci dd_dev_info(dd, "HFI1 resume function called\n"); 6278c2ecf20Sopenharmony_ci /* 6288c2ecf20Sopenharmony_ci * Running jobs will fail, since it's asynchronous 6298c2ecf20Sopenharmony_ci * unlike sysfs-requested reset. Better than 6308c2ecf20Sopenharmony_ci * doing nothing. 6318c2ecf20Sopenharmony_ci */ 6328c2ecf20Sopenharmony_ci hfi1_init(dd, 1); /* same as re-init after reset */ 6338c2ecf20Sopenharmony_ci} 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ciconst struct pci_error_handlers hfi1_pci_err_handler = { 6368c2ecf20Sopenharmony_ci .error_detected = pci_error_detected, 6378c2ecf20Sopenharmony_ci .mmio_enabled = pci_mmio_enabled, 6388c2ecf20Sopenharmony_ci .slot_reset = pci_slot_reset, 6398c2ecf20Sopenharmony_ci .resume = pci_resume, 6408c2ecf20Sopenharmony_ci}; 6418c2ecf20Sopenharmony_ci 6428c2ecf20Sopenharmony_ci/*============================================================================*/ 6438c2ecf20Sopenharmony_ci/* PCIe Gen3 support */ 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci/* 6468c2ecf20Sopenharmony_ci * This code is separated out because it is expected to be removed in the 6478c2ecf20Sopenharmony_ci * final shipping product. If not, then it will be revisited and items 6488c2ecf20Sopenharmony_ci * will be moved to more standard locations. 6498c2ecf20Sopenharmony_ci */ 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci/* ASIC_PCI_SD_HOST_STATUS.FW_DNLD_STS field values */ 6528c2ecf20Sopenharmony_ci#define DL_STATUS_HFI0 0x1 /* hfi0 firmware download complete */ 6538c2ecf20Sopenharmony_ci#define DL_STATUS_HFI1 0x2 /* hfi1 firmware download complete */ 6548c2ecf20Sopenharmony_ci#define DL_STATUS_BOTH 0x3 /* hfi0 and hfi1 firmware download complete */ 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci/* ASIC_PCI_SD_HOST_STATUS.FW_DNLD_ERR field values */ 6578c2ecf20Sopenharmony_ci#define DL_ERR_NONE 0x0 /* no error */ 6588c2ecf20Sopenharmony_ci#define DL_ERR_SWAP_PARITY 0x1 /* parity error in SerDes interrupt */ 6598c2ecf20Sopenharmony_ci /* or response data */ 6608c2ecf20Sopenharmony_ci#define DL_ERR_DISABLED 0x2 /* hfi disabled */ 6618c2ecf20Sopenharmony_ci#define DL_ERR_SECURITY 0x3 /* security check failed */ 6628c2ecf20Sopenharmony_ci#define DL_ERR_SBUS 0x4 /* SBus status error */ 6638c2ecf20Sopenharmony_ci#define DL_ERR_XFR_PARITY 0x5 /* parity error during ROM transfer*/ 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci/* gasket block secondary bus reset delay */ 6668c2ecf20Sopenharmony_ci#define SBR_DELAY_US 200000 /* 200ms */ 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistatic uint pcie_target = 3; 6698c2ecf20Sopenharmony_cimodule_param(pcie_target, uint, S_IRUGO); 6708c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pcie_target, "PCIe target speed (0 skip, 1-3 Gen1-3)"); 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_cistatic uint pcie_force; 6738c2ecf20Sopenharmony_cimodule_param(pcie_force, uint, S_IRUGO); 6748c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pcie_force, "Force driver to do a PCIe firmware download even if already at target speed"); 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_cistatic uint pcie_retry = 5; 6778c2ecf20Sopenharmony_cimodule_param(pcie_retry, uint, S_IRUGO); 6788c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pcie_retry, "Driver will try this many times to reach requested speed"); 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci#define UNSET_PSET 255 6818c2ecf20Sopenharmony_ci#define DEFAULT_DISCRETE_PSET 2 /* discrete HFI */ 6828c2ecf20Sopenharmony_ci#define DEFAULT_MCP_PSET 6 /* MCP HFI */ 6838c2ecf20Sopenharmony_cistatic uint pcie_pset = UNSET_PSET; 6848c2ecf20Sopenharmony_cimodule_param(pcie_pset, uint, S_IRUGO); 6858c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pcie_pset, "PCIe Eq Pset value to use, range is 0-10"); 6868c2ecf20Sopenharmony_ci 6878c2ecf20Sopenharmony_cistatic uint pcie_ctle = 3; /* discrete on, integrated on */ 6888c2ecf20Sopenharmony_cimodule_param(pcie_ctle, uint, S_IRUGO); 6898c2ecf20Sopenharmony_ciMODULE_PARM_DESC(pcie_ctle, "PCIe static CTLE mode, bit 0 - discrete on/off, bit 1 - integrated on/off"); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci/* equalization columns */ 6928c2ecf20Sopenharmony_ci#define PREC 0 6938c2ecf20Sopenharmony_ci#define ATTN 1 6948c2ecf20Sopenharmony_ci#define POST 2 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci/* discrete silicon preliminary equalization values */ 6978c2ecf20Sopenharmony_cistatic const u8 discrete_preliminary_eq[11][3] = { 6988c2ecf20Sopenharmony_ci /* prec attn post */ 6998c2ecf20Sopenharmony_ci { 0x00, 0x00, 0x12 }, /* p0 */ 7008c2ecf20Sopenharmony_ci { 0x00, 0x00, 0x0c }, /* p1 */ 7018c2ecf20Sopenharmony_ci { 0x00, 0x00, 0x0f }, /* p2 */ 7028c2ecf20Sopenharmony_ci { 0x00, 0x00, 0x09 }, /* p3 */ 7038c2ecf20Sopenharmony_ci { 0x00, 0x00, 0x00 }, /* p4 */ 7048c2ecf20Sopenharmony_ci { 0x06, 0x00, 0x00 }, /* p5 */ 7058c2ecf20Sopenharmony_ci { 0x09, 0x00, 0x00 }, /* p6 */ 7068c2ecf20Sopenharmony_ci { 0x06, 0x00, 0x0f }, /* p7 */ 7078c2ecf20Sopenharmony_ci { 0x09, 0x00, 0x09 }, /* p8 */ 7088c2ecf20Sopenharmony_ci { 0x0c, 0x00, 0x00 }, /* p9 */ 7098c2ecf20Sopenharmony_ci { 0x00, 0x00, 0x18 }, /* p10 */ 7108c2ecf20Sopenharmony_ci}; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci/* integrated silicon preliminary equalization values */ 7138c2ecf20Sopenharmony_cistatic const u8 integrated_preliminary_eq[11][3] = { 7148c2ecf20Sopenharmony_ci /* prec attn post */ 7158c2ecf20Sopenharmony_ci { 0x00, 0x1e, 0x07 }, /* p0 */ 7168c2ecf20Sopenharmony_ci { 0x00, 0x1e, 0x05 }, /* p1 */ 7178c2ecf20Sopenharmony_ci { 0x00, 0x1e, 0x06 }, /* p2 */ 7188c2ecf20Sopenharmony_ci { 0x00, 0x1e, 0x04 }, /* p3 */ 7198c2ecf20Sopenharmony_ci { 0x00, 0x1e, 0x00 }, /* p4 */ 7208c2ecf20Sopenharmony_ci { 0x03, 0x1e, 0x00 }, /* p5 */ 7218c2ecf20Sopenharmony_ci { 0x04, 0x1e, 0x00 }, /* p6 */ 7228c2ecf20Sopenharmony_ci { 0x03, 0x1e, 0x06 }, /* p7 */ 7238c2ecf20Sopenharmony_ci { 0x03, 0x1e, 0x04 }, /* p8 */ 7248c2ecf20Sopenharmony_ci { 0x05, 0x1e, 0x00 }, /* p9 */ 7258c2ecf20Sopenharmony_ci { 0x00, 0x1e, 0x0a }, /* p10 */ 7268c2ecf20Sopenharmony_ci}; 7278c2ecf20Sopenharmony_ci 7288c2ecf20Sopenharmony_cistatic const u8 discrete_ctle_tunings[11][4] = { 7298c2ecf20Sopenharmony_ci /* DC LF HF BW */ 7308c2ecf20Sopenharmony_ci { 0x48, 0x0b, 0x04, 0x04 }, /* p0 */ 7318c2ecf20Sopenharmony_ci { 0x60, 0x05, 0x0f, 0x0a }, /* p1 */ 7328c2ecf20Sopenharmony_ci { 0x50, 0x09, 0x06, 0x06 }, /* p2 */ 7338c2ecf20Sopenharmony_ci { 0x68, 0x05, 0x0f, 0x0a }, /* p3 */ 7348c2ecf20Sopenharmony_ci { 0x80, 0x05, 0x0f, 0x0a }, /* p4 */ 7358c2ecf20Sopenharmony_ci { 0x70, 0x05, 0x0f, 0x0a }, /* p5 */ 7368c2ecf20Sopenharmony_ci { 0x68, 0x05, 0x0f, 0x0a }, /* p6 */ 7378c2ecf20Sopenharmony_ci { 0x38, 0x0f, 0x00, 0x00 }, /* p7 */ 7388c2ecf20Sopenharmony_ci { 0x48, 0x09, 0x06, 0x06 }, /* p8 */ 7398c2ecf20Sopenharmony_ci { 0x60, 0x05, 0x0f, 0x0a }, /* p9 */ 7408c2ecf20Sopenharmony_ci { 0x38, 0x0f, 0x00, 0x00 }, /* p10 */ 7418c2ecf20Sopenharmony_ci}; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_cistatic const u8 integrated_ctle_tunings[11][4] = { 7448c2ecf20Sopenharmony_ci /* DC LF HF BW */ 7458c2ecf20Sopenharmony_ci { 0x38, 0x0f, 0x00, 0x00 }, /* p0 */ 7468c2ecf20Sopenharmony_ci { 0x38, 0x0f, 0x00, 0x00 }, /* p1 */ 7478c2ecf20Sopenharmony_ci { 0x38, 0x0f, 0x00, 0x00 }, /* p2 */ 7488c2ecf20Sopenharmony_ci { 0x38, 0x0f, 0x00, 0x00 }, /* p3 */ 7498c2ecf20Sopenharmony_ci { 0x58, 0x0a, 0x05, 0x05 }, /* p4 */ 7508c2ecf20Sopenharmony_ci { 0x48, 0x0a, 0x05, 0x05 }, /* p5 */ 7518c2ecf20Sopenharmony_ci { 0x40, 0x0a, 0x05, 0x05 }, /* p6 */ 7528c2ecf20Sopenharmony_ci { 0x38, 0x0f, 0x00, 0x00 }, /* p7 */ 7538c2ecf20Sopenharmony_ci { 0x38, 0x0f, 0x00, 0x00 }, /* p8 */ 7548c2ecf20Sopenharmony_ci { 0x38, 0x09, 0x06, 0x06 }, /* p9 */ 7558c2ecf20Sopenharmony_ci { 0x38, 0x0e, 0x01, 0x01 }, /* p10 */ 7568c2ecf20Sopenharmony_ci}; 7578c2ecf20Sopenharmony_ci 7588c2ecf20Sopenharmony_ci/* helper to format the value to write to hardware */ 7598c2ecf20Sopenharmony_ci#define eq_value(pre, curr, post) \ 7608c2ecf20Sopenharmony_ci ((((u32)(pre)) << \ 7618c2ecf20Sopenharmony_ci PCIE_CFG_REG_PL102_GEN3_EQ_PRE_CURSOR_PSET_SHIFT) \ 7628c2ecf20Sopenharmony_ci | (((u32)(curr)) << PCIE_CFG_REG_PL102_GEN3_EQ_CURSOR_PSET_SHIFT) \ 7638c2ecf20Sopenharmony_ci | (((u32)(post)) << \ 7648c2ecf20Sopenharmony_ci PCIE_CFG_REG_PL102_GEN3_EQ_POST_CURSOR_PSET_SHIFT)) 7658c2ecf20Sopenharmony_ci 7668c2ecf20Sopenharmony_ci/* 7678c2ecf20Sopenharmony_ci * Load the given EQ preset table into the PCIe hardware. 7688c2ecf20Sopenharmony_ci */ 7698c2ecf20Sopenharmony_cistatic int load_eq_table(struct hfi1_devdata *dd, const u8 eq[11][3], u8 fs, 7708c2ecf20Sopenharmony_ci u8 div) 7718c2ecf20Sopenharmony_ci{ 7728c2ecf20Sopenharmony_ci struct pci_dev *pdev = dd->pcidev; 7738c2ecf20Sopenharmony_ci u32 hit_error = 0; 7748c2ecf20Sopenharmony_ci u32 violation; 7758c2ecf20Sopenharmony_ci u32 i; 7768c2ecf20Sopenharmony_ci u8 c_minus1, c0, c_plus1; 7778c2ecf20Sopenharmony_ci int ret; 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_ci for (i = 0; i < 11; i++) { 7808c2ecf20Sopenharmony_ci /* set index */ 7818c2ecf20Sopenharmony_ci pci_write_config_dword(pdev, PCIE_CFG_REG_PL103, i); 7828c2ecf20Sopenharmony_ci /* write the value */ 7838c2ecf20Sopenharmony_ci c_minus1 = eq[i][PREC] / div; 7848c2ecf20Sopenharmony_ci c0 = fs - (eq[i][PREC] / div) - (eq[i][POST] / div); 7858c2ecf20Sopenharmony_ci c_plus1 = eq[i][POST] / div; 7868c2ecf20Sopenharmony_ci pci_write_config_dword(pdev, PCIE_CFG_REG_PL102, 7878c2ecf20Sopenharmony_ci eq_value(c_minus1, c0, c_plus1)); 7888c2ecf20Sopenharmony_ci /* check if these coefficients violate EQ rules */ 7898c2ecf20Sopenharmony_ci ret = pci_read_config_dword(dd->pcidev, 7908c2ecf20Sopenharmony_ci PCIE_CFG_REG_PL105, &violation); 7918c2ecf20Sopenharmony_ci if (ret) { 7928c2ecf20Sopenharmony_ci dd_dev_err(dd, "Unable to read from PCI config\n"); 7938c2ecf20Sopenharmony_ci hit_error = 1; 7948c2ecf20Sopenharmony_ci break; 7958c2ecf20Sopenharmony_ci } 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci if (violation 7988c2ecf20Sopenharmony_ci & PCIE_CFG_REG_PL105_GEN3_EQ_VIOLATE_COEF_RULES_SMASK){ 7998c2ecf20Sopenharmony_ci if (hit_error == 0) { 8008c2ecf20Sopenharmony_ci dd_dev_err(dd, 8018c2ecf20Sopenharmony_ci "Gen3 EQ Table Coefficient rule violations\n"); 8028c2ecf20Sopenharmony_ci dd_dev_err(dd, " prec attn post\n"); 8038c2ecf20Sopenharmony_ci } 8048c2ecf20Sopenharmony_ci dd_dev_err(dd, " p%02d: %02x %02x %02x\n", 8058c2ecf20Sopenharmony_ci i, (u32)eq[i][0], (u32)eq[i][1], 8068c2ecf20Sopenharmony_ci (u32)eq[i][2]); 8078c2ecf20Sopenharmony_ci dd_dev_err(dd, " %02x %02x %02x\n", 8088c2ecf20Sopenharmony_ci (u32)c_minus1, (u32)c0, (u32)c_plus1); 8098c2ecf20Sopenharmony_ci hit_error = 1; 8108c2ecf20Sopenharmony_ci } 8118c2ecf20Sopenharmony_ci } 8128c2ecf20Sopenharmony_ci if (hit_error) 8138c2ecf20Sopenharmony_ci return -EINVAL; 8148c2ecf20Sopenharmony_ci return 0; 8158c2ecf20Sopenharmony_ci} 8168c2ecf20Sopenharmony_ci 8178c2ecf20Sopenharmony_ci/* 8188c2ecf20Sopenharmony_ci * Steps to be done after the PCIe firmware is downloaded and 8198c2ecf20Sopenharmony_ci * before the SBR for the Pcie Gen3. 8208c2ecf20Sopenharmony_ci * The SBus resource is already being held. 8218c2ecf20Sopenharmony_ci */ 8228c2ecf20Sopenharmony_cistatic void pcie_post_steps(struct hfi1_devdata *dd) 8238c2ecf20Sopenharmony_ci{ 8248c2ecf20Sopenharmony_ci int i; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_ci set_sbus_fast_mode(dd); 8278c2ecf20Sopenharmony_ci /* 8288c2ecf20Sopenharmony_ci * Write to the PCIe PCSes to set the G3_LOCKED_NEXT bits to 1. 8298c2ecf20Sopenharmony_ci * This avoids a spurious framing error that can otherwise be 8308c2ecf20Sopenharmony_ci * generated by the MAC layer. 8318c2ecf20Sopenharmony_ci * 8328c2ecf20Sopenharmony_ci * Use individual addresses since no broadcast is set up. 8338c2ecf20Sopenharmony_ci */ 8348c2ecf20Sopenharmony_ci for (i = 0; i < NUM_PCIE_SERDES; i++) { 8358c2ecf20Sopenharmony_ci sbus_request(dd, pcie_pcs_addrs[dd->hfi1_id][i], 8368c2ecf20Sopenharmony_ci 0x03, WRITE_SBUS_RECEIVER, 0x00022132); 8378c2ecf20Sopenharmony_ci } 8388c2ecf20Sopenharmony_ci 8398c2ecf20Sopenharmony_ci clear_sbus_fast_mode(dd); 8408c2ecf20Sopenharmony_ci} 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci/* 8438c2ecf20Sopenharmony_ci * Trigger a secondary bus reset (SBR) on ourselves using our parent. 8448c2ecf20Sopenharmony_ci * 8458c2ecf20Sopenharmony_ci * Based on pci_parent_bus_reset() which is not exported by the 8468c2ecf20Sopenharmony_ci * kernel core. 8478c2ecf20Sopenharmony_ci */ 8488c2ecf20Sopenharmony_cistatic int trigger_sbr(struct hfi1_devdata *dd) 8498c2ecf20Sopenharmony_ci{ 8508c2ecf20Sopenharmony_ci struct pci_dev *dev = dd->pcidev; 8518c2ecf20Sopenharmony_ci struct pci_dev *pdev; 8528c2ecf20Sopenharmony_ci 8538c2ecf20Sopenharmony_ci /* need a parent */ 8548c2ecf20Sopenharmony_ci if (!dev->bus->self) { 8558c2ecf20Sopenharmony_ci dd_dev_err(dd, "%s: no parent device\n", __func__); 8568c2ecf20Sopenharmony_ci return -ENOTTY; 8578c2ecf20Sopenharmony_ci } 8588c2ecf20Sopenharmony_ci 8598c2ecf20Sopenharmony_ci /* should not be anyone else on the bus */ 8608c2ecf20Sopenharmony_ci list_for_each_entry(pdev, &dev->bus->devices, bus_list) 8618c2ecf20Sopenharmony_ci if (pdev != dev) { 8628c2ecf20Sopenharmony_ci dd_dev_err(dd, 8638c2ecf20Sopenharmony_ci "%s: another device is on the same bus\n", 8648c2ecf20Sopenharmony_ci __func__); 8658c2ecf20Sopenharmony_ci return -ENOTTY; 8668c2ecf20Sopenharmony_ci } 8678c2ecf20Sopenharmony_ci 8688c2ecf20Sopenharmony_ci /* 8698c2ecf20Sopenharmony_ci * This is an end around to do an SBR during probe time. A new API needs 8708c2ecf20Sopenharmony_ci * to be implemented to have cleaner interface but this fixes the 8718c2ecf20Sopenharmony_ci * current brokenness 8728c2ecf20Sopenharmony_ci */ 8738c2ecf20Sopenharmony_ci return pci_bridge_secondary_bus_reset(dev->bus->self); 8748c2ecf20Sopenharmony_ci} 8758c2ecf20Sopenharmony_ci 8768c2ecf20Sopenharmony_ci/* 8778c2ecf20Sopenharmony_ci * Write the given gasket interrupt register. 8788c2ecf20Sopenharmony_ci */ 8798c2ecf20Sopenharmony_cistatic void write_gasket_interrupt(struct hfi1_devdata *dd, int index, 8808c2ecf20Sopenharmony_ci u16 code, u16 data) 8818c2ecf20Sopenharmony_ci{ 8828c2ecf20Sopenharmony_ci write_csr(dd, ASIC_PCIE_SD_INTRPT_LIST + (index * 8), 8838c2ecf20Sopenharmony_ci (((u64)code << ASIC_PCIE_SD_INTRPT_LIST_INTRPT_CODE_SHIFT) | 8848c2ecf20Sopenharmony_ci ((u64)data << ASIC_PCIE_SD_INTRPT_LIST_INTRPT_DATA_SHIFT))); 8858c2ecf20Sopenharmony_ci} 8868c2ecf20Sopenharmony_ci 8878c2ecf20Sopenharmony_ci/* 8888c2ecf20Sopenharmony_ci * Tell the gasket logic how to react to the reset. 8898c2ecf20Sopenharmony_ci */ 8908c2ecf20Sopenharmony_cistatic void arm_gasket_logic(struct hfi1_devdata *dd) 8918c2ecf20Sopenharmony_ci{ 8928c2ecf20Sopenharmony_ci u64 reg; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci reg = (((u64)1 << dd->hfi1_id) << 8958c2ecf20Sopenharmony_ci ASIC_PCIE_SD_HOST_CMD_INTRPT_CMD_SHIFT) | 8968c2ecf20Sopenharmony_ci ((u64)pcie_serdes_broadcast[dd->hfi1_id] << 8978c2ecf20Sopenharmony_ci ASIC_PCIE_SD_HOST_CMD_SBUS_RCVR_ADDR_SHIFT | 8988c2ecf20Sopenharmony_ci ASIC_PCIE_SD_HOST_CMD_SBR_MODE_SMASK | 8998c2ecf20Sopenharmony_ci ((u64)SBR_DELAY_US & ASIC_PCIE_SD_HOST_CMD_TIMER_MASK) << 9008c2ecf20Sopenharmony_ci ASIC_PCIE_SD_HOST_CMD_TIMER_SHIFT); 9018c2ecf20Sopenharmony_ci write_csr(dd, ASIC_PCIE_SD_HOST_CMD, reg); 9028c2ecf20Sopenharmony_ci /* read back to push the write */ 9038c2ecf20Sopenharmony_ci read_csr(dd, ASIC_PCIE_SD_HOST_CMD); 9048c2ecf20Sopenharmony_ci} 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci/* 9078c2ecf20Sopenharmony_ci * CCE_PCIE_CTRL long name helpers 9088c2ecf20Sopenharmony_ci * We redefine these shorter macros to use in the code while leaving 9098c2ecf20Sopenharmony_ci * chip_registers.h to be autogenerated from the hardware spec. 9108c2ecf20Sopenharmony_ci */ 9118c2ecf20Sopenharmony_ci#define LANE_BUNDLE_MASK CCE_PCIE_CTRL_PCIE_LANE_BUNDLE_MASK 9128c2ecf20Sopenharmony_ci#define LANE_BUNDLE_SHIFT CCE_PCIE_CTRL_PCIE_LANE_BUNDLE_SHIFT 9138c2ecf20Sopenharmony_ci#define LANE_DELAY_MASK CCE_PCIE_CTRL_PCIE_LANE_DELAY_MASK 9148c2ecf20Sopenharmony_ci#define LANE_DELAY_SHIFT CCE_PCIE_CTRL_PCIE_LANE_DELAY_SHIFT 9158c2ecf20Sopenharmony_ci#define MARGIN_OVERWRITE_ENABLE_SHIFT CCE_PCIE_CTRL_XMT_MARGIN_OVERWRITE_ENABLE_SHIFT 9168c2ecf20Sopenharmony_ci#define MARGIN_SHIFT CCE_PCIE_CTRL_XMT_MARGIN_SHIFT 9178c2ecf20Sopenharmony_ci#define MARGIN_G1_G2_OVERWRITE_MASK CCE_PCIE_CTRL_XMT_MARGIN_GEN1_GEN2_OVERWRITE_ENABLE_MASK 9188c2ecf20Sopenharmony_ci#define MARGIN_G1_G2_OVERWRITE_SHIFT CCE_PCIE_CTRL_XMT_MARGIN_GEN1_GEN2_OVERWRITE_ENABLE_SHIFT 9198c2ecf20Sopenharmony_ci#define MARGIN_GEN1_GEN2_MASK CCE_PCIE_CTRL_XMT_MARGIN_GEN1_GEN2_MASK 9208c2ecf20Sopenharmony_ci#define MARGIN_GEN1_GEN2_SHIFT CCE_PCIE_CTRL_XMT_MARGIN_GEN1_GEN2_SHIFT 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci /* 9238c2ecf20Sopenharmony_ci * Write xmt_margin for full-swing (WFR-B) or half-swing (WFR-C). 9248c2ecf20Sopenharmony_ci */ 9258c2ecf20Sopenharmony_cistatic void write_xmt_margin(struct hfi1_devdata *dd, const char *fname) 9268c2ecf20Sopenharmony_ci{ 9278c2ecf20Sopenharmony_ci u64 pcie_ctrl; 9288c2ecf20Sopenharmony_ci u64 xmt_margin; 9298c2ecf20Sopenharmony_ci u64 xmt_margin_oe; 9308c2ecf20Sopenharmony_ci u64 lane_delay; 9318c2ecf20Sopenharmony_ci u64 lane_bundle; 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci pcie_ctrl = read_csr(dd, CCE_PCIE_CTRL); 9348c2ecf20Sopenharmony_ci 9358c2ecf20Sopenharmony_ci /* 9368c2ecf20Sopenharmony_ci * For Discrete, use full-swing. 9378c2ecf20Sopenharmony_ci * - PCIe TX defaults to full-swing. 9388c2ecf20Sopenharmony_ci * Leave this register as default. 9398c2ecf20Sopenharmony_ci * For Integrated, use half-swing 9408c2ecf20Sopenharmony_ci * - Copy xmt_margin and xmt_margin_oe 9418c2ecf20Sopenharmony_ci * from Gen1/Gen2 to Gen3. 9428c2ecf20Sopenharmony_ci */ 9438c2ecf20Sopenharmony_ci if (dd->pcidev->device == PCI_DEVICE_ID_INTEL1) { /* integrated */ 9448c2ecf20Sopenharmony_ci /* extract initial fields */ 9458c2ecf20Sopenharmony_ci xmt_margin = (pcie_ctrl >> MARGIN_GEN1_GEN2_SHIFT) 9468c2ecf20Sopenharmony_ci & MARGIN_GEN1_GEN2_MASK; 9478c2ecf20Sopenharmony_ci xmt_margin_oe = (pcie_ctrl >> MARGIN_G1_G2_OVERWRITE_SHIFT) 9488c2ecf20Sopenharmony_ci & MARGIN_G1_G2_OVERWRITE_MASK; 9498c2ecf20Sopenharmony_ci lane_delay = (pcie_ctrl >> LANE_DELAY_SHIFT) & LANE_DELAY_MASK; 9508c2ecf20Sopenharmony_ci lane_bundle = (pcie_ctrl >> LANE_BUNDLE_SHIFT) 9518c2ecf20Sopenharmony_ci & LANE_BUNDLE_MASK; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci /* 9548c2ecf20Sopenharmony_ci * For A0, EFUSE values are not set. Override with the 9558c2ecf20Sopenharmony_ci * correct values. 9568c2ecf20Sopenharmony_ci */ 9578c2ecf20Sopenharmony_ci if (is_ax(dd)) { 9588c2ecf20Sopenharmony_ci /* 9598c2ecf20Sopenharmony_ci * xmt_margin and OverwiteEnabel should be the 9608c2ecf20Sopenharmony_ci * same for Gen1/Gen2 and Gen3 9618c2ecf20Sopenharmony_ci */ 9628c2ecf20Sopenharmony_ci xmt_margin = 0x5; 9638c2ecf20Sopenharmony_ci xmt_margin_oe = 0x1; 9648c2ecf20Sopenharmony_ci lane_delay = 0xF; /* Delay 240ns. */ 9658c2ecf20Sopenharmony_ci lane_bundle = 0x0; /* Set to 1 lane. */ 9668c2ecf20Sopenharmony_ci } 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci /* overwrite existing values */ 9698c2ecf20Sopenharmony_ci pcie_ctrl = (xmt_margin << MARGIN_GEN1_GEN2_SHIFT) 9708c2ecf20Sopenharmony_ci | (xmt_margin_oe << MARGIN_G1_G2_OVERWRITE_SHIFT) 9718c2ecf20Sopenharmony_ci | (xmt_margin << MARGIN_SHIFT) 9728c2ecf20Sopenharmony_ci | (xmt_margin_oe << MARGIN_OVERWRITE_ENABLE_SHIFT) 9738c2ecf20Sopenharmony_ci | (lane_delay << LANE_DELAY_SHIFT) 9748c2ecf20Sopenharmony_ci | (lane_bundle << LANE_BUNDLE_SHIFT); 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci write_csr(dd, CCE_PCIE_CTRL, pcie_ctrl); 9778c2ecf20Sopenharmony_ci } 9788c2ecf20Sopenharmony_ci 9798c2ecf20Sopenharmony_ci dd_dev_dbg(dd, "%s: program XMT margin, CcePcieCtrl 0x%llx\n", 9808c2ecf20Sopenharmony_ci fname, pcie_ctrl); 9818c2ecf20Sopenharmony_ci} 9828c2ecf20Sopenharmony_ci 9838c2ecf20Sopenharmony_ci/* 9848c2ecf20Sopenharmony_ci * Do all the steps needed to transition the PCIe link to Gen3 speed. 9858c2ecf20Sopenharmony_ci */ 9868c2ecf20Sopenharmony_ciint do_pcie_gen3_transition(struct hfi1_devdata *dd) 9878c2ecf20Sopenharmony_ci{ 9888c2ecf20Sopenharmony_ci struct pci_dev *parent = dd->pcidev->bus->self; 9898c2ecf20Sopenharmony_ci u64 fw_ctrl; 9908c2ecf20Sopenharmony_ci u64 reg, therm; 9918c2ecf20Sopenharmony_ci u32 reg32, fs, lf; 9928c2ecf20Sopenharmony_ci u32 status, err; 9938c2ecf20Sopenharmony_ci int ret; 9948c2ecf20Sopenharmony_ci int do_retry, retry_count = 0; 9958c2ecf20Sopenharmony_ci int intnum = 0; 9968c2ecf20Sopenharmony_ci uint default_pset; 9978c2ecf20Sopenharmony_ci uint pset = pcie_pset; 9988c2ecf20Sopenharmony_ci u16 target_vector, target_speed; 9998c2ecf20Sopenharmony_ci u16 lnkctl2, vendor; 10008c2ecf20Sopenharmony_ci u8 div; 10018c2ecf20Sopenharmony_ci const u8 (*eq)[3]; 10028c2ecf20Sopenharmony_ci const u8 (*ctle_tunings)[4]; 10038c2ecf20Sopenharmony_ci uint static_ctle_mode; 10048c2ecf20Sopenharmony_ci int return_error = 0; 10058c2ecf20Sopenharmony_ci u32 target_width; 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci /* PCIe Gen3 is for the ASIC only */ 10088c2ecf20Sopenharmony_ci if (dd->icode != ICODE_RTL_SILICON) 10098c2ecf20Sopenharmony_ci return 0; 10108c2ecf20Sopenharmony_ci 10118c2ecf20Sopenharmony_ci if (pcie_target == 1) { /* target Gen1 */ 10128c2ecf20Sopenharmony_ci target_vector = PCI_EXP_LNKCTL2_TLS_2_5GT; 10138c2ecf20Sopenharmony_ci target_speed = 2500; 10148c2ecf20Sopenharmony_ci } else if (pcie_target == 2) { /* target Gen2 */ 10158c2ecf20Sopenharmony_ci target_vector = PCI_EXP_LNKCTL2_TLS_5_0GT; 10168c2ecf20Sopenharmony_ci target_speed = 5000; 10178c2ecf20Sopenharmony_ci } else if (pcie_target == 3) { /* target Gen3 */ 10188c2ecf20Sopenharmony_ci target_vector = PCI_EXP_LNKCTL2_TLS_8_0GT; 10198c2ecf20Sopenharmony_ci target_speed = 8000; 10208c2ecf20Sopenharmony_ci } else { 10218c2ecf20Sopenharmony_ci /* off or invalid target - skip */ 10228c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: Skipping PCIe transition\n", __func__); 10238c2ecf20Sopenharmony_ci return 0; 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci /* if already at target speed, done (unless forced) */ 10278c2ecf20Sopenharmony_ci if (dd->lbus_speed == target_speed) { 10288c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: PCIe already at gen%d, %s\n", __func__, 10298c2ecf20Sopenharmony_ci pcie_target, 10308c2ecf20Sopenharmony_ci pcie_force ? "re-doing anyway" : "skipping"); 10318c2ecf20Sopenharmony_ci if (!pcie_force) 10328c2ecf20Sopenharmony_ci return 0; 10338c2ecf20Sopenharmony_ci } 10348c2ecf20Sopenharmony_ci 10358c2ecf20Sopenharmony_ci /* 10368c2ecf20Sopenharmony_ci * The driver cannot do the transition if it has no access to the 10378c2ecf20Sopenharmony_ci * upstream component 10388c2ecf20Sopenharmony_ci */ 10398c2ecf20Sopenharmony_ci if (!parent) { 10408c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: No upstream, Can't do gen3 transition\n", 10418c2ecf20Sopenharmony_ci __func__); 10428c2ecf20Sopenharmony_ci return 0; 10438c2ecf20Sopenharmony_ci } 10448c2ecf20Sopenharmony_ci 10458c2ecf20Sopenharmony_ci /* Previous Gen1/Gen2 bus width */ 10468c2ecf20Sopenharmony_ci target_width = dd->lbus_width; 10478c2ecf20Sopenharmony_ci 10488c2ecf20Sopenharmony_ci /* 10498c2ecf20Sopenharmony_ci * Do the Gen3 transition. Steps are those of the PCIe Gen3 10508c2ecf20Sopenharmony_ci * recipe. 10518c2ecf20Sopenharmony_ci */ 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci /* step 1: pcie link working in gen1/gen2 */ 10548c2ecf20Sopenharmony_ci 10558c2ecf20Sopenharmony_ci /* step 2: if either side is not capable of Gen3, done */ 10568c2ecf20Sopenharmony_ci if (pcie_target == 3 && !dd->link_gen3_capable) { 10578c2ecf20Sopenharmony_ci dd_dev_err(dd, "The PCIe link is not Gen3 capable\n"); 10588c2ecf20Sopenharmony_ci ret = -ENOSYS; 10598c2ecf20Sopenharmony_ci goto done_no_mutex; 10608c2ecf20Sopenharmony_ci } 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci /* hold the SBus resource across the firmware download and SBR */ 10638c2ecf20Sopenharmony_ci ret = acquire_chip_resource(dd, CR_SBUS, SBUS_TIMEOUT); 10648c2ecf20Sopenharmony_ci if (ret) { 10658c2ecf20Sopenharmony_ci dd_dev_err(dd, "%s: unable to acquire SBus resource\n", 10668c2ecf20Sopenharmony_ci __func__); 10678c2ecf20Sopenharmony_ci return ret; 10688c2ecf20Sopenharmony_ci } 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci /* make sure thermal polling is not causing interrupts */ 10718c2ecf20Sopenharmony_ci therm = read_csr(dd, ASIC_CFG_THERM_POLL_EN); 10728c2ecf20Sopenharmony_ci if (therm) { 10738c2ecf20Sopenharmony_ci write_csr(dd, ASIC_CFG_THERM_POLL_EN, 0x0); 10748c2ecf20Sopenharmony_ci msleep(100); 10758c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: Disabled therm polling\n", 10768c2ecf20Sopenharmony_ci __func__); 10778c2ecf20Sopenharmony_ci } 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ciretry: 10808c2ecf20Sopenharmony_ci /* the SBus download will reset the spico for thermal */ 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci /* step 3: download SBus Master firmware */ 10838c2ecf20Sopenharmony_ci /* step 4: download PCIe Gen3 SerDes firmware */ 10848c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: downloading firmware\n", __func__); 10858c2ecf20Sopenharmony_ci ret = load_pcie_firmware(dd); 10868c2ecf20Sopenharmony_ci if (ret) { 10878c2ecf20Sopenharmony_ci /* do not proceed if the firmware cannot be downloaded */ 10888c2ecf20Sopenharmony_ci return_error = 1; 10898c2ecf20Sopenharmony_ci goto done; 10908c2ecf20Sopenharmony_ci } 10918c2ecf20Sopenharmony_ci 10928c2ecf20Sopenharmony_ci /* step 5: set up device parameter settings */ 10938c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: setting PCIe registers\n", __func__); 10948c2ecf20Sopenharmony_ci 10958c2ecf20Sopenharmony_ci /* 10968c2ecf20Sopenharmony_ci * PcieCfgSpcie1 - Link Control 3 10978c2ecf20Sopenharmony_ci * Leave at reset value. No need to set PerfEq - link equalization 10988c2ecf20Sopenharmony_ci * will be performed automatically after the SBR when the target 10998c2ecf20Sopenharmony_ci * speed is 8GT/s. 11008c2ecf20Sopenharmony_ci */ 11018c2ecf20Sopenharmony_ci 11028c2ecf20Sopenharmony_ci /* clear all 16 per-lane error bits (PCIe: Lane Error Status) */ 11038c2ecf20Sopenharmony_ci pci_write_config_dword(dd->pcidev, PCIE_CFG_SPCIE2, 0xffff); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci /* step 5a: Set Synopsys Port Logic registers */ 11068c2ecf20Sopenharmony_ci 11078c2ecf20Sopenharmony_ci /* 11088c2ecf20Sopenharmony_ci * PcieCfgRegPl2 - Port Force Link 11098c2ecf20Sopenharmony_ci * 11108c2ecf20Sopenharmony_ci * Set the low power field to 0x10 to avoid unnecessary power 11118c2ecf20Sopenharmony_ci * management messages. All other fields are zero. 11128c2ecf20Sopenharmony_ci */ 11138c2ecf20Sopenharmony_ci reg32 = 0x10ul << PCIE_CFG_REG_PL2_LOW_PWR_ENT_CNT_SHIFT; 11148c2ecf20Sopenharmony_ci pci_write_config_dword(dd->pcidev, PCIE_CFG_REG_PL2, reg32); 11158c2ecf20Sopenharmony_ci 11168c2ecf20Sopenharmony_ci /* 11178c2ecf20Sopenharmony_ci * PcieCfgRegPl100 - Gen3 Control 11188c2ecf20Sopenharmony_ci * 11198c2ecf20Sopenharmony_ci * turn off PcieCfgRegPl100.Gen3ZRxDcNonCompl 11208c2ecf20Sopenharmony_ci * turn on PcieCfgRegPl100.EqEieosCnt 11218c2ecf20Sopenharmony_ci * Everything else zero. 11228c2ecf20Sopenharmony_ci */ 11238c2ecf20Sopenharmony_ci reg32 = PCIE_CFG_REG_PL100_EQ_EIEOS_CNT_SMASK; 11248c2ecf20Sopenharmony_ci pci_write_config_dword(dd->pcidev, PCIE_CFG_REG_PL100, reg32); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci /* 11278c2ecf20Sopenharmony_ci * PcieCfgRegPl101 - Gen3 EQ FS and LF 11288c2ecf20Sopenharmony_ci * PcieCfgRegPl102 - Gen3 EQ Presets to Coefficients Mapping 11298c2ecf20Sopenharmony_ci * PcieCfgRegPl103 - Gen3 EQ Preset Index 11308c2ecf20Sopenharmony_ci * PcieCfgRegPl105 - Gen3 EQ Status 11318c2ecf20Sopenharmony_ci * 11328c2ecf20Sopenharmony_ci * Give initial EQ settings. 11338c2ecf20Sopenharmony_ci */ 11348c2ecf20Sopenharmony_ci if (dd->pcidev->device == PCI_DEVICE_ID_INTEL0) { /* discrete */ 11358c2ecf20Sopenharmony_ci /* 1000mV, FS=24, LF = 8 */ 11368c2ecf20Sopenharmony_ci fs = 24; 11378c2ecf20Sopenharmony_ci lf = 8; 11388c2ecf20Sopenharmony_ci div = 3; 11398c2ecf20Sopenharmony_ci eq = discrete_preliminary_eq; 11408c2ecf20Sopenharmony_ci default_pset = DEFAULT_DISCRETE_PSET; 11418c2ecf20Sopenharmony_ci ctle_tunings = discrete_ctle_tunings; 11428c2ecf20Sopenharmony_ci /* bit 0 - discrete on/off */ 11438c2ecf20Sopenharmony_ci static_ctle_mode = pcie_ctle & 0x1; 11448c2ecf20Sopenharmony_ci } else { 11458c2ecf20Sopenharmony_ci /* 400mV, FS=29, LF = 9 */ 11468c2ecf20Sopenharmony_ci fs = 29; 11478c2ecf20Sopenharmony_ci lf = 9; 11488c2ecf20Sopenharmony_ci div = 1; 11498c2ecf20Sopenharmony_ci eq = integrated_preliminary_eq; 11508c2ecf20Sopenharmony_ci default_pset = DEFAULT_MCP_PSET; 11518c2ecf20Sopenharmony_ci ctle_tunings = integrated_ctle_tunings; 11528c2ecf20Sopenharmony_ci /* bit 1 - integrated on/off */ 11538c2ecf20Sopenharmony_ci static_ctle_mode = (pcie_ctle >> 1) & 0x1; 11548c2ecf20Sopenharmony_ci } 11558c2ecf20Sopenharmony_ci pci_write_config_dword(dd->pcidev, PCIE_CFG_REG_PL101, 11568c2ecf20Sopenharmony_ci (fs << 11578c2ecf20Sopenharmony_ci PCIE_CFG_REG_PL101_GEN3_EQ_LOCAL_FS_SHIFT) | 11588c2ecf20Sopenharmony_ci (lf << 11598c2ecf20Sopenharmony_ci PCIE_CFG_REG_PL101_GEN3_EQ_LOCAL_LF_SHIFT)); 11608c2ecf20Sopenharmony_ci ret = load_eq_table(dd, eq, fs, div); 11618c2ecf20Sopenharmony_ci if (ret) 11628c2ecf20Sopenharmony_ci goto done; 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci /* 11658c2ecf20Sopenharmony_ci * PcieCfgRegPl106 - Gen3 EQ Control 11668c2ecf20Sopenharmony_ci * 11678c2ecf20Sopenharmony_ci * Set Gen3EqPsetReqVec, leave other fields 0. 11688c2ecf20Sopenharmony_ci */ 11698c2ecf20Sopenharmony_ci if (pset == UNSET_PSET) 11708c2ecf20Sopenharmony_ci pset = default_pset; 11718c2ecf20Sopenharmony_ci if (pset > 10) { /* valid range is 0-10, inclusive */ 11728c2ecf20Sopenharmony_ci dd_dev_err(dd, "%s: Invalid Eq Pset %u, setting to %d\n", 11738c2ecf20Sopenharmony_ci __func__, pset, default_pset); 11748c2ecf20Sopenharmony_ci pset = default_pset; 11758c2ecf20Sopenharmony_ci } 11768c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: using EQ Pset %u\n", __func__, pset); 11778c2ecf20Sopenharmony_ci pci_write_config_dword(dd->pcidev, PCIE_CFG_REG_PL106, 11788c2ecf20Sopenharmony_ci ((1 << pset) << 11798c2ecf20Sopenharmony_ci PCIE_CFG_REG_PL106_GEN3_EQ_PSET_REQ_VEC_SHIFT) | 11808c2ecf20Sopenharmony_ci PCIE_CFG_REG_PL106_GEN3_EQ_EVAL2MS_DISABLE_SMASK | 11818c2ecf20Sopenharmony_ci PCIE_CFG_REG_PL106_GEN3_EQ_PHASE23_EXIT_MODE_SMASK); 11828c2ecf20Sopenharmony_ci 11838c2ecf20Sopenharmony_ci /* 11848c2ecf20Sopenharmony_ci * step 5b: Do post firmware download steps via SBus 11858c2ecf20Sopenharmony_ci */ 11868c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: doing pcie post steps\n", __func__); 11878c2ecf20Sopenharmony_ci pcie_post_steps(dd); 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci /* 11908c2ecf20Sopenharmony_ci * step 5c: Program gasket interrupts 11918c2ecf20Sopenharmony_ci */ 11928c2ecf20Sopenharmony_ci /* set the Rx Bit Rate to REFCLK ratio */ 11938c2ecf20Sopenharmony_ci write_gasket_interrupt(dd, intnum++, 0x0006, 0x0050); 11948c2ecf20Sopenharmony_ci /* disable pCal for PCIe Gen3 RX equalization */ 11958c2ecf20Sopenharmony_ci /* select adaptive or static CTLE */ 11968c2ecf20Sopenharmony_ci write_gasket_interrupt(dd, intnum++, 0x0026, 11978c2ecf20Sopenharmony_ci 0x5b01 | (static_ctle_mode << 3)); 11988c2ecf20Sopenharmony_ci /* 11998c2ecf20Sopenharmony_ci * Enable iCal for PCIe Gen3 RX equalization, and set which 12008c2ecf20Sopenharmony_ci * evaluation of RX_EQ_EVAL will launch the iCal procedure. 12018c2ecf20Sopenharmony_ci */ 12028c2ecf20Sopenharmony_ci write_gasket_interrupt(dd, intnum++, 0x0026, 0x5202); 12038c2ecf20Sopenharmony_ci 12048c2ecf20Sopenharmony_ci if (static_ctle_mode) { 12058c2ecf20Sopenharmony_ci /* apply static CTLE tunings */ 12068c2ecf20Sopenharmony_ci u8 pcie_dc, pcie_lf, pcie_hf, pcie_bw; 12078c2ecf20Sopenharmony_ci 12088c2ecf20Sopenharmony_ci pcie_dc = ctle_tunings[pset][0]; 12098c2ecf20Sopenharmony_ci pcie_lf = ctle_tunings[pset][1]; 12108c2ecf20Sopenharmony_ci pcie_hf = ctle_tunings[pset][2]; 12118c2ecf20Sopenharmony_ci pcie_bw = ctle_tunings[pset][3]; 12128c2ecf20Sopenharmony_ci write_gasket_interrupt(dd, intnum++, 0x0026, 0x0200 | pcie_dc); 12138c2ecf20Sopenharmony_ci write_gasket_interrupt(dd, intnum++, 0x0026, 0x0100 | pcie_lf); 12148c2ecf20Sopenharmony_ci write_gasket_interrupt(dd, intnum++, 0x0026, 0x0000 | pcie_hf); 12158c2ecf20Sopenharmony_ci write_gasket_interrupt(dd, intnum++, 0x0026, 0x5500 | pcie_bw); 12168c2ecf20Sopenharmony_ci } 12178c2ecf20Sopenharmony_ci 12188c2ecf20Sopenharmony_ci /* terminate list */ 12198c2ecf20Sopenharmony_ci write_gasket_interrupt(dd, intnum++, 0x0000, 0x0000); 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci /* 12228c2ecf20Sopenharmony_ci * step 5d: program XMT margin 12238c2ecf20Sopenharmony_ci */ 12248c2ecf20Sopenharmony_ci write_xmt_margin(dd, __func__); 12258c2ecf20Sopenharmony_ci 12268c2ecf20Sopenharmony_ci /* 12278c2ecf20Sopenharmony_ci * step 5e: disable active state power management (ASPM). It 12288c2ecf20Sopenharmony_ci * will be enabled if required later 12298c2ecf20Sopenharmony_ci */ 12308c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: clearing ASPM\n", __func__); 12318c2ecf20Sopenharmony_ci aspm_hw_disable_l1(dd); 12328c2ecf20Sopenharmony_ci 12338c2ecf20Sopenharmony_ci /* 12348c2ecf20Sopenharmony_ci * step 5f: clear DirectSpeedChange 12358c2ecf20Sopenharmony_ci * PcieCfgRegPl67.DirectSpeedChange must be zero to prevent the 12368c2ecf20Sopenharmony_ci * change in the speed target from starting before we are ready. 12378c2ecf20Sopenharmony_ci * This field defaults to 0 and we are not changing it, so nothing 12388c2ecf20Sopenharmony_ci * needs to be done. 12398c2ecf20Sopenharmony_ci */ 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci /* step 5g: Set target link speed */ 12428c2ecf20Sopenharmony_ci /* 12438c2ecf20Sopenharmony_ci * Set target link speed to be target on both device and parent. 12448c2ecf20Sopenharmony_ci * On setting the parent: Some system BIOSs "helpfully" set the 12458c2ecf20Sopenharmony_ci * parent target speed to Gen2 to match the ASIC's initial speed. 12468c2ecf20Sopenharmony_ci * We can set the target Gen3 because we have already checked 12478c2ecf20Sopenharmony_ci * that it is Gen3 capable earlier. 12488c2ecf20Sopenharmony_ci */ 12498c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: setting parent target link speed\n", __func__); 12508c2ecf20Sopenharmony_ci ret = pcie_capability_read_word(parent, PCI_EXP_LNKCTL2, &lnkctl2); 12518c2ecf20Sopenharmony_ci if (ret) { 12528c2ecf20Sopenharmony_ci dd_dev_err(dd, "Unable to read from PCI config\n"); 12538c2ecf20Sopenharmony_ci return_error = 1; 12548c2ecf20Sopenharmony_ci goto done; 12558c2ecf20Sopenharmony_ci } 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: ..old link control2: 0x%x\n", __func__, 12588c2ecf20Sopenharmony_ci (u32)lnkctl2); 12598c2ecf20Sopenharmony_ci /* only write to parent if target is not as high as ours */ 12608c2ecf20Sopenharmony_ci if ((lnkctl2 & PCI_EXP_LNKCTL2_TLS) < target_vector) { 12618c2ecf20Sopenharmony_ci lnkctl2 &= ~PCI_EXP_LNKCTL2_TLS; 12628c2ecf20Sopenharmony_ci lnkctl2 |= target_vector; 12638c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: ..new link control2: 0x%x\n", __func__, 12648c2ecf20Sopenharmony_ci (u32)lnkctl2); 12658c2ecf20Sopenharmony_ci ret = pcie_capability_write_word(parent, 12668c2ecf20Sopenharmony_ci PCI_EXP_LNKCTL2, lnkctl2); 12678c2ecf20Sopenharmony_ci if (ret) { 12688c2ecf20Sopenharmony_ci dd_dev_err(dd, "Unable to write to PCI config\n"); 12698c2ecf20Sopenharmony_ci return_error = 1; 12708c2ecf20Sopenharmony_ci goto done; 12718c2ecf20Sopenharmony_ci } 12728c2ecf20Sopenharmony_ci } else { 12738c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: ..target speed is OK\n", __func__); 12748c2ecf20Sopenharmony_ci } 12758c2ecf20Sopenharmony_ci 12768c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: setting target link speed\n", __func__); 12778c2ecf20Sopenharmony_ci ret = pcie_capability_read_word(dd->pcidev, PCI_EXP_LNKCTL2, &lnkctl2); 12788c2ecf20Sopenharmony_ci if (ret) { 12798c2ecf20Sopenharmony_ci dd_dev_err(dd, "Unable to read from PCI config\n"); 12808c2ecf20Sopenharmony_ci return_error = 1; 12818c2ecf20Sopenharmony_ci goto done; 12828c2ecf20Sopenharmony_ci } 12838c2ecf20Sopenharmony_ci 12848c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: ..old link control2: 0x%x\n", __func__, 12858c2ecf20Sopenharmony_ci (u32)lnkctl2); 12868c2ecf20Sopenharmony_ci lnkctl2 &= ~PCI_EXP_LNKCTL2_TLS; 12878c2ecf20Sopenharmony_ci lnkctl2 |= target_vector; 12888c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: ..new link control2: 0x%x\n", __func__, 12898c2ecf20Sopenharmony_ci (u32)lnkctl2); 12908c2ecf20Sopenharmony_ci ret = pcie_capability_write_word(dd->pcidev, PCI_EXP_LNKCTL2, lnkctl2); 12918c2ecf20Sopenharmony_ci if (ret) { 12928c2ecf20Sopenharmony_ci dd_dev_err(dd, "Unable to write to PCI config\n"); 12938c2ecf20Sopenharmony_ci return_error = 1; 12948c2ecf20Sopenharmony_ci goto done; 12958c2ecf20Sopenharmony_ci } 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci /* step 5h: arm gasket logic */ 12988c2ecf20Sopenharmony_ci /* hold DC in reset across the SBR */ 12998c2ecf20Sopenharmony_ci write_csr(dd, CCE_DC_CTRL, CCE_DC_CTRL_DC_RESET_SMASK); 13008c2ecf20Sopenharmony_ci (void)read_csr(dd, CCE_DC_CTRL); /* DC reset hold */ 13018c2ecf20Sopenharmony_ci /* save firmware control across the SBR */ 13028c2ecf20Sopenharmony_ci fw_ctrl = read_csr(dd, MISC_CFG_FW_CTRL); 13038c2ecf20Sopenharmony_ci 13048c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: arming gasket logic\n", __func__); 13058c2ecf20Sopenharmony_ci arm_gasket_logic(dd); 13068c2ecf20Sopenharmony_ci 13078c2ecf20Sopenharmony_ci /* 13088c2ecf20Sopenharmony_ci * step 6: quiesce PCIe link 13098c2ecf20Sopenharmony_ci * The chip has already been reset, so there will be no traffic 13108c2ecf20Sopenharmony_ci * from the chip. Linux has no easy way to enforce that it will 13118c2ecf20Sopenharmony_ci * not try to access the device, so we just need to hope it doesn't 13128c2ecf20Sopenharmony_ci * do it while we are doing the reset. 13138c2ecf20Sopenharmony_ci */ 13148c2ecf20Sopenharmony_ci 13158c2ecf20Sopenharmony_ci /* 13168c2ecf20Sopenharmony_ci * step 7: initiate the secondary bus reset (SBR) 13178c2ecf20Sopenharmony_ci * step 8: hardware brings the links back up 13188c2ecf20Sopenharmony_ci * step 9: wait for link speed transition to be complete 13198c2ecf20Sopenharmony_ci */ 13208c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: calling trigger_sbr\n", __func__); 13218c2ecf20Sopenharmony_ci ret = trigger_sbr(dd); 13228c2ecf20Sopenharmony_ci if (ret) 13238c2ecf20Sopenharmony_ci goto done; 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci /* step 10: decide what to do next */ 13268c2ecf20Sopenharmony_ci 13278c2ecf20Sopenharmony_ci /* check if we can read PCI space */ 13288c2ecf20Sopenharmony_ci ret = pci_read_config_word(dd->pcidev, PCI_VENDOR_ID, &vendor); 13298c2ecf20Sopenharmony_ci if (ret) { 13308c2ecf20Sopenharmony_ci dd_dev_info(dd, 13318c2ecf20Sopenharmony_ci "%s: read of VendorID failed after SBR, err %d\n", 13328c2ecf20Sopenharmony_ci __func__, ret); 13338c2ecf20Sopenharmony_ci return_error = 1; 13348c2ecf20Sopenharmony_ci goto done; 13358c2ecf20Sopenharmony_ci } 13368c2ecf20Sopenharmony_ci if (vendor == 0xffff) { 13378c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: VendorID is all 1s after SBR\n", __func__); 13388c2ecf20Sopenharmony_ci return_error = 1; 13398c2ecf20Sopenharmony_ci ret = -EIO; 13408c2ecf20Sopenharmony_ci goto done; 13418c2ecf20Sopenharmony_ci } 13428c2ecf20Sopenharmony_ci 13438c2ecf20Sopenharmony_ci /* restore PCI space registers we know were reset */ 13448c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: calling restore_pci_variables\n", __func__); 13458c2ecf20Sopenharmony_ci ret = restore_pci_variables(dd); 13468c2ecf20Sopenharmony_ci if (ret) { 13478c2ecf20Sopenharmony_ci dd_dev_err(dd, "%s: Could not restore PCI variables\n", 13488c2ecf20Sopenharmony_ci __func__); 13498c2ecf20Sopenharmony_ci return_error = 1; 13508c2ecf20Sopenharmony_ci goto done; 13518c2ecf20Sopenharmony_ci } 13528c2ecf20Sopenharmony_ci 13538c2ecf20Sopenharmony_ci /* restore firmware control */ 13548c2ecf20Sopenharmony_ci write_csr(dd, MISC_CFG_FW_CTRL, fw_ctrl); 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci /* 13578c2ecf20Sopenharmony_ci * Check the gasket block status. 13588c2ecf20Sopenharmony_ci * 13598c2ecf20Sopenharmony_ci * This is the first CSR read after the SBR. If the read returns 13608c2ecf20Sopenharmony_ci * all 1s (fails), the link did not make it back. 13618c2ecf20Sopenharmony_ci * 13628c2ecf20Sopenharmony_ci * Once we're sure we can read and write, clear the DC reset after 13638c2ecf20Sopenharmony_ci * the SBR. Then check for any per-lane errors. Then look over 13648c2ecf20Sopenharmony_ci * the status. 13658c2ecf20Sopenharmony_ci */ 13668c2ecf20Sopenharmony_ci reg = read_csr(dd, ASIC_PCIE_SD_HOST_STATUS); 13678c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: gasket block status: 0x%llx\n", __func__, reg); 13688c2ecf20Sopenharmony_ci if (reg == ~0ull) { /* PCIe read failed/timeout */ 13698c2ecf20Sopenharmony_ci dd_dev_err(dd, "SBR failed - unable to read from device\n"); 13708c2ecf20Sopenharmony_ci return_error = 1; 13718c2ecf20Sopenharmony_ci ret = -ENOSYS; 13728c2ecf20Sopenharmony_ci goto done; 13738c2ecf20Sopenharmony_ci } 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci /* clear the DC reset */ 13768c2ecf20Sopenharmony_ci write_csr(dd, CCE_DC_CTRL, 0); 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci /* Set the LED off */ 13798c2ecf20Sopenharmony_ci setextled(dd, 0); 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ci /* check for any per-lane errors */ 13828c2ecf20Sopenharmony_ci ret = pci_read_config_dword(dd->pcidev, PCIE_CFG_SPCIE2, ®32); 13838c2ecf20Sopenharmony_ci if (ret) { 13848c2ecf20Sopenharmony_ci dd_dev_err(dd, "Unable to read from PCI config\n"); 13858c2ecf20Sopenharmony_ci return_error = 1; 13868c2ecf20Sopenharmony_ci goto done; 13878c2ecf20Sopenharmony_ci } 13888c2ecf20Sopenharmony_ci 13898c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: per-lane errors: 0x%x\n", __func__, reg32); 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci /* extract status, look for our HFI */ 13928c2ecf20Sopenharmony_ci status = (reg >> ASIC_PCIE_SD_HOST_STATUS_FW_DNLD_STS_SHIFT) 13938c2ecf20Sopenharmony_ci & ASIC_PCIE_SD_HOST_STATUS_FW_DNLD_STS_MASK; 13948c2ecf20Sopenharmony_ci if ((status & (1 << dd->hfi1_id)) == 0) { 13958c2ecf20Sopenharmony_ci dd_dev_err(dd, 13968c2ecf20Sopenharmony_ci "%s: gasket status 0x%x, expecting 0x%x\n", 13978c2ecf20Sopenharmony_ci __func__, status, 1 << dd->hfi1_id); 13988c2ecf20Sopenharmony_ci ret = -EIO; 13998c2ecf20Sopenharmony_ci goto done; 14008c2ecf20Sopenharmony_ci } 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci /* extract error */ 14038c2ecf20Sopenharmony_ci err = (reg >> ASIC_PCIE_SD_HOST_STATUS_FW_DNLD_ERR_SHIFT) 14048c2ecf20Sopenharmony_ci & ASIC_PCIE_SD_HOST_STATUS_FW_DNLD_ERR_MASK; 14058c2ecf20Sopenharmony_ci if (err) { 14068c2ecf20Sopenharmony_ci dd_dev_err(dd, "%s: gasket error %d\n", __func__, err); 14078c2ecf20Sopenharmony_ci ret = -EIO; 14088c2ecf20Sopenharmony_ci goto done; 14098c2ecf20Sopenharmony_ci } 14108c2ecf20Sopenharmony_ci 14118c2ecf20Sopenharmony_ci /* update our link information cache */ 14128c2ecf20Sopenharmony_ci update_lbus_info(dd); 14138c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: new speed and width: %s\n", __func__, 14148c2ecf20Sopenharmony_ci dd->lbus_info); 14158c2ecf20Sopenharmony_ci 14168c2ecf20Sopenharmony_ci if (dd->lbus_speed != target_speed || 14178c2ecf20Sopenharmony_ci dd->lbus_width < target_width) { /* not target */ 14188c2ecf20Sopenharmony_ci /* maybe retry */ 14198c2ecf20Sopenharmony_ci do_retry = retry_count < pcie_retry; 14208c2ecf20Sopenharmony_ci dd_dev_err(dd, "PCIe link speed or width did not match target%s\n", 14218c2ecf20Sopenharmony_ci do_retry ? ", retrying" : ""); 14228c2ecf20Sopenharmony_ci retry_count++; 14238c2ecf20Sopenharmony_ci if (do_retry) { 14248c2ecf20Sopenharmony_ci msleep(100); /* allow time to settle */ 14258c2ecf20Sopenharmony_ci goto retry; 14268c2ecf20Sopenharmony_ci } 14278c2ecf20Sopenharmony_ci ret = -EIO; 14288c2ecf20Sopenharmony_ci } 14298c2ecf20Sopenharmony_ci 14308c2ecf20Sopenharmony_cidone: 14318c2ecf20Sopenharmony_ci if (therm) { 14328c2ecf20Sopenharmony_ci write_csr(dd, ASIC_CFG_THERM_POLL_EN, 0x1); 14338c2ecf20Sopenharmony_ci msleep(100); 14348c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: Re-enable therm polling\n", 14358c2ecf20Sopenharmony_ci __func__); 14368c2ecf20Sopenharmony_ci } 14378c2ecf20Sopenharmony_ci release_chip_resource(dd, CR_SBUS); 14388c2ecf20Sopenharmony_cidone_no_mutex: 14398c2ecf20Sopenharmony_ci /* return no error if it is OK to be at current speed */ 14408c2ecf20Sopenharmony_ci if (ret && !return_error) { 14418c2ecf20Sopenharmony_ci dd_dev_err(dd, "Proceeding at current speed PCIe speed\n"); 14428c2ecf20Sopenharmony_ci ret = 0; 14438c2ecf20Sopenharmony_ci } 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci dd_dev_info(dd, "%s: done\n", __func__); 14468c2ecf20Sopenharmony_ci return ret; 14478c2ecf20Sopenharmony_ci} 1448