1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright 2014-2016 IBM Corp. 4 */ 5 6 #include <linux/module.h> 7 #include <asm/pnv-pci.h> 8 #include <asm/opal.h> 9 10 #include "pci.h" 11 12 int pnv_phb_to_cxl_mode(struct pci_dev *dev, uint64_t mode) 13 { 14 struct pci_controller *hose = pci_bus_to_host(dev->bus); 15 struct pnv_phb *phb = hose->private_data; 16 struct pnv_ioda_pe *pe; 17 int rc; 18 19 pe = pnv_ioda_get_pe(dev); 20 if (!pe) 21 return -ENODEV; 22 23 pe_info(pe, "Switching PHB to CXL\n"); 24 25 rc = opal_pci_set_phb_cxl_mode(phb->opal_id, mode, pe->pe_number); 26 if (rc == OPAL_UNSUPPORTED) 27 dev_err(&dev->dev, "Required cxl mode not supported by firmware - update skiboot\n"); 28 else if (rc) 29 dev_err(&dev->dev, "opal_pci_set_phb_cxl_mode failed: %i\n", rc); 30 31 return rc; 32 } 33 EXPORT_SYMBOL(pnv_phb_to_cxl_mode); 34 35 /* Find PHB for cxl dev and allocate MSI hwirqs? 36 * Returns the absolute hardware IRQ number 37 */ 38 int pnv_cxl_alloc_hwirqs(struct pci_dev *dev, int num) 39 { 40 struct pci_controller *hose = pci_bus_to_host(dev->bus); 41 struct pnv_phb *phb = hose->private_data; 42 int hwirq = msi_bitmap_alloc_hwirqs(&phb->msi_bmp, num); 43 44 if (hwirq < 0) { 45 dev_warn(&dev->dev, "Failed to find a free MSI\n"); 46 return -ENOSPC; 47 } 48 49 return phb->msi_base + hwirq; 50 } 51 EXPORT_SYMBOL(pnv_cxl_alloc_hwirqs); 52 53 void pnv_cxl_release_hwirqs(struct pci_dev *dev, int hwirq, int num) 54 { 55 struct pci_controller *hose = pci_bus_to_host(dev->bus); 56 struct pnv_phb *phb = hose->private_data; 57 58 msi_bitmap_free_hwirqs(&phb->msi_bmp, hwirq - phb->msi_base, num); 59 } 60 EXPORT_SYMBOL(pnv_cxl_release_hwirqs); 61 62 void pnv_cxl_release_hwirq_ranges(struct cxl_irq_ranges *irqs, 63 struct pci_dev *dev) 64 { 65 struct pci_controller *hose = pci_bus_to_host(dev->bus); 66 struct pnv_phb *phb = hose->private_data; 67 int i, hwirq; 68 69 for (i = 1; i < CXL_IRQ_RANGES; i++) { 70 if (!irqs->range[i]) 71 continue; 72 pr_devel("cxl release irq range 0x%x: offset: 0x%lx limit: %ld\n", 73 i, irqs->offset[i], 74 irqs->range[i]); 75 hwirq = irqs->offset[i] - phb->msi_base; 76 msi_bitmap_free_hwirqs(&phb->msi_bmp, hwirq, 77 irqs->range[i]); 78 } 79 } 80 EXPORT_SYMBOL(pnv_cxl_release_hwirq_ranges); 81 82 int pnv_cxl_alloc_hwirq_ranges(struct cxl_irq_ranges *irqs, 83 struct pci_dev *dev, int num) 84 { 85 struct pci_controller *hose = pci_bus_to_host(dev->bus); 86 struct pnv_phb *phb = hose->private_data; 87 int i, hwirq, try; 88 89 memset(irqs, 0, sizeof(struct cxl_irq_ranges)); 90 91 /* 0 is reserved for the multiplexed PSL DSI interrupt */ 92 for (i = 1; i < CXL_IRQ_RANGES && num; i++) { 93 try = num; 94 while (try) { 95 hwirq = msi_bitmap_alloc_hwirqs(&phb->msi_bmp, try); 96 if (hwirq >= 0) 97 break; 98 try /= 2; 99 } 100 if (!try) 101 goto fail; 102 103 irqs->offset[i] = phb->msi_base + hwirq; 104 irqs->range[i] = try; 105 pr_devel("cxl alloc irq range 0x%x: offset: 0x%lx limit: %li\n", 106 i, irqs->offset[i], irqs->range[i]); 107 num -= try; 108 } 109 if (num) 110 goto fail; 111 112 return 0; 113 fail: 114 pnv_cxl_release_hwirq_ranges(irqs, dev); 115 return -ENOSPC; 116 } 117 EXPORT_SYMBOL(pnv_cxl_alloc_hwirq_ranges); 118 119 int pnv_cxl_get_irq_count(struct pci_dev *dev) 120 { 121 struct pci_controller *hose = pci_bus_to_host(dev->bus); 122 struct pnv_phb *phb = hose->private_data; 123 124 return phb->msi_bmp.irq_count; 125 } 126 EXPORT_SYMBOL(pnv_cxl_get_irq_count); 127 128 int pnv_cxl_ioda_msi_setup(struct pci_dev *dev, unsigned int hwirq, 129 unsigned int virq) 130 { 131 struct pci_controller *hose = pci_bus_to_host(dev->bus); 132 struct pnv_phb *phb = hose->private_data; 133 unsigned int xive_num = hwirq - phb->msi_base; 134 struct pnv_ioda_pe *pe; 135 int rc; 136 137 if (!(pe = pnv_ioda_get_pe(dev))) 138 return -ENODEV; 139 140 /* Assign XIVE to PE */ 141 rc = opal_pci_set_xive_pe(phb->opal_id, pe->pe_number, xive_num); 142 if (rc) { 143 pe_warn(pe, "%s: OPAL error %d setting msi_base 0x%x " 144 "hwirq 0x%x XIVE 0x%x PE\n", 145 pci_name(dev), rc, phb->msi_base, hwirq, xive_num); 146 return -EIO; 147 } 148 pnv_set_msi_irq_chip(phb, virq); 149 150 return 0; 151 } 152 EXPORT_SYMBOL(pnv_cxl_ioda_msi_setup); 153