18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright 2014-2016 IBM Corp. 48c2ecf20Sopenharmony_ci */ 58c2ecf20Sopenharmony_ci 68c2ecf20Sopenharmony_ci#include <linux/module.h> 78c2ecf20Sopenharmony_ci#include <asm/pnv-pci.h> 88c2ecf20Sopenharmony_ci#include <asm/opal.h> 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include "pci.h" 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ciint pnv_phb_to_cxl_mode(struct pci_dev *dev, uint64_t mode) 138c2ecf20Sopenharmony_ci{ 148c2ecf20Sopenharmony_ci struct pci_controller *hose = pci_bus_to_host(dev->bus); 158c2ecf20Sopenharmony_ci struct pnv_phb *phb = hose->private_data; 168c2ecf20Sopenharmony_ci struct pnv_ioda_pe *pe; 178c2ecf20Sopenharmony_ci int rc; 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci pe = pnv_ioda_get_pe(dev); 208c2ecf20Sopenharmony_ci if (!pe) 218c2ecf20Sopenharmony_ci return -ENODEV; 228c2ecf20Sopenharmony_ci 238c2ecf20Sopenharmony_ci pe_info(pe, "Switching PHB to CXL\n"); 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci rc = opal_pci_set_phb_cxl_mode(phb->opal_id, mode, pe->pe_number); 268c2ecf20Sopenharmony_ci if (rc == OPAL_UNSUPPORTED) 278c2ecf20Sopenharmony_ci dev_err(&dev->dev, "Required cxl mode not supported by firmware - update skiboot\n"); 288c2ecf20Sopenharmony_ci else if (rc) 298c2ecf20Sopenharmony_ci dev_err(&dev->dev, "opal_pci_set_phb_cxl_mode failed: %i\n", rc); 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci return rc; 328c2ecf20Sopenharmony_ci} 338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pnv_phb_to_cxl_mode); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci/* Find PHB for cxl dev and allocate MSI hwirqs? 368c2ecf20Sopenharmony_ci * Returns the absolute hardware IRQ number 378c2ecf20Sopenharmony_ci */ 388c2ecf20Sopenharmony_ciint pnv_cxl_alloc_hwirqs(struct pci_dev *dev, int num) 398c2ecf20Sopenharmony_ci{ 408c2ecf20Sopenharmony_ci struct pci_controller *hose = pci_bus_to_host(dev->bus); 418c2ecf20Sopenharmony_ci struct pnv_phb *phb = hose->private_data; 428c2ecf20Sopenharmony_ci int hwirq = msi_bitmap_alloc_hwirqs(&phb->msi_bmp, num); 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci if (hwirq < 0) { 458c2ecf20Sopenharmony_ci dev_warn(&dev->dev, "Failed to find a free MSI\n"); 468c2ecf20Sopenharmony_ci return -ENOSPC; 478c2ecf20Sopenharmony_ci } 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci return phb->msi_base + hwirq; 508c2ecf20Sopenharmony_ci} 518c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pnv_cxl_alloc_hwirqs); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_civoid pnv_cxl_release_hwirqs(struct pci_dev *dev, int hwirq, int num) 548c2ecf20Sopenharmony_ci{ 558c2ecf20Sopenharmony_ci struct pci_controller *hose = pci_bus_to_host(dev->bus); 568c2ecf20Sopenharmony_ci struct pnv_phb *phb = hose->private_data; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci msi_bitmap_free_hwirqs(&phb->msi_bmp, hwirq - phb->msi_base, num); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pnv_cxl_release_hwirqs); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_civoid pnv_cxl_release_hwirq_ranges(struct cxl_irq_ranges *irqs, 638c2ecf20Sopenharmony_ci struct pci_dev *dev) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci struct pci_controller *hose = pci_bus_to_host(dev->bus); 668c2ecf20Sopenharmony_ci struct pnv_phb *phb = hose->private_data; 678c2ecf20Sopenharmony_ci int i, hwirq; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci for (i = 1; i < CXL_IRQ_RANGES; i++) { 708c2ecf20Sopenharmony_ci if (!irqs->range[i]) 718c2ecf20Sopenharmony_ci continue; 728c2ecf20Sopenharmony_ci pr_devel("cxl release irq range 0x%x: offset: 0x%lx limit: %ld\n", 738c2ecf20Sopenharmony_ci i, irqs->offset[i], 748c2ecf20Sopenharmony_ci irqs->range[i]); 758c2ecf20Sopenharmony_ci hwirq = irqs->offset[i] - phb->msi_base; 768c2ecf20Sopenharmony_ci msi_bitmap_free_hwirqs(&phb->msi_bmp, hwirq, 778c2ecf20Sopenharmony_ci irqs->range[i]); 788c2ecf20Sopenharmony_ci } 798c2ecf20Sopenharmony_ci} 808c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pnv_cxl_release_hwirq_ranges); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_ciint pnv_cxl_alloc_hwirq_ranges(struct cxl_irq_ranges *irqs, 838c2ecf20Sopenharmony_ci struct pci_dev *dev, int num) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci struct pci_controller *hose = pci_bus_to_host(dev->bus); 868c2ecf20Sopenharmony_ci struct pnv_phb *phb = hose->private_data; 878c2ecf20Sopenharmony_ci int i, hwirq, try; 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_ci memset(irqs, 0, sizeof(struct cxl_irq_ranges)); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* 0 is reserved for the multiplexed PSL DSI interrupt */ 928c2ecf20Sopenharmony_ci for (i = 1; i < CXL_IRQ_RANGES && num; i++) { 938c2ecf20Sopenharmony_ci try = num; 948c2ecf20Sopenharmony_ci while (try) { 958c2ecf20Sopenharmony_ci hwirq = msi_bitmap_alloc_hwirqs(&phb->msi_bmp, try); 968c2ecf20Sopenharmony_ci if (hwirq >= 0) 978c2ecf20Sopenharmony_ci break; 988c2ecf20Sopenharmony_ci try /= 2; 998c2ecf20Sopenharmony_ci } 1008c2ecf20Sopenharmony_ci if (!try) 1018c2ecf20Sopenharmony_ci goto fail; 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci irqs->offset[i] = phb->msi_base + hwirq; 1048c2ecf20Sopenharmony_ci irqs->range[i] = try; 1058c2ecf20Sopenharmony_ci pr_devel("cxl alloc irq range 0x%x: offset: 0x%lx limit: %li\n", 1068c2ecf20Sopenharmony_ci i, irqs->offset[i], irqs->range[i]); 1078c2ecf20Sopenharmony_ci num -= try; 1088c2ecf20Sopenharmony_ci } 1098c2ecf20Sopenharmony_ci if (num) 1108c2ecf20Sopenharmony_ci goto fail; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_ci return 0; 1138c2ecf20Sopenharmony_cifail: 1148c2ecf20Sopenharmony_ci pnv_cxl_release_hwirq_ranges(irqs, dev); 1158c2ecf20Sopenharmony_ci return -ENOSPC; 1168c2ecf20Sopenharmony_ci} 1178c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pnv_cxl_alloc_hwirq_ranges); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ciint pnv_cxl_get_irq_count(struct pci_dev *dev) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct pci_controller *hose = pci_bus_to_host(dev->bus); 1228c2ecf20Sopenharmony_ci struct pnv_phb *phb = hose->private_data; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return phb->msi_bmp.irq_count; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pnv_cxl_get_irq_count); 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ciint pnv_cxl_ioda_msi_setup(struct pci_dev *dev, unsigned int hwirq, 1298c2ecf20Sopenharmony_ci unsigned int virq) 1308c2ecf20Sopenharmony_ci{ 1318c2ecf20Sopenharmony_ci struct pci_controller *hose = pci_bus_to_host(dev->bus); 1328c2ecf20Sopenharmony_ci struct pnv_phb *phb = hose->private_data; 1338c2ecf20Sopenharmony_ci unsigned int xive_num = hwirq - phb->msi_base; 1348c2ecf20Sopenharmony_ci struct pnv_ioda_pe *pe; 1358c2ecf20Sopenharmony_ci int rc; 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci if (!(pe = pnv_ioda_get_pe(dev))) 1388c2ecf20Sopenharmony_ci return -ENODEV; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci /* Assign XIVE to PE */ 1418c2ecf20Sopenharmony_ci rc = opal_pci_set_xive_pe(phb->opal_id, pe->pe_number, xive_num); 1428c2ecf20Sopenharmony_ci if (rc) { 1438c2ecf20Sopenharmony_ci pe_warn(pe, "%s: OPAL error %d setting msi_base 0x%x " 1448c2ecf20Sopenharmony_ci "hwirq 0x%x XIVE 0x%x PE\n", 1458c2ecf20Sopenharmony_ci pci_name(dev), rc, phb->msi_base, hwirq, xive_num); 1468c2ecf20Sopenharmony_ci return -EIO; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci pnv_set_msi_irq_chip(phb, virq); 1498c2ecf20Sopenharmony_ci 1508c2ecf20Sopenharmony_ci return 0; 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pnv_cxl_ioda_msi_setup); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci#if IS_MODULE(CONFIG_CXL) 1558c2ecf20Sopenharmony_cistatic inline int get_cxl_module(void) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci struct module *cxl_module; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci mutex_lock(&module_mutex); 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci cxl_module = find_module("cxl"); 1628c2ecf20Sopenharmony_ci if (cxl_module) 1638c2ecf20Sopenharmony_ci __module_get(cxl_module); 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_ci mutex_unlock(&module_mutex); 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci if (!cxl_module) 1688c2ecf20Sopenharmony_ci return -ENODEV; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci return 0; 1718c2ecf20Sopenharmony_ci} 1728c2ecf20Sopenharmony_ci#else 1738c2ecf20Sopenharmony_cistatic inline int get_cxl_module(void) { return 0; } 1748c2ecf20Sopenharmony_ci#endif 175