18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* Marvell OcteonTX CPT driver 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2019 Marvell International Ltd. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This program is free software; you can redistribute it and/or modify 78c2ecf20Sopenharmony_ci * it under the terms of the GNU General Public License version 2 as 88c2ecf20Sopenharmony_ci * published by the Free Software Foundation. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci 118c2ecf20Sopenharmony_ci#include "otx_cpt_common.h" 128c2ecf20Sopenharmony_ci#include "otx_cptpf.h" 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define DRV_NAME "octeontx-cpt" 158c2ecf20Sopenharmony_ci#define DRV_VERSION "1.0" 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_cistatic void otx_cpt_disable_mbox_interrupts(struct otx_cpt_device *cpt) 188c2ecf20Sopenharmony_ci{ 198c2ecf20Sopenharmony_ci /* Disable mbox(0) interrupts for all VFs */ 208c2ecf20Sopenharmony_ci writeq(~0ull, cpt->reg_base + OTX_CPT_PF_MBOX_ENA_W1CX(0)); 218c2ecf20Sopenharmony_ci} 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_cistatic void otx_cpt_enable_mbox_interrupts(struct otx_cpt_device *cpt) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci /* Enable mbox(0) interrupts for all VFs */ 268c2ecf20Sopenharmony_ci writeq(~0ull, cpt->reg_base + OTX_CPT_PF_MBOX_ENA_W1SX(0)); 278c2ecf20Sopenharmony_ci} 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic irqreturn_t otx_cpt_mbx0_intr_handler(int __always_unused irq, 308c2ecf20Sopenharmony_ci void *cpt) 318c2ecf20Sopenharmony_ci{ 328c2ecf20Sopenharmony_ci otx_cpt_mbox_intr_handler(cpt, 0); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci return IRQ_HANDLED; 358c2ecf20Sopenharmony_ci} 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_cistatic void otx_cpt_reset(struct otx_cpt_device *cpt) 388c2ecf20Sopenharmony_ci{ 398c2ecf20Sopenharmony_ci writeq(1, cpt->reg_base + OTX_CPT_PF_RESET); 408c2ecf20Sopenharmony_ci} 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_cistatic void otx_cpt_find_max_enabled_cores(struct otx_cpt_device *cpt) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci union otx_cptx_pf_constants pf_cnsts = {0}; 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci pf_cnsts.u = readq(cpt->reg_base + OTX_CPT_PF_CONSTANTS); 478c2ecf20Sopenharmony_ci cpt->eng_grps.avail.max_se_cnt = pf_cnsts.s.se; 488c2ecf20Sopenharmony_ci cpt->eng_grps.avail.max_ae_cnt = pf_cnsts.s.ae; 498c2ecf20Sopenharmony_ci} 508c2ecf20Sopenharmony_ci 518c2ecf20Sopenharmony_cistatic u32 otx_cpt_check_bist_status(struct otx_cpt_device *cpt) 528c2ecf20Sopenharmony_ci{ 538c2ecf20Sopenharmony_ci union otx_cptx_pf_bist_status bist_sts = {0}; 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci bist_sts.u = readq(cpt->reg_base + OTX_CPT_PF_BIST_STATUS); 568c2ecf20Sopenharmony_ci return bist_sts.u; 578c2ecf20Sopenharmony_ci} 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic u64 otx_cpt_check_exe_bist_status(struct otx_cpt_device *cpt) 608c2ecf20Sopenharmony_ci{ 618c2ecf20Sopenharmony_ci union otx_cptx_pf_exe_bist_status bist_sts = {0}; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci bist_sts.u = readq(cpt->reg_base + OTX_CPT_PF_EXE_BIST_STATUS); 648c2ecf20Sopenharmony_ci return bist_sts.u; 658c2ecf20Sopenharmony_ci} 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_cistatic int otx_cpt_device_init(struct otx_cpt_device *cpt) 688c2ecf20Sopenharmony_ci{ 698c2ecf20Sopenharmony_ci struct device *dev = &cpt->pdev->dev; 708c2ecf20Sopenharmony_ci u16 sdevid; 718c2ecf20Sopenharmony_ci u64 bist; 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci /* Reset the PF when probed first */ 748c2ecf20Sopenharmony_ci otx_cpt_reset(cpt); 758c2ecf20Sopenharmony_ci mdelay(100); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci pci_read_config_word(cpt->pdev, PCI_SUBSYSTEM_ID, &sdevid); 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci /* Check BIST status */ 808c2ecf20Sopenharmony_ci bist = (u64)otx_cpt_check_bist_status(cpt); 818c2ecf20Sopenharmony_ci if (bist) { 828c2ecf20Sopenharmony_ci dev_err(dev, "RAM BIST failed with code 0x%llx\n", bist); 838c2ecf20Sopenharmony_ci return -ENODEV; 848c2ecf20Sopenharmony_ci } 858c2ecf20Sopenharmony_ci 868c2ecf20Sopenharmony_ci bist = otx_cpt_check_exe_bist_status(cpt); 878c2ecf20Sopenharmony_ci if (bist) { 888c2ecf20Sopenharmony_ci dev_err(dev, "Engine BIST failed with code 0x%llx\n", bist); 898c2ecf20Sopenharmony_ci return -ENODEV; 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* Get max enabled cores */ 938c2ecf20Sopenharmony_ci otx_cpt_find_max_enabled_cores(cpt); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci if ((sdevid == OTX_CPT_PCI_PF_SUBSYS_ID) && 968c2ecf20Sopenharmony_ci (cpt->eng_grps.avail.max_se_cnt == 0)) { 978c2ecf20Sopenharmony_ci cpt->pf_type = OTX_CPT_AE; 988c2ecf20Sopenharmony_ci } else if ((sdevid == OTX_CPT_PCI_PF_SUBSYS_ID) && 998c2ecf20Sopenharmony_ci (cpt->eng_grps.avail.max_ae_cnt == 0)) { 1008c2ecf20Sopenharmony_ci cpt->pf_type = OTX_CPT_SE; 1018c2ecf20Sopenharmony_ci } 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci /* Get max VQs/VFs supported by the device */ 1048c2ecf20Sopenharmony_ci cpt->max_vfs = pci_sriov_get_totalvfs(cpt->pdev); 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci /* Disable all cores */ 1078c2ecf20Sopenharmony_ci otx_cpt_disable_all_cores(cpt); 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return 0; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic int otx_cpt_register_interrupts(struct otx_cpt_device *cpt) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct device *dev = &cpt->pdev->dev; 1158c2ecf20Sopenharmony_ci u32 mbox_int_idx = OTX_CPT_PF_MBOX_INT; 1168c2ecf20Sopenharmony_ci u32 num_vec = OTX_CPT_PF_MSIX_VECTORS; 1178c2ecf20Sopenharmony_ci int ret; 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci /* Enable MSI-X */ 1208c2ecf20Sopenharmony_ci ret = pci_alloc_irq_vectors(cpt->pdev, num_vec, num_vec, PCI_IRQ_MSIX); 1218c2ecf20Sopenharmony_ci if (ret < 0) { 1228c2ecf20Sopenharmony_ci dev_err(&cpt->pdev->dev, 1238c2ecf20Sopenharmony_ci "Request for #%d msix vectors failed\n", 1248c2ecf20Sopenharmony_ci num_vec); 1258c2ecf20Sopenharmony_ci return ret; 1268c2ecf20Sopenharmony_ci } 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci /* Register mailbox interrupt handlers */ 1298c2ecf20Sopenharmony_ci ret = request_irq(pci_irq_vector(cpt->pdev, 1308c2ecf20Sopenharmony_ci OTX_CPT_PF_INT_VEC_E_MBOXX(mbox_int_idx, 0)), 1318c2ecf20Sopenharmony_ci otx_cpt_mbx0_intr_handler, 0, "CPT Mbox0", cpt); 1328c2ecf20Sopenharmony_ci if (ret) { 1338c2ecf20Sopenharmony_ci dev_err(dev, "Request irq failed\n"); 1348c2ecf20Sopenharmony_ci pci_free_irq_vectors(cpt->pdev); 1358c2ecf20Sopenharmony_ci return ret; 1368c2ecf20Sopenharmony_ci } 1378c2ecf20Sopenharmony_ci /* Enable mailbox interrupt */ 1388c2ecf20Sopenharmony_ci otx_cpt_enable_mbox_interrupts(cpt); 1398c2ecf20Sopenharmony_ci return 0; 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_cistatic void otx_cpt_unregister_interrupts(struct otx_cpt_device *cpt) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci u32 mbox_int_idx = OTX_CPT_PF_MBOX_INT; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci otx_cpt_disable_mbox_interrupts(cpt); 1478c2ecf20Sopenharmony_ci free_irq(pci_irq_vector(cpt->pdev, 1488c2ecf20Sopenharmony_ci OTX_CPT_PF_INT_VEC_E_MBOXX(mbox_int_idx, 0)), 1498c2ecf20Sopenharmony_ci cpt); 1508c2ecf20Sopenharmony_ci pci_free_irq_vectors(cpt->pdev); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_cistatic int otx_cpt_sriov_configure(struct pci_dev *pdev, int numvfs) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci struct otx_cpt_device *cpt = pci_get_drvdata(pdev); 1578c2ecf20Sopenharmony_ci int ret = 0; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (numvfs > cpt->max_vfs) 1608c2ecf20Sopenharmony_ci numvfs = cpt->max_vfs; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci if (numvfs > 0) { 1638c2ecf20Sopenharmony_ci ret = otx_cpt_try_create_default_eng_grps(cpt->pdev, 1648c2ecf20Sopenharmony_ci &cpt->eng_grps, 1658c2ecf20Sopenharmony_ci cpt->pf_type); 1668c2ecf20Sopenharmony_ci if (ret) 1678c2ecf20Sopenharmony_ci return ret; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci cpt->vfs_enabled = numvfs; 1708c2ecf20Sopenharmony_ci ret = pci_enable_sriov(pdev, numvfs); 1718c2ecf20Sopenharmony_ci if (ret) { 1728c2ecf20Sopenharmony_ci cpt->vfs_enabled = 0; 1738c2ecf20Sopenharmony_ci return ret; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci otx_cpt_set_eng_grps_is_rdonly(&cpt->eng_grps, true); 1768c2ecf20Sopenharmony_ci try_module_get(THIS_MODULE); 1778c2ecf20Sopenharmony_ci ret = numvfs; 1788c2ecf20Sopenharmony_ci } else { 1798c2ecf20Sopenharmony_ci pci_disable_sriov(pdev); 1808c2ecf20Sopenharmony_ci otx_cpt_set_eng_grps_is_rdonly(&cpt->eng_grps, false); 1818c2ecf20Sopenharmony_ci module_put(THIS_MODULE); 1828c2ecf20Sopenharmony_ci cpt->vfs_enabled = 0; 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci dev_notice(&cpt->pdev->dev, "VFs enabled: %d\n", ret); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci return ret; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic int otx_cpt_probe(struct pci_dev *pdev, 1908c2ecf20Sopenharmony_ci const struct pci_device_id __always_unused *ent) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 1938c2ecf20Sopenharmony_ci struct otx_cpt_device *cpt; 1948c2ecf20Sopenharmony_ci int err; 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_ci cpt = devm_kzalloc(dev, sizeof(*cpt), GFP_KERNEL); 1978c2ecf20Sopenharmony_ci if (!cpt) 1988c2ecf20Sopenharmony_ci return -ENOMEM; 1998c2ecf20Sopenharmony_ci 2008c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, cpt); 2018c2ecf20Sopenharmony_ci cpt->pdev = pdev; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci err = pci_enable_device(pdev); 2048c2ecf20Sopenharmony_ci if (err) { 2058c2ecf20Sopenharmony_ci dev_err(dev, "Failed to enable PCI device\n"); 2068c2ecf20Sopenharmony_ci goto err_clear_drvdata; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci err = pci_request_regions(pdev, DRV_NAME); 2108c2ecf20Sopenharmony_ci if (err) { 2118c2ecf20Sopenharmony_ci dev_err(dev, "PCI request regions failed 0x%x\n", err); 2128c2ecf20Sopenharmony_ci goto err_disable_device; 2138c2ecf20Sopenharmony_ci } 2148c2ecf20Sopenharmony_ci 2158c2ecf20Sopenharmony_ci err = pci_set_dma_mask(pdev, DMA_BIT_MASK(48)); 2168c2ecf20Sopenharmony_ci if (err) { 2178c2ecf20Sopenharmony_ci dev_err(dev, "Unable to get usable DMA configuration\n"); 2188c2ecf20Sopenharmony_ci goto err_release_regions; 2198c2ecf20Sopenharmony_ci } 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(48)); 2228c2ecf20Sopenharmony_ci if (err) { 2238c2ecf20Sopenharmony_ci dev_err(dev, "Unable to get 48-bit DMA for consistent allocations\n"); 2248c2ecf20Sopenharmony_ci goto err_release_regions; 2258c2ecf20Sopenharmony_ci } 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci /* MAP PF's configuration registers */ 2288c2ecf20Sopenharmony_ci cpt->reg_base = pci_iomap(pdev, OTX_CPT_PF_PCI_CFG_BAR, 0); 2298c2ecf20Sopenharmony_ci if (!cpt->reg_base) { 2308c2ecf20Sopenharmony_ci dev_err(dev, "Cannot map config register space, aborting\n"); 2318c2ecf20Sopenharmony_ci err = -ENOMEM; 2328c2ecf20Sopenharmony_ci goto err_release_regions; 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci /* CPT device HW initialization */ 2368c2ecf20Sopenharmony_ci err = otx_cpt_device_init(cpt); 2378c2ecf20Sopenharmony_ci if (err) 2388c2ecf20Sopenharmony_ci goto err_unmap_region; 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci /* Register interrupts */ 2418c2ecf20Sopenharmony_ci err = otx_cpt_register_interrupts(cpt); 2428c2ecf20Sopenharmony_ci if (err) 2438c2ecf20Sopenharmony_ci goto err_unmap_region; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci /* Initialize engine groups */ 2468c2ecf20Sopenharmony_ci err = otx_cpt_init_eng_grps(pdev, &cpt->eng_grps, cpt->pf_type); 2478c2ecf20Sopenharmony_ci if (err) 2488c2ecf20Sopenharmony_ci goto err_unregister_interrupts; 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci return 0; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_cierr_unregister_interrupts: 2538c2ecf20Sopenharmony_ci otx_cpt_unregister_interrupts(cpt); 2548c2ecf20Sopenharmony_cierr_unmap_region: 2558c2ecf20Sopenharmony_ci pci_iounmap(pdev, cpt->reg_base); 2568c2ecf20Sopenharmony_cierr_release_regions: 2578c2ecf20Sopenharmony_ci pci_release_regions(pdev); 2588c2ecf20Sopenharmony_cierr_disable_device: 2598c2ecf20Sopenharmony_ci pci_disable_device(pdev); 2608c2ecf20Sopenharmony_cierr_clear_drvdata: 2618c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, NULL); 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci return err; 2648c2ecf20Sopenharmony_ci} 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_cistatic void otx_cpt_remove(struct pci_dev *pdev) 2678c2ecf20Sopenharmony_ci{ 2688c2ecf20Sopenharmony_ci struct otx_cpt_device *cpt = pci_get_drvdata(pdev); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (!cpt) 2718c2ecf20Sopenharmony_ci return; 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci /* Disable VFs */ 2748c2ecf20Sopenharmony_ci pci_disable_sriov(pdev); 2758c2ecf20Sopenharmony_ci /* Cleanup engine groups */ 2768c2ecf20Sopenharmony_ci otx_cpt_cleanup_eng_grps(pdev, &cpt->eng_grps); 2778c2ecf20Sopenharmony_ci /* Disable CPT PF interrupts */ 2788c2ecf20Sopenharmony_ci otx_cpt_unregister_interrupts(cpt); 2798c2ecf20Sopenharmony_ci /* Disengage SE and AE cores from all groups */ 2808c2ecf20Sopenharmony_ci otx_cpt_disable_all_cores(cpt); 2818c2ecf20Sopenharmony_ci pci_iounmap(pdev, cpt->reg_base); 2828c2ecf20Sopenharmony_ci pci_release_regions(pdev); 2838c2ecf20Sopenharmony_ci pci_disable_device(pdev); 2848c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, NULL); 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_ci/* Supported devices */ 2888c2ecf20Sopenharmony_cistatic const struct pci_device_id otx_cpt_id_table[] = { 2898c2ecf20Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_CAVIUM, OTX_CPT_PCI_PF_DEVICE_ID) }, 2908c2ecf20Sopenharmony_ci { 0, } /* end of table */ 2918c2ecf20Sopenharmony_ci}; 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_cistatic struct pci_driver otx_cpt_pci_driver = { 2948c2ecf20Sopenharmony_ci .name = DRV_NAME, 2958c2ecf20Sopenharmony_ci .id_table = otx_cpt_id_table, 2968c2ecf20Sopenharmony_ci .probe = otx_cpt_probe, 2978c2ecf20Sopenharmony_ci .remove = otx_cpt_remove, 2988c2ecf20Sopenharmony_ci .sriov_configure = otx_cpt_sriov_configure 2998c2ecf20Sopenharmony_ci}; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_cimodule_pci_driver(otx_cpt_pci_driver); 3028c2ecf20Sopenharmony_ci 3038c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marvell International Ltd."); 3048c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Marvell OcteonTX CPT Physical Function Driver"); 3058c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 3068c2ecf20Sopenharmony_ciMODULE_VERSION(DRV_VERSION); 3078c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, otx_cpt_id_table); 308