18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2016 Cavium, Inc. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/acpi.h> 78c2ecf20Sopenharmony_ci#include <linux/module.h> 88c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 98c2ecf20Sopenharmony_ci#include <linux/pci.h> 108c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 118c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 128c2ecf20Sopenharmony_ci#include <linux/phy.h> 138c2ecf20Sopenharmony_ci#include <linux/of.h> 148c2ecf20Sopenharmony_ci#include <linux/of_mdio.h> 158c2ecf20Sopenharmony_ci#include <linux/of_net.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#include "nic.h" 188c2ecf20Sopenharmony_ci#include "thunder_bgx.h" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci#define DRV_NAME "thunder_xcv" 218c2ecf20Sopenharmony_ci#define DRV_VERSION "1.0" 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci/* Register offsets */ 248c2ecf20Sopenharmony_ci#define XCV_RESET 0x00 258c2ecf20Sopenharmony_ci#define PORT_EN BIT_ULL(63) 268c2ecf20Sopenharmony_ci#define CLK_RESET BIT_ULL(15) 278c2ecf20Sopenharmony_ci#define DLL_RESET BIT_ULL(11) 288c2ecf20Sopenharmony_ci#define COMP_EN BIT_ULL(7) 298c2ecf20Sopenharmony_ci#define TX_PKT_RESET BIT_ULL(3) 308c2ecf20Sopenharmony_ci#define TX_DATA_RESET BIT_ULL(2) 318c2ecf20Sopenharmony_ci#define RX_PKT_RESET BIT_ULL(1) 328c2ecf20Sopenharmony_ci#define RX_DATA_RESET BIT_ULL(0) 338c2ecf20Sopenharmony_ci#define XCV_DLL_CTL 0x10 348c2ecf20Sopenharmony_ci#define CLKRX_BYP BIT_ULL(23) 358c2ecf20Sopenharmony_ci#define CLKTX_BYP BIT_ULL(15) 368c2ecf20Sopenharmony_ci#define XCV_COMP_CTL 0x20 378c2ecf20Sopenharmony_ci#define DRV_BYP BIT_ULL(63) 388c2ecf20Sopenharmony_ci#define XCV_CTL 0x30 398c2ecf20Sopenharmony_ci#define XCV_INT 0x40 408c2ecf20Sopenharmony_ci#define XCV_INT_W1S 0x48 418c2ecf20Sopenharmony_ci#define XCV_INT_ENA_W1C 0x50 428c2ecf20Sopenharmony_ci#define XCV_INT_ENA_W1S 0x58 438c2ecf20Sopenharmony_ci#define XCV_INBND_STATUS 0x80 448c2ecf20Sopenharmony_ci#define XCV_BATCH_CRD_RET 0x100 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_cistruct xcv { 478c2ecf20Sopenharmony_ci void __iomem *reg_base; 488c2ecf20Sopenharmony_ci struct pci_dev *pdev; 498c2ecf20Sopenharmony_ci}; 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic struct xcv *xcv; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci/* Supported devices */ 548c2ecf20Sopenharmony_cistatic const struct pci_device_id xcv_id_table[] = { 558c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, 0xA056) }, 568c2ecf20Sopenharmony_ci { 0, } /* end of table */ 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ciMODULE_AUTHOR("Cavium Inc"); 608c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Cavium Thunder RGX/XCV Driver"); 618c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 628c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 638c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, xcv_id_table); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_civoid xcv_init_hw(void) 668c2ecf20Sopenharmony_ci{ 678c2ecf20Sopenharmony_ci u64 cfg; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci /* Take DLL out of reset */ 708c2ecf20Sopenharmony_ci cfg = readq_relaxed(xcv->reg_base + XCV_RESET); 718c2ecf20Sopenharmony_ci cfg &= ~DLL_RESET; 728c2ecf20Sopenharmony_ci writeq_relaxed(cfg, xcv->reg_base + XCV_RESET); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci /* Take clock tree out of reset */ 758c2ecf20Sopenharmony_ci cfg = readq_relaxed(xcv->reg_base + XCV_RESET); 768c2ecf20Sopenharmony_ci cfg &= ~CLK_RESET; 778c2ecf20Sopenharmony_ci writeq_relaxed(cfg, xcv->reg_base + XCV_RESET); 788c2ecf20Sopenharmony_ci /* Wait for DLL to lock */ 798c2ecf20Sopenharmony_ci msleep(1); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* Configure DLL - enable or bypass 828c2ecf20Sopenharmony_ci * TX no bypass, RX bypass 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_ci cfg = readq_relaxed(xcv->reg_base + XCV_DLL_CTL); 858c2ecf20Sopenharmony_ci cfg &= ~0xFF03; 868c2ecf20Sopenharmony_ci cfg |= CLKRX_BYP; 878c2ecf20Sopenharmony_ci writeq_relaxed(cfg, xcv->reg_base + XCV_DLL_CTL); 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci /* Enable compensation controller and force the 908c2ecf20Sopenharmony_ci * write to be visible to HW by readig back. 918c2ecf20Sopenharmony_ci */ 928c2ecf20Sopenharmony_ci cfg = readq_relaxed(xcv->reg_base + XCV_RESET); 938c2ecf20Sopenharmony_ci cfg |= COMP_EN; 948c2ecf20Sopenharmony_ci writeq_relaxed(cfg, xcv->reg_base + XCV_RESET); 958c2ecf20Sopenharmony_ci readq_relaxed(xcv->reg_base + XCV_RESET); 968c2ecf20Sopenharmony_ci /* Wait for compensation state machine to lock */ 978c2ecf20Sopenharmony_ci msleep(10); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci /* enable the XCV block */ 1008c2ecf20Sopenharmony_ci cfg = readq_relaxed(xcv->reg_base + XCV_RESET); 1018c2ecf20Sopenharmony_ci cfg |= PORT_EN; 1028c2ecf20Sopenharmony_ci writeq_relaxed(cfg, xcv->reg_base + XCV_RESET); 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci cfg = readq_relaxed(xcv->reg_base + XCV_RESET); 1058c2ecf20Sopenharmony_ci cfg |= CLK_RESET; 1068c2ecf20Sopenharmony_ci writeq_relaxed(cfg, xcv->reg_base + XCV_RESET); 1078c2ecf20Sopenharmony_ci} 1088c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xcv_init_hw); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_civoid xcv_setup_link(bool link_up, int link_speed) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci u64 cfg; 1138c2ecf20Sopenharmony_ci int speed = 2; 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (!xcv) { 1168c2ecf20Sopenharmony_ci pr_err("XCV init not done, probe may have failed\n"); 1178c2ecf20Sopenharmony_ci return; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (link_speed == 100) 1218c2ecf20Sopenharmony_ci speed = 1; 1228c2ecf20Sopenharmony_ci else if (link_speed == 10) 1238c2ecf20Sopenharmony_ci speed = 0; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci if (link_up) { 1268c2ecf20Sopenharmony_ci /* set operating speed */ 1278c2ecf20Sopenharmony_ci cfg = readq_relaxed(xcv->reg_base + XCV_CTL); 1288c2ecf20Sopenharmony_ci cfg &= ~0x03; 1298c2ecf20Sopenharmony_ci cfg |= speed; 1308c2ecf20Sopenharmony_ci writeq_relaxed(cfg, xcv->reg_base + XCV_CTL); 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_ci /* Reset datapaths */ 1338c2ecf20Sopenharmony_ci cfg = readq_relaxed(xcv->reg_base + XCV_RESET); 1348c2ecf20Sopenharmony_ci cfg |= TX_DATA_RESET | RX_DATA_RESET; 1358c2ecf20Sopenharmony_ci writeq_relaxed(cfg, xcv->reg_base + XCV_RESET); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci /* Enable the packet flow */ 1388c2ecf20Sopenharmony_ci cfg = readq_relaxed(xcv->reg_base + XCV_RESET); 1398c2ecf20Sopenharmony_ci cfg |= TX_PKT_RESET | RX_PKT_RESET; 1408c2ecf20Sopenharmony_ci writeq_relaxed(cfg, xcv->reg_base + XCV_RESET); 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* Return credits to RGX */ 1438c2ecf20Sopenharmony_ci writeq_relaxed(0x01, xcv->reg_base + XCV_BATCH_CRD_RET); 1448c2ecf20Sopenharmony_ci } else { 1458c2ecf20Sopenharmony_ci /* Disable packet flow */ 1468c2ecf20Sopenharmony_ci cfg = readq_relaxed(xcv->reg_base + XCV_RESET); 1478c2ecf20Sopenharmony_ci cfg &= ~(TX_PKT_RESET | RX_PKT_RESET); 1488c2ecf20Sopenharmony_ci writeq_relaxed(cfg, xcv->reg_base + XCV_RESET); 1498c2ecf20Sopenharmony_ci readq_relaxed(xcv->reg_base + XCV_RESET); 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(xcv_setup_link); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int xcv_probe(struct pci_dev *pdev, const struct pci_device_id *ent) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci int err; 1578c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci xcv = devm_kzalloc(dev, sizeof(struct xcv), GFP_KERNEL); 1608c2ecf20Sopenharmony_ci if (!xcv) 1618c2ecf20Sopenharmony_ci return -ENOMEM; 1628c2ecf20Sopenharmony_ci xcv->pdev = pdev; 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, xcv); 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci err = pci_enable_device(pdev); 1678c2ecf20Sopenharmony_ci if (err) { 1688c2ecf20Sopenharmony_ci dev_err(dev, "Failed to enable PCI device\n"); 1698c2ecf20Sopenharmony_ci goto err_kfree; 1708c2ecf20Sopenharmony_ci } 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci err = pci_request_regions(pdev, DRV_NAME); 1738c2ecf20Sopenharmony_ci if (err) { 1748c2ecf20Sopenharmony_ci dev_err(dev, "PCI request regions failed 0x%x\n", err); 1758c2ecf20Sopenharmony_ci goto err_disable_device; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* MAP configuration registers */ 1798c2ecf20Sopenharmony_ci xcv->reg_base = pcim_iomap(pdev, PCI_CFG_REG_BAR_NUM, 0); 1808c2ecf20Sopenharmony_ci if (!xcv->reg_base) { 1818c2ecf20Sopenharmony_ci dev_err(dev, "XCV: Cannot map CSR memory space, aborting\n"); 1828c2ecf20Sopenharmony_ci err = -ENOMEM; 1838c2ecf20Sopenharmony_ci goto err_release_regions; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci return 0; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_cierr_release_regions: 1898c2ecf20Sopenharmony_ci pci_release_regions(pdev); 1908c2ecf20Sopenharmony_cierr_disable_device: 1918c2ecf20Sopenharmony_ci pci_disable_device(pdev); 1928c2ecf20Sopenharmony_cierr_kfree: 1938c2ecf20Sopenharmony_ci devm_kfree(dev, xcv); 1948c2ecf20Sopenharmony_ci xcv = NULL; 1958c2ecf20Sopenharmony_ci return err; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic void xcv_remove(struct pci_dev *pdev) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci if (xcv) { 2038c2ecf20Sopenharmony_ci devm_kfree(dev, xcv); 2048c2ecf20Sopenharmony_ci xcv = NULL; 2058c2ecf20Sopenharmony_ci } 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_ci pci_release_regions(pdev); 2088c2ecf20Sopenharmony_ci pci_disable_device(pdev); 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic struct pci_driver xcv_driver = { 2128c2ecf20Sopenharmony_ci .name = DRV_NAME, 2138c2ecf20Sopenharmony_ci .id_table = xcv_id_table, 2148c2ecf20Sopenharmony_ci .probe = xcv_probe, 2158c2ecf20Sopenharmony_ci .remove = xcv_remove, 2168c2ecf20Sopenharmony_ci}; 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic int __init xcv_init_module(void) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci pr_info("%s, ver %s\n", DRV_NAME, DRV_VERSION); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return pci_register_driver(&xcv_driver); 2238c2ecf20Sopenharmony_ci} 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_cistatic void __exit xcv_cleanup_module(void) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci pci_unregister_driver(&xcv_driver); 2288c2ecf20Sopenharmony_ci} 2298c2ecf20Sopenharmony_ci 2308c2ecf20Sopenharmony_cimodule_init(xcv_init_module); 2318c2ecf20Sopenharmony_cimodule_exit(xcv_cleanup_module); 232