Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Support PCI/PCIe on PowerNV platforms |
| 3 | * |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 4 | * Copyright 2011 Benjamin Herrenschmidt, IBM Corp. |
| 5 | * |
| 6 | * This program is free software; you can redistribute it and/or |
| 7 | * modify it under the terms of the GNU General Public License |
| 8 | * as published by the Free Software Foundation; either version |
| 9 | * 2 of the License, or (at your option) any later version. |
| 10 | */ |
| 11 | |
| 12 | #include <linux/kernel.h> |
| 13 | #include <linux/pci.h> |
| 14 | #include <linux/delay.h> |
| 15 | #include <linux/string.h> |
| 16 | #include <linux/init.h> |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 17 | #include <linux/irq.h> |
| 18 | #include <linux/io.h> |
Benjamin Herrenschmidt | c1a2562 | 2011-09-19 17:45:06 +0000 | [diff] [blame] | 19 | #include <linux/msi.h> |
Alexey Kardashevskiy | 4e13c1a | 2013-05-21 13:33:09 +1000 | [diff] [blame] | 20 | #include <linux/iommu.h> |
Philippe Bergheaud | d6a90bb | 2018-03-02 10:56:11 +0100 | [diff] [blame] | 21 | #include <linux/sched/mm.h> |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 22 | |
| 23 | #include <asm/sections.h> |
| 24 | #include <asm/io.h> |
| 25 | #include <asm/prom.h> |
| 26 | #include <asm/pci-bridge.h> |
| 27 | #include <asm/machdep.h> |
Gavin Shan | fb1b55d | 2013-03-05 21:12:37 +0000 | [diff] [blame] | 28 | #include <asm/msi_bitmap.h> |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 29 | #include <asm/ppc-pci.h> |
Gavin Shan | 7e19bf3 | 2016-05-20 16:41:40 +1000 | [diff] [blame] | 30 | #include <asm/pnv-pci.h> |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 31 | #include <asm/opal.h> |
| 32 | #include <asm/iommu.h> |
| 33 | #include <asm/tce.h> |
Stephen Rothwell | f533927 | 2012-03-15 18:18:00 +0000 | [diff] [blame] | 34 | #include <asm/firmware.h> |
Gavin Shan | be7e744 | 2013-06-20 13:21:15 +0800 | [diff] [blame] | 35 | #include <asm/eeh_event.h> |
| 36 | #include <asm/eeh.h> |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 37 | |
| 38 | #include "powernv.h" |
| 39 | #include "pci.h" |
| 40 | |
Frederic Barrat | 2552910 | 2017-08-04 11:55:14 +0200 | [diff] [blame] | 41 | static DEFINE_MUTEX(p2p_mutex); |
Philippe Bergheaud | d6a90bb | 2018-03-02 10:56:11 +0100 | [diff] [blame] | 42 | static DEFINE_MUTEX(tunnel_mutex); |
Frederic Barrat | 2552910 | 2017-08-04 11:55:14 +0200 | [diff] [blame] | 43 | |
Gavin Shan | 7e19bf3 | 2016-05-20 16:41:40 +1000 | [diff] [blame] | 44 | int pnv_pci_get_slot_id(struct device_node *np, uint64_t *id) |
| 45 | { |
| 46 | struct device_node *parent = np; |
| 47 | u32 bdfn; |
| 48 | u64 phbid; |
| 49 | int ret; |
| 50 | |
| 51 | ret = of_property_read_u32(np, "reg", &bdfn); |
| 52 | if (ret) |
| 53 | return -ENXIO; |
| 54 | |
| 55 | bdfn = ((bdfn & 0x00ffff00) >> 8); |
| 56 | while ((parent = of_get_parent(parent))) { |
| 57 | if (!PCI_DN(parent)) { |
| 58 | of_node_put(parent); |
| 59 | break; |
| 60 | } |
| 61 | |
| 62 | if (!of_device_is_compatible(parent, "ibm,ioda2-phb")) { |
| 63 | of_node_put(parent); |
| 64 | continue; |
| 65 | } |
| 66 | |
| 67 | ret = of_property_read_u64(parent, "ibm,opal-phbid", &phbid); |
| 68 | if (ret) { |
| 69 | of_node_put(parent); |
| 70 | return -ENXIO; |
| 71 | } |
| 72 | |
| 73 | *id = PCI_SLOT_ID(phbid, bdfn); |
| 74 | return 0; |
| 75 | } |
| 76 | |
| 77 | return -ENODEV; |
| 78 | } |
| 79 | EXPORT_SYMBOL_GPL(pnv_pci_get_slot_id); |
| 80 | |
Gavin Shan | ea0d856 | 2016-05-20 16:41:41 +1000 | [diff] [blame] | 81 | int pnv_pci_get_device_tree(uint32_t phandle, void *buf, uint64_t len) |
| 82 | { |
| 83 | int64_t rc; |
| 84 | |
| 85 | if (!opal_check_token(OPAL_GET_DEVICE_TREE)) |
| 86 | return -ENXIO; |
| 87 | |
| 88 | rc = opal_get_device_tree(phandle, (uint64_t)buf, len); |
| 89 | if (rc < OPAL_SUCCESS) |
| 90 | return -EIO; |
| 91 | |
| 92 | return rc; |
| 93 | } |
| 94 | EXPORT_SYMBOL_GPL(pnv_pci_get_device_tree); |
| 95 | |
| 96 | int pnv_pci_get_presence_state(uint64_t id, uint8_t *state) |
| 97 | { |
| 98 | int64_t rc; |
| 99 | |
| 100 | if (!opal_check_token(OPAL_PCI_GET_PRESENCE_STATE)) |
| 101 | return -ENXIO; |
| 102 | |
| 103 | rc = opal_pci_get_presence_state(id, (uint64_t)state); |
| 104 | if (rc != OPAL_SUCCESS) |
| 105 | return -EIO; |
| 106 | |
| 107 | return 0; |
| 108 | } |
| 109 | EXPORT_SYMBOL_GPL(pnv_pci_get_presence_state); |
| 110 | |
| 111 | int pnv_pci_get_power_state(uint64_t id, uint8_t *state) |
| 112 | { |
| 113 | int64_t rc; |
| 114 | |
| 115 | if (!opal_check_token(OPAL_PCI_GET_POWER_STATE)) |
| 116 | return -ENXIO; |
| 117 | |
| 118 | rc = opal_pci_get_power_state(id, (uint64_t)state); |
| 119 | if (rc != OPAL_SUCCESS) |
| 120 | return -EIO; |
| 121 | |
| 122 | return 0; |
| 123 | } |
| 124 | EXPORT_SYMBOL_GPL(pnv_pci_get_power_state); |
| 125 | |
| 126 | int pnv_pci_set_power_state(uint64_t id, uint8_t state, struct opal_msg *msg) |
| 127 | { |
| 128 | struct opal_msg m; |
| 129 | int token, ret; |
| 130 | int64_t rc; |
| 131 | |
| 132 | if (!opal_check_token(OPAL_PCI_SET_POWER_STATE)) |
| 133 | return -ENXIO; |
| 134 | |
| 135 | token = opal_async_get_token_interruptible(); |
| 136 | if (unlikely(token < 0)) |
| 137 | return token; |
| 138 | |
| 139 | rc = opal_pci_set_power_state(token, id, (uint64_t)&state); |
| 140 | if (rc == OPAL_SUCCESS) { |
| 141 | ret = 0; |
| 142 | goto exit; |
| 143 | } else if (rc != OPAL_ASYNC_COMPLETION) { |
| 144 | ret = -EIO; |
| 145 | goto exit; |
| 146 | } |
| 147 | |
| 148 | ret = opal_async_wait_response(token, &m); |
| 149 | if (ret < 0) |
| 150 | goto exit; |
| 151 | |
| 152 | if (msg) { |
| 153 | ret = 1; |
| 154 | memcpy(msg, &m, sizeof(m)); |
| 155 | } |
| 156 | |
| 157 | exit: |
| 158 | opal_async_release_token(token); |
| 159 | return ret; |
| 160 | } |
| 161 | EXPORT_SYMBOL_GPL(pnv_pci_set_power_state); |
| 162 | |
Daniel Axtens | 92ae035 | 2015-04-28 15:12:05 +1000 | [diff] [blame] | 163 | int pnv_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) |
Benjamin Herrenschmidt | c1a2562 | 2011-09-19 17:45:06 +0000 | [diff] [blame] | 164 | { |
| 165 | struct pci_controller *hose = pci_bus_to_host(pdev->bus); |
| 166 | struct pnv_phb *phb = hose->private_data; |
| 167 | struct msi_desc *entry; |
| 168 | struct msi_msg msg; |
Gavin Shan | fb1b55d | 2013-03-05 21:12:37 +0000 | [diff] [blame] | 169 | int hwirq; |
| 170 | unsigned int virq; |
Benjamin Herrenschmidt | c1a2562 | 2011-09-19 17:45:06 +0000 | [diff] [blame] | 171 | int rc; |
| 172 | |
Alexander Gordeev | 6b2fd7ef | 2014-09-07 20:57:53 +0200 | [diff] [blame] | 173 | if (WARN_ON(!phb) || !phb->msi_bmp.bitmap) |
| 174 | return -ENODEV; |
| 175 | |
Benjamin Herrenschmidt | 3607438 | 2014-10-07 16:12:36 +1100 | [diff] [blame] | 176 | if (pdev->no_64bit_msi && !phb->msi32_support) |
Benjamin Herrenschmidt | c1a2562 | 2011-09-19 17:45:06 +0000 | [diff] [blame] | 177 | return -ENODEV; |
| 178 | |
Jiang Liu | 2921d17 | 2015-07-09 16:00:38 +0800 | [diff] [blame] | 179 | for_each_pci_msi_entry(entry, pdev) { |
Benjamin Herrenschmidt | c1a2562 | 2011-09-19 17:45:06 +0000 | [diff] [blame] | 180 | if (!entry->msi_attrib.is_64 && !phb->msi32_support) { |
| 181 | pr_warn("%s: Supports only 64-bit MSIs\n", |
| 182 | pci_name(pdev)); |
| 183 | return -ENXIO; |
| 184 | } |
Gavin Shan | fb1b55d | 2013-03-05 21:12:37 +0000 | [diff] [blame] | 185 | hwirq = msi_bitmap_alloc_hwirqs(&phb->msi_bmp, 1); |
| 186 | if (hwirq < 0) { |
Benjamin Herrenschmidt | c1a2562 | 2011-09-19 17:45:06 +0000 | [diff] [blame] | 187 | pr_warn("%s: Failed to find a free MSI\n", |
| 188 | pci_name(pdev)); |
| 189 | return -ENOSPC; |
| 190 | } |
Gavin Shan | fb1b55d | 2013-03-05 21:12:37 +0000 | [diff] [blame] | 191 | virq = irq_create_mapping(NULL, phb->msi_base + hwirq); |
Michael Ellerman | ef24ba7 | 2016-09-06 21:53:24 +1000 | [diff] [blame] | 192 | if (!virq) { |
Benjamin Herrenschmidt | c1a2562 | 2011-09-19 17:45:06 +0000 | [diff] [blame] | 193 | pr_warn("%s: Failed to map MSI to linux irq\n", |
| 194 | pci_name(pdev)); |
Gavin Shan | fb1b55d | 2013-03-05 21:12:37 +0000 | [diff] [blame] | 195 | msi_bitmap_free_hwirqs(&phb->msi_bmp, hwirq, 1); |
Benjamin Herrenschmidt | c1a2562 | 2011-09-19 17:45:06 +0000 | [diff] [blame] | 196 | return -ENOMEM; |
| 197 | } |
Gavin Shan | fb1b55d | 2013-03-05 21:12:37 +0000 | [diff] [blame] | 198 | rc = phb->msi_setup(phb, pdev, phb->msi_base + hwirq, |
Gavin Shan | 137436c | 2013-04-25 19:20:59 +0000 | [diff] [blame] | 199 | virq, entry->msi_attrib.is_64, &msg); |
Benjamin Herrenschmidt | c1a2562 | 2011-09-19 17:45:06 +0000 | [diff] [blame] | 200 | if (rc) { |
| 201 | pr_warn("%s: Failed to setup MSI\n", pci_name(pdev)); |
| 202 | irq_dispose_mapping(virq); |
Gavin Shan | fb1b55d | 2013-03-05 21:12:37 +0000 | [diff] [blame] | 203 | msi_bitmap_free_hwirqs(&phb->msi_bmp, hwirq, 1); |
Benjamin Herrenschmidt | c1a2562 | 2011-09-19 17:45:06 +0000 | [diff] [blame] | 204 | return rc; |
| 205 | } |
| 206 | irq_set_msi_desc(virq, entry); |
Jiang Liu | 83a1891 | 2014-11-09 23:10:34 +0800 | [diff] [blame] | 207 | pci_write_msi_msg(virq, &msg); |
Benjamin Herrenschmidt | c1a2562 | 2011-09-19 17:45:06 +0000 | [diff] [blame] | 208 | } |
| 209 | return 0; |
| 210 | } |
| 211 | |
Daniel Axtens | 92ae035 | 2015-04-28 15:12:05 +1000 | [diff] [blame] | 212 | void pnv_teardown_msi_irqs(struct pci_dev *pdev) |
Benjamin Herrenschmidt | c1a2562 | 2011-09-19 17:45:06 +0000 | [diff] [blame] | 213 | { |
| 214 | struct pci_controller *hose = pci_bus_to_host(pdev->bus); |
| 215 | struct pnv_phb *phb = hose->private_data; |
| 216 | struct msi_desc *entry; |
Paul Mackerras | e297c93 | 2015-09-10 14:36:21 +1000 | [diff] [blame] | 217 | irq_hw_number_t hwirq; |
Benjamin Herrenschmidt | c1a2562 | 2011-09-19 17:45:06 +0000 | [diff] [blame] | 218 | |
| 219 | if (WARN_ON(!phb)) |
| 220 | return; |
| 221 | |
Jiang Liu | 2921d17 | 2015-07-09 16:00:38 +0800 | [diff] [blame] | 222 | for_each_pci_msi_entry(entry, pdev) { |
Michael Ellerman | ef24ba7 | 2016-09-06 21:53:24 +1000 | [diff] [blame] | 223 | if (!entry->irq) |
Benjamin Herrenschmidt | c1a2562 | 2011-09-19 17:45:06 +0000 | [diff] [blame] | 224 | continue; |
Paul Mackerras | e297c93 | 2015-09-10 14:36:21 +1000 | [diff] [blame] | 225 | hwirq = virq_to_hw(entry->irq); |
Benjamin Herrenschmidt | c1a2562 | 2011-09-19 17:45:06 +0000 | [diff] [blame] | 226 | irq_set_msi_desc(entry->irq, NULL); |
Benjamin Herrenschmidt | c1a2562 | 2011-09-19 17:45:06 +0000 | [diff] [blame] | 227 | irq_dispose_mapping(entry->irq); |
Paul Mackerras | e297c93 | 2015-09-10 14:36:21 +1000 | [diff] [blame] | 228 | msi_bitmap_free_hwirqs(&phb->msi_bmp, hwirq - phb->msi_base, 1); |
Benjamin Herrenschmidt | c1a2562 | 2011-09-19 17:45:06 +0000 | [diff] [blame] | 229 | } |
| 230 | } |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 231 | |
Russell Currey | 31bbd45 | 2017-06-14 14:19:58 +1000 | [diff] [blame] | 232 | /* Nicely print the contents of the PE State Tables (PEST). */ |
| 233 | static void pnv_pci_dump_pest(__be64 pestA[], __be64 pestB[], int pest_size) |
| 234 | { |
| 235 | __be64 prevA = ULONG_MAX, prevB = ULONG_MAX; |
| 236 | bool dup = false; |
| 237 | int i; |
| 238 | |
| 239 | for (i = 0; i < pest_size; i++) { |
| 240 | __be64 peA = be64_to_cpu(pestA[i]); |
| 241 | __be64 peB = be64_to_cpu(pestB[i]); |
| 242 | |
| 243 | if (peA != prevA || peB != prevB) { |
| 244 | if (dup) { |
| 245 | pr_info("PE[..%03x] A/B: as above\n", i-1); |
| 246 | dup = false; |
| 247 | } |
| 248 | prevA = peA; |
| 249 | prevB = peB; |
| 250 | if (peA & PNV_IODA_STOPPED_STATE || |
| 251 | peB & PNV_IODA_STOPPED_STATE) |
| 252 | pr_info("PE[%03x] A/B: %016llx %016llx\n", |
| 253 | i, peA, peB); |
| 254 | } else if (!dup && (peA & PNV_IODA_STOPPED_STATE || |
| 255 | peB & PNV_IODA_STOPPED_STATE)) { |
| 256 | dup = true; |
| 257 | } |
| 258 | } |
| 259 | } |
| 260 | |
Gavin Shan | 93aef2a | 2013-11-22 16:28:45 +0800 | [diff] [blame] | 261 | static void pnv_pci_dump_p7ioc_diag_data(struct pci_controller *hose, |
| 262 | struct OpalIoPhbErrorCommon *common) |
Benjamin Herrenschmidt | cee72d5 | 2011-11-29 18:22:53 +0000 | [diff] [blame] | 263 | { |
Gavin Shan | 93aef2a | 2013-11-22 16:28:45 +0800 | [diff] [blame] | 264 | struct OpalIoP7IOCPhbErrorData *data; |
Benjamin Herrenschmidt | cee72d5 | 2011-11-29 18:22:53 +0000 | [diff] [blame] | 265 | |
Gavin Shan | 93aef2a | 2013-11-22 16:28:45 +0800 | [diff] [blame] | 266 | data = (struct OpalIoP7IOCPhbErrorData *)common; |
Russell Currey | 1f52f17 | 2016-11-16 14:02:15 +1100 | [diff] [blame] | 267 | pr_info("P7IOC PHB#%x Diag-data (Version: %d)\n", |
Gavin Shan | f18440f | 2014-07-17 14:41:42 +1000 | [diff] [blame] | 268 | hose->global_number, be32_to_cpu(common->version)); |
Benjamin Herrenschmidt | cee72d5 | 2011-11-29 18:22:53 +0000 | [diff] [blame] | 269 | |
Gavin Shan | af87d2f | 2014-02-25 15:28:38 +0800 | [diff] [blame] | 270 | if (data->brdgCtl) |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 271 | pr_info("brdgCtl: %08x\n", |
Gavin Shan | f18440f | 2014-07-17 14:41:42 +1000 | [diff] [blame] | 272 | be32_to_cpu(data->brdgCtl)); |
Gavin Shan | af87d2f | 2014-02-25 15:28:38 +0800 | [diff] [blame] | 273 | if (data->portStatusReg || data->rootCmplxStatus || |
| 274 | data->busAgentStatus) |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 275 | pr_info("UtlSts: %08x %08x %08x\n", |
Gavin Shan | f18440f | 2014-07-17 14:41:42 +1000 | [diff] [blame] | 276 | be32_to_cpu(data->portStatusReg), |
| 277 | be32_to_cpu(data->rootCmplxStatus), |
| 278 | be32_to_cpu(data->busAgentStatus)); |
Gavin Shan | af87d2f | 2014-02-25 15:28:38 +0800 | [diff] [blame] | 279 | if (data->deviceStatus || data->slotStatus || |
| 280 | data->linkStatus || data->devCmdStatus || |
| 281 | data->devSecStatus) |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 282 | pr_info("RootSts: %08x %08x %08x %08x %08x\n", |
Gavin Shan | f18440f | 2014-07-17 14:41:42 +1000 | [diff] [blame] | 283 | be32_to_cpu(data->deviceStatus), |
| 284 | be32_to_cpu(data->slotStatus), |
| 285 | be32_to_cpu(data->linkStatus), |
| 286 | be32_to_cpu(data->devCmdStatus), |
| 287 | be32_to_cpu(data->devSecStatus)); |
Gavin Shan | af87d2f | 2014-02-25 15:28:38 +0800 | [diff] [blame] | 288 | if (data->rootErrorStatus || data->uncorrErrorStatus || |
| 289 | data->corrErrorStatus) |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 290 | pr_info("RootErrSts: %08x %08x %08x\n", |
Gavin Shan | f18440f | 2014-07-17 14:41:42 +1000 | [diff] [blame] | 291 | be32_to_cpu(data->rootErrorStatus), |
| 292 | be32_to_cpu(data->uncorrErrorStatus), |
| 293 | be32_to_cpu(data->corrErrorStatus)); |
Gavin Shan | af87d2f | 2014-02-25 15:28:38 +0800 | [diff] [blame] | 294 | if (data->tlpHdr1 || data->tlpHdr2 || |
| 295 | data->tlpHdr3 || data->tlpHdr4) |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 296 | pr_info("RootErrLog: %08x %08x %08x %08x\n", |
Gavin Shan | f18440f | 2014-07-17 14:41:42 +1000 | [diff] [blame] | 297 | be32_to_cpu(data->tlpHdr1), |
| 298 | be32_to_cpu(data->tlpHdr2), |
| 299 | be32_to_cpu(data->tlpHdr3), |
| 300 | be32_to_cpu(data->tlpHdr4)); |
Gavin Shan | af87d2f | 2014-02-25 15:28:38 +0800 | [diff] [blame] | 301 | if (data->sourceId || data->errorClass || |
| 302 | data->correlator) |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 303 | pr_info("RootErrLog1: %08x %016llx %016llx\n", |
Gavin Shan | f18440f | 2014-07-17 14:41:42 +1000 | [diff] [blame] | 304 | be32_to_cpu(data->sourceId), |
| 305 | be64_to_cpu(data->errorClass), |
| 306 | be64_to_cpu(data->correlator)); |
Gavin Shan | af87d2f | 2014-02-25 15:28:38 +0800 | [diff] [blame] | 307 | if (data->p7iocPlssr || data->p7iocCsr) |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 308 | pr_info("PhbSts: %016llx %016llx\n", |
Gavin Shan | f18440f | 2014-07-17 14:41:42 +1000 | [diff] [blame] | 309 | be64_to_cpu(data->p7iocPlssr), |
| 310 | be64_to_cpu(data->p7iocCsr)); |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 311 | if (data->lemFir) |
| 312 | pr_info("Lem: %016llx %016llx %016llx\n", |
Gavin Shan | f18440f | 2014-07-17 14:41:42 +1000 | [diff] [blame] | 313 | be64_to_cpu(data->lemFir), |
| 314 | be64_to_cpu(data->lemErrorMask), |
| 315 | be64_to_cpu(data->lemWOF)); |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 316 | if (data->phbErrorStatus) |
| 317 | pr_info("PhbErr: %016llx %016llx %016llx %016llx\n", |
Gavin Shan | f18440f | 2014-07-17 14:41:42 +1000 | [diff] [blame] | 318 | be64_to_cpu(data->phbErrorStatus), |
| 319 | be64_to_cpu(data->phbFirstErrorStatus), |
| 320 | be64_to_cpu(data->phbErrorLog0), |
| 321 | be64_to_cpu(data->phbErrorLog1)); |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 322 | if (data->mmioErrorStatus) |
| 323 | pr_info("OutErr: %016llx %016llx %016llx %016llx\n", |
Gavin Shan | f18440f | 2014-07-17 14:41:42 +1000 | [diff] [blame] | 324 | be64_to_cpu(data->mmioErrorStatus), |
| 325 | be64_to_cpu(data->mmioFirstErrorStatus), |
| 326 | be64_to_cpu(data->mmioErrorLog0), |
| 327 | be64_to_cpu(data->mmioErrorLog1)); |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 328 | if (data->dma0ErrorStatus) |
| 329 | pr_info("InAErr: %016llx %016llx %016llx %016llx\n", |
Gavin Shan | f18440f | 2014-07-17 14:41:42 +1000 | [diff] [blame] | 330 | be64_to_cpu(data->dma0ErrorStatus), |
| 331 | be64_to_cpu(data->dma0FirstErrorStatus), |
| 332 | be64_to_cpu(data->dma0ErrorLog0), |
| 333 | be64_to_cpu(data->dma0ErrorLog1)); |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 334 | if (data->dma1ErrorStatus) |
| 335 | pr_info("InBErr: %016llx %016llx %016llx %016llx\n", |
Gavin Shan | f18440f | 2014-07-17 14:41:42 +1000 | [diff] [blame] | 336 | be64_to_cpu(data->dma1ErrorStatus), |
| 337 | be64_to_cpu(data->dma1FirstErrorStatus), |
| 338 | be64_to_cpu(data->dma1ErrorLog0), |
| 339 | be64_to_cpu(data->dma1ErrorLog1)); |
Benjamin Herrenschmidt | cee72d5 | 2011-11-29 18:22:53 +0000 | [diff] [blame] | 340 | |
Russell Currey | 31bbd45 | 2017-06-14 14:19:58 +1000 | [diff] [blame] | 341 | pnv_pci_dump_pest(data->pestA, data->pestB, OPAL_P7IOC_NUM_PEST_REGS); |
Benjamin Herrenschmidt | cee72d5 | 2011-11-29 18:22:53 +0000 | [diff] [blame] | 342 | } |
| 343 | |
Gavin Shan | 93aef2a | 2013-11-22 16:28:45 +0800 | [diff] [blame] | 344 | static void pnv_pci_dump_phb3_diag_data(struct pci_controller *hose, |
| 345 | struct OpalIoPhbErrorCommon *common) |
Benjamin Herrenschmidt | cee72d5 | 2011-11-29 18:22:53 +0000 | [diff] [blame] | 346 | { |
Gavin Shan | 93aef2a | 2013-11-22 16:28:45 +0800 | [diff] [blame] | 347 | struct OpalIoPhb3ErrorData *data; |
Gavin Shan | 93aef2a | 2013-11-22 16:28:45 +0800 | [diff] [blame] | 348 | |
| 349 | data = (struct OpalIoPhb3ErrorData*)common; |
Russell Currey | 1f52f17 | 2016-11-16 14:02:15 +1100 | [diff] [blame] | 350 | pr_info("PHB3 PHB#%x Diag-data (Version: %d)\n", |
Guo Chao | ddf0322a | 2014-06-09 16:58:51 +0800 | [diff] [blame] | 351 | hose->global_number, be32_to_cpu(common->version)); |
Gavin Shan | af87d2f | 2014-02-25 15:28:38 +0800 | [diff] [blame] | 352 | if (data->brdgCtl) |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 353 | pr_info("brdgCtl: %08x\n", |
Guo Chao | ddf0322a | 2014-06-09 16:58:51 +0800 | [diff] [blame] | 354 | be32_to_cpu(data->brdgCtl)); |
Gavin Shan | af87d2f | 2014-02-25 15:28:38 +0800 | [diff] [blame] | 355 | if (data->portStatusReg || data->rootCmplxStatus || |
| 356 | data->busAgentStatus) |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 357 | pr_info("UtlSts: %08x %08x %08x\n", |
Guo Chao | ddf0322a | 2014-06-09 16:58:51 +0800 | [diff] [blame] | 358 | be32_to_cpu(data->portStatusReg), |
| 359 | be32_to_cpu(data->rootCmplxStatus), |
| 360 | be32_to_cpu(data->busAgentStatus)); |
Gavin Shan | af87d2f | 2014-02-25 15:28:38 +0800 | [diff] [blame] | 361 | if (data->deviceStatus || data->slotStatus || |
| 362 | data->linkStatus || data->devCmdStatus || |
| 363 | data->devSecStatus) |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 364 | pr_info("RootSts: %08x %08x %08x %08x %08x\n", |
Guo Chao | ddf0322a | 2014-06-09 16:58:51 +0800 | [diff] [blame] | 365 | be32_to_cpu(data->deviceStatus), |
| 366 | be32_to_cpu(data->slotStatus), |
| 367 | be32_to_cpu(data->linkStatus), |
| 368 | be32_to_cpu(data->devCmdStatus), |
| 369 | be32_to_cpu(data->devSecStatus)); |
Gavin Shan | af87d2f | 2014-02-25 15:28:38 +0800 | [diff] [blame] | 370 | if (data->rootErrorStatus || data->uncorrErrorStatus || |
| 371 | data->corrErrorStatus) |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 372 | pr_info("RootErrSts: %08x %08x %08x\n", |
Guo Chao | ddf0322a | 2014-06-09 16:58:51 +0800 | [diff] [blame] | 373 | be32_to_cpu(data->rootErrorStatus), |
| 374 | be32_to_cpu(data->uncorrErrorStatus), |
| 375 | be32_to_cpu(data->corrErrorStatus)); |
Gavin Shan | af87d2f | 2014-02-25 15:28:38 +0800 | [diff] [blame] | 376 | if (data->tlpHdr1 || data->tlpHdr2 || |
| 377 | data->tlpHdr3 || data->tlpHdr4) |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 378 | pr_info("RootErrLog: %08x %08x %08x %08x\n", |
Guo Chao | ddf0322a | 2014-06-09 16:58:51 +0800 | [diff] [blame] | 379 | be32_to_cpu(data->tlpHdr1), |
| 380 | be32_to_cpu(data->tlpHdr2), |
| 381 | be32_to_cpu(data->tlpHdr3), |
| 382 | be32_to_cpu(data->tlpHdr4)); |
Gavin Shan | af87d2f | 2014-02-25 15:28:38 +0800 | [diff] [blame] | 383 | if (data->sourceId || data->errorClass || |
| 384 | data->correlator) |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 385 | pr_info("RootErrLog1: %08x %016llx %016llx\n", |
Guo Chao | ddf0322a | 2014-06-09 16:58:51 +0800 | [diff] [blame] | 386 | be32_to_cpu(data->sourceId), |
| 387 | be64_to_cpu(data->errorClass), |
| 388 | be64_to_cpu(data->correlator)); |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 389 | if (data->nFir) |
| 390 | pr_info("nFir: %016llx %016llx %016llx\n", |
Guo Chao | ddf0322a | 2014-06-09 16:58:51 +0800 | [diff] [blame] | 391 | be64_to_cpu(data->nFir), |
| 392 | be64_to_cpu(data->nFirMask), |
| 393 | be64_to_cpu(data->nFirWOF)); |
Gavin Shan | af87d2f | 2014-02-25 15:28:38 +0800 | [diff] [blame] | 394 | if (data->phbPlssr || data->phbCsr) |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 395 | pr_info("PhbSts: %016llx %016llx\n", |
Guo Chao | ddf0322a | 2014-06-09 16:58:51 +0800 | [diff] [blame] | 396 | be64_to_cpu(data->phbPlssr), |
| 397 | be64_to_cpu(data->phbCsr)); |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 398 | if (data->lemFir) |
| 399 | pr_info("Lem: %016llx %016llx %016llx\n", |
Guo Chao | ddf0322a | 2014-06-09 16:58:51 +0800 | [diff] [blame] | 400 | be64_to_cpu(data->lemFir), |
| 401 | be64_to_cpu(data->lemErrorMask), |
| 402 | be64_to_cpu(data->lemWOF)); |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 403 | if (data->phbErrorStatus) |
| 404 | pr_info("PhbErr: %016llx %016llx %016llx %016llx\n", |
Guo Chao | ddf0322a | 2014-06-09 16:58:51 +0800 | [diff] [blame] | 405 | be64_to_cpu(data->phbErrorStatus), |
| 406 | be64_to_cpu(data->phbFirstErrorStatus), |
| 407 | be64_to_cpu(data->phbErrorLog0), |
| 408 | be64_to_cpu(data->phbErrorLog1)); |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 409 | if (data->mmioErrorStatus) |
| 410 | pr_info("OutErr: %016llx %016llx %016llx %016llx\n", |
Guo Chao | ddf0322a | 2014-06-09 16:58:51 +0800 | [diff] [blame] | 411 | be64_to_cpu(data->mmioErrorStatus), |
| 412 | be64_to_cpu(data->mmioFirstErrorStatus), |
| 413 | be64_to_cpu(data->mmioErrorLog0), |
| 414 | be64_to_cpu(data->mmioErrorLog1)); |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 415 | if (data->dma0ErrorStatus) |
| 416 | pr_info("InAErr: %016llx %016llx %016llx %016llx\n", |
Guo Chao | ddf0322a | 2014-06-09 16:58:51 +0800 | [diff] [blame] | 417 | be64_to_cpu(data->dma0ErrorStatus), |
| 418 | be64_to_cpu(data->dma0FirstErrorStatus), |
| 419 | be64_to_cpu(data->dma0ErrorLog0), |
| 420 | be64_to_cpu(data->dma0ErrorLog1)); |
Gavin Shan | b34497d | 2014-04-24 18:00:10 +1000 | [diff] [blame] | 421 | if (data->dma1ErrorStatus) |
| 422 | pr_info("InBErr: %016llx %016llx %016llx %016llx\n", |
Guo Chao | ddf0322a | 2014-06-09 16:58:51 +0800 | [diff] [blame] | 423 | be64_to_cpu(data->dma1ErrorStatus), |
| 424 | be64_to_cpu(data->dma1FirstErrorStatus), |
| 425 | be64_to_cpu(data->dma1ErrorLog0), |
| 426 | be64_to_cpu(data->dma1ErrorLog1)); |
Gavin Shan | 93aef2a | 2013-11-22 16:28:45 +0800 | [diff] [blame] | 427 | |
Russell Currey | 31bbd45 | 2017-06-14 14:19:58 +1000 | [diff] [blame] | 428 | pnv_pci_dump_pest(data->pestA, data->pestB, OPAL_PHB3_NUM_PEST_REGS); |
Gavin Shan | 93aef2a | 2013-11-22 16:28:45 +0800 | [diff] [blame] | 429 | } |
| 430 | |
Russell Currey | a4b48ba | 2017-06-14 14:20:00 +1000 | [diff] [blame] | 431 | static void pnv_pci_dump_phb4_diag_data(struct pci_controller *hose, |
| 432 | struct OpalIoPhbErrorCommon *common) |
| 433 | { |
| 434 | struct OpalIoPhb4ErrorData *data; |
| 435 | |
| 436 | data = (struct OpalIoPhb4ErrorData*)common; |
| 437 | pr_info("PHB4 PHB#%d Diag-data (Version: %d)\n", |
| 438 | hose->global_number, be32_to_cpu(common->version)); |
| 439 | if (data->brdgCtl) |
| 440 | pr_info("brdgCtl: %08x\n", |
| 441 | be32_to_cpu(data->brdgCtl)); |
| 442 | if (data->deviceStatus || data->slotStatus || |
| 443 | data->linkStatus || data->devCmdStatus || |
| 444 | data->devSecStatus) |
| 445 | pr_info("RootSts: %08x %08x %08x %08x %08x\n", |
| 446 | be32_to_cpu(data->deviceStatus), |
| 447 | be32_to_cpu(data->slotStatus), |
| 448 | be32_to_cpu(data->linkStatus), |
| 449 | be32_to_cpu(data->devCmdStatus), |
| 450 | be32_to_cpu(data->devSecStatus)); |
| 451 | if (data->rootErrorStatus || data->uncorrErrorStatus || |
| 452 | data->corrErrorStatus) |
| 453 | pr_info("RootErrSts: %08x %08x %08x\n", |
| 454 | be32_to_cpu(data->rootErrorStatus), |
| 455 | be32_to_cpu(data->uncorrErrorStatus), |
| 456 | be32_to_cpu(data->corrErrorStatus)); |
| 457 | if (data->tlpHdr1 || data->tlpHdr2 || |
| 458 | data->tlpHdr3 || data->tlpHdr4) |
| 459 | pr_info("RootErrLog: %08x %08x %08x %08x\n", |
| 460 | be32_to_cpu(data->tlpHdr1), |
| 461 | be32_to_cpu(data->tlpHdr2), |
| 462 | be32_to_cpu(data->tlpHdr3), |
| 463 | be32_to_cpu(data->tlpHdr4)); |
| 464 | if (data->sourceId) |
| 465 | pr_info("sourceId: %08x\n", be32_to_cpu(data->sourceId)); |
| 466 | if (data->nFir) |
| 467 | pr_info("nFir: %016llx %016llx %016llx\n", |
| 468 | be64_to_cpu(data->nFir), |
| 469 | be64_to_cpu(data->nFirMask), |
| 470 | be64_to_cpu(data->nFirWOF)); |
| 471 | if (data->phbPlssr || data->phbCsr) |
| 472 | pr_info("PhbSts: %016llx %016llx\n", |
| 473 | be64_to_cpu(data->phbPlssr), |
| 474 | be64_to_cpu(data->phbCsr)); |
| 475 | if (data->lemFir) |
| 476 | pr_info("Lem: %016llx %016llx %016llx\n", |
| 477 | be64_to_cpu(data->lemFir), |
| 478 | be64_to_cpu(data->lemErrorMask), |
| 479 | be64_to_cpu(data->lemWOF)); |
| 480 | if (data->phbErrorStatus) |
| 481 | pr_info("PhbErr: %016llx %016llx %016llx %016llx\n", |
| 482 | be64_to_cpu(data->phbErrorStatus), |
| 483 | be64_to_cpu(data->phbFirstErrorStatus), |
| 484 | be64_to_cpu(data->phbErrorLog0), |
| 485 | be64_to_cpu(data->phbErrorLog1)); |
| 486 | if (data->phbTxeErrorStatus) |
| 487 | pr_info("PhbTxeErr: %016llx %016llx %016llx %016llx\n", |
| 488 | be64_to_cpu(data->phbTxeErrorStatus), |
| 489 | be64_to_cpu(data->phbTxeFirstErrorStatus), |
| 490 | be64_to_cpu(data->phbTxeErrorLog0), |
| 491 | be64_to_cpu(data->phbTxeErrorLog1)); |
| 492 | if (data->phbRxeArbErrorStatus) |
| 493 | pr_info("RxeArbErr: %016llx %016llx %016llx %016llx\n", |
| 494 | be64_to_cpu(data->phbRxeArbErrorStatus), |
| 495 | be64_to_cpu(data->phbRxeArbFirstErrorStatus), |
| 496 | be64_to_cpu(data->phbRxeArbErrorLog0), |
| 497 | be64_to_cpu(data->phbRxeArbErrorLog1)); |
| 498 | if (data->phbRxeMrgErrorStatus) |
| 499 | pr_info("RxeMrgErr: %016llx %016llx %016llx %016llx\n", |
| 500 | be64_to_cpu(data->phbRxeMrgErrorStatus), |
| 501 | be64_to_cpu(data->phbRxeMrgFirstErrorStatus), |
| 502 | be64_to_cpu(data->phbRxeMrgErrorLog0), |
| 503 | be64_to_cpu(data->phbRxeMrgErrorLog1)); |
| 504 | if (data->phbRxeTceErrorStatus) |
| 505 | pr_info("RxeTceErr: %016llx %016llx %016llx %016llx\n", |
| 506 | be64_to_cpu(data->phbRxeTceErrorStatus), |
| 507 | be64_to_cpu(data->phbRxeTceFirstErrorStatus), |
| 508 | be64_to_cpu(data->phbRxeTceErrorLog0), |
| 509 | be64_to_cpu(data->phbRxeTceErrorLog1)); |
| 510 | |
| 511 | if (data->phbPblErrorStatus) |
| 512 | pr_info("PblErr: %016llx %016llx %016llx %016llx\n", |
| 513 | be64_to_cpu(data->phbPblErrorStatus), |
| 514 | be64_to_cpu(data->phbPblFirstErrorStatus), |
| 515 | be64_to_cpu(data->phbPblErrorLog0), |
| 516 | be64_to_cpu(data->phbPblErrorLog1)); |
| 517 | if (data->phbPcieDlpErrorStatus) |
| 518 | pr_info("PcieDlp: %016llx %016llx %016llx\n", |
| 519 | be64_to_cpu(data->phbPcieDlpErrorLog1), |
| 520 | be64_to_cpu(data->phbPcieDlpErrorLog2), |
| 521 | be64_to_cpu(data->phbPcieDlpErrorStatus)); |
| 522 | if (data->phbRegbErrorStatus) |
| 523 | pr_info("RegbErr: %016llx %016llx %016llx %016llx\n", |
| 524 | be64_to_cpu(data->phbRegbErrorStatus), |
| 525 | be64_to_cpu(data->phbRegbFirstErrorStatus), |
| 526 | be64_to_cpu(data->phbRegbErrorLog0), |
| 527 | be64_to_cpu(data->phbRegbErrorLog1)); |
| 528 | |
| 529 | |
| 530 | pnv_pci_dump_pest(data->pestA, data->pestB, OPAL_PHB4_NUM_PEST_REGS); |
| 531 | } |
| 532 | |
Gavin Shan | 93aef2a | 2013-11-22 16:28:45 +0800 | [diff] [blame] | 533 | void pnv_pci_dump_phb_diag_data(struct pci_controller *hose, |
| 534 | unsigned char *log_buff) |
| 535 | { |
| 536 | struct OpalIoPhbErrorCommon *common; |
| 537 | |
| 538 | if (!hose || !log_buff) |
| 539 | return; |
| 540 | |
| 541 | common = (struct OpalIoPhbErrorCommon *)log_buff; |
Guo Chao | ddf0322a | 2014-06-09 16:58:51 +0800 | [diff] [blame] | 542 | switch (be32_to_cpu(common->ioType)) { |
Gavin Shan | 93aef2a | 2013-11-22 16:28:45 +0800 | [diff] [blame] | 543 | case OPAL_PHB_ERROR_DATA_TYPE_P7IOC: |
| 544 | pnv_pci_dump_p7ioc_diag_data(hose, common); |
| 545 | break; |
| 546 | case OPAL_PHB_ERROR_DATA_TYPE_PHB3: |
| 547 | pnv_pci_dump_phb3_diag_data(hose, common); |
Benjamin Herrenschmidt | cee72d5 | 2011-11-29 18:22:53 +0000 | [diff] [blame] | 548 | break; |
Russell Currey | a4b48ba | 2017-06-14 14:20:00 +1000 | [diff] [blame] | 549 | case OPAL_PHB_ERROR_DATA_TYPE_PHB4: |
| 550 | pnv_pci_dump_phb4_diag_data(hose, common); |
| 551 | break; |
Benjamin Herrenschmidt | cee72d5 | 2011-11-29 18:22:53 +0000 | [diff] [blame] | 552 | default: |
Gavin Shan | 93aef2a | 2013-11-22 16:28:45 +0800 | [diff] [blame] | 553 | pr_warn("%s: Unrecognized ioType %d\n", |
Guo Chao | ddf0322a | 2014-06-09 16:58:51 +0800 | [diff] [blame] | 554 | __func__, be32_to_cpu(common->ioType)); |
Benjamin Herrenschmidt | cee72d5 | 2011-11-29 18:22:53 +0000 | [diff] [blame] | 555 | } |
| 556 | } |
| 557 | |
| 558 | static void pnv_pci_handle_eeh_config(struct pnv_phb *phb, u32 pe_no) |
| 559 | { |
| 560 | unsigned long flags, rc; |
Gavin Shan | 98fd700 | 2014-07-21 14:42:35 +1000 | [diff] [blame] | 561 | int has_diag, ret = 0; |
Benjamin Herrenschmidt | cee72d5 | 2011-11-29 18:22:53 +0000 | [diff] [blame] | 562 | |
| 563 | spin_lock_irqsave(&phb->lock, flags); |
| 564 | |
Gavin Shan | 98fd700 | 2014-07-21 14:42:35 +1000 | [diff] [blame] | 565 | /* Fetch PHB diag-data */ |
Russell Currey | 5cb1f8f | 2017-06-14 14:19:59 +1000 | [diff] [blame] | 566 | rc = opal_pci_get_phb_diag_data2(phb->opal_id, phb->diag_data, |
| 567 | phb->diag_data_size); |
Benjamin Herrenschmidt | cee72d5 | 2011-11-29 18:22:53 +0000 | [diff] [blame] | 568 | has_diag = (rc == OPAL_SUCCESS); |
| 569 | |
Gavin Shan | 98fd700 | 2014-07-21 14:42:35 +1000 | [diff] [blame] | 570 | /* If PHB supports compound PE, to handle it */ |
| 571 | if (phb->unfreeze_pe) { |
| 572 | ret = phb->unfreeze_pe(phb, |
| 573 | pe_no, |
Benjamin Herrenschmidt | cee72d5 | 2011-11-29 18:22:53 +0000 | [diff] [blame] | 574 | OPAL_EEH_ACTION_CLEAR_FREEZE_ALL); |
Gavin Shan | 98fd700 | 2014-07-21 14:42:35 +1000 | [diff] [blame] | 575 | } else { |
| 576 | rc = opal_pci_eeh_freeze_clear(phb->opal_id, |
| 577 | pe_no, |
| 578 | OPAL_EEH_ACTION_CLEAR_FREEZE_ALL); |
| 579 | if (rc) { |
| 580 | pr_warn("%s: Failure %ld clearing frozen " |
| 581 | "PHB#%x-PE#%x\n", |
| 582 | __func__, rc, phb->hose->global_number, |
| 583 | pe_no); |
| 584 | ret = -EIO; |
| 585 | } |
Benjamin Herrenschmidt | cee72d5 | 2011-11-29 18:22:53 +0000 | [diff] [blame] | 586 | } |
| 587 | |
Gavin Shan | 98fd700 | 2014-07-21 14:42:35 +1000 | [diff] [blame] | 588 | /* |
| 589 | * For now, let's only display the diag buffer when we fail to clear |
| 590 | * the EEH status. We'll do more sensible things later when we have |
| 591 | * proper EEH support. We need to make sure we don't pollute ourselves |
| 592 | * with the normal errors generated when probing empty slots |
| 593 | */ |
| 594 | if (has_diag && ret) |
Russell Currey | 5cb1f8f | 2017-06-14 14:19:59 +1000 | [diff] [blame] | 595 | pnv_pci_dump_phb_diag_data(phb->hose, phb->diag_data); |
Gavin Shan | 98fd700 | 2014-07-21 14:42:35 +1000 | [diff] [blame] | 596 | |
Benjamin Herrenschmidt | cee72d5 | 2011-11-29 18:22:53 +0000 | [diff] [blame] | 597 | spin_unlock_irqrestore(&phb->lock, flags); |
| 598 | } |
| 599 | |
Gavin Shan | 3532a741 | 2015-03-17 16:15:03 +1100 | [diff] [blame] | 600 | static void pnv_pci_config_check_eeh(struct pci_dn *pdn) |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 601 | { |
Gavin Shan | 3532a741 | 2015-03-17 16:15:03 +1100 | [diff] [blame] | 602 | struct pnv_phb *phb = pdn->phb->private_data; |
Alexey Kardashevskiy | c205770 | 2018-11-19 15:25:17 +1100 | [diff] [blame] | 603 | u8 fstate = 0; |
| 604 | __be16 pcierr = 0; |
Gavin Shan | 689ee8c | 2016-05-03 15:41:25 +1000 | [diff] [blame] | 605 | unsigned int pe_no; |
Gavin Shan | 98fd700 | 2014-07-21 14:42:35 +1000 | [diff] [blame] | 606 | s64 rc; |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 607 | |
Gavin Shan | 9bf41be | 2013-06-27 13:46:48 +0800 | [diff] [blame] | 608 | /* |
| 609 | * Get the PE#. During the PCI probe stage, we might not |
| 610 | * setup that yet. So all ER errors should be mapped to |
Gavin Shan | 36954dc | 2013-11-04 16:32:47 +0800 | [diff] [blame] | 611 | * reserved PE. |
Gavin Shan | 9bf41be | 2013-06-27 13:46:48 +0800 | [diff] [blame] | 612 | */ |
Gavin Shan | 3532a741 | 2015-03-17 16:15:03 +1100 | [diff] [blame] | 613 | pe_no = pdn->pe_number; |
Gavin Shan | 36954dc | 2013-11-04 16:32:47 +0800 | [diff] [blame] | 614 | if (pe_no == IODA_INVALID_PE) { |
Gavin Shan | 92b8f13 | 2016-05-03 15:41:24 +1000 | [diff] [blame] | 615 | pe_no = phb->ioda.reserved_pe_idx; |
Gavin Shan | 36954dc | 2013-11-04 16:32:47 +0800 | [diff] [blame] | 616 | } |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 617 | |
Gavin Shan | 98fd700 | 2014-07-21 14:42:35 +1000 | [diff] [blame] | 618 | /* |
| 619 | * Fetch frozen state. If the PHB support compound PE, |
| 620 | * we need handle that case. |
| 621 | */ |
| 622 | if (phb->get_pe_state) { |
| 623 | fstate = phb->get_pe_state(phb, pe_no); |
| 624 | } else { |
| 625 | rc = opal_pci_eeh_freeze_status(phb->opal_id, |
| 626 | pe_no, |
| 627 | &fstate, |
| 628 | &pcierr, |
| 629 | NULL); |
| 630 | if (rc) { |
| 631 | pr_warn("%s: Failure %lld getting PHB#%x-PE#%x state\n", |
| 632 | __func__, rc, phb->hose->global_number, pe_no); |
| 633 | return; |
| 634 | } |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 635 | } |
Gavin Shan | 98fd700 | 2014-07-21 14:42:35 +1000 | [diff] [blame] | 636 | |
Russell Currey | 1f52f17 | 2016-11-16 14:02:15 +1100 | [diff] [blame] | 637 | pr_devel(" -> EEH check, bdfn=%04x PE#%x fstate=%x\n", |
Alexey Kardashevskiy | 9e44754 | 2016-05-02 17:06:12 +1000 | [diff] [blame] | 638 | (pdn->busno << 8) | (pdn->devfn), pe_no, fstate); |
Gavin Shan | 98fd700 | 2014-07-21 14:42:35 +1000 | [diff] [blame] | 639 | |
| 640 | /* Clear the frozen state if applicable */ |
| 641 | if (fstate == OPAL_EEH_STOPPED_MMIO_FREEZE || |
| 642 | fstate == OPAL_EEH_STOPPED_DMA_FREEZE || |
| 643 | fstate == OPAL_EEH_STOPPED_MMIO_DMA_FREEZE) { |
| 644 | /* |
| 645 | * If PHB supports compound PE, freeze it for |
| 646 | * consistency. |
| 647 | */ |
| 648 | if (phb->freeze_pe) |
| 649 | phb->freeze_pe(phb, pe_no); |
| 650 | |
Benjamin Herrenschmidt | cee72d5 | 2011-11-29 18:22:53 +0000 | [diff] [blame] | 651 | pnv_pci_handle_eeh_config(phb, pe_no); |
Gavin Shan | 98fd700 | 2014-07-21 14:42:35 +1000 | [diff] [blame] | 652 | } |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 653 | } |
| 654 | |
Gavin Shan | 3532a741 | 2015-03-17 16:15:03 +1100 | [diff] [blame] | 655 | int pnv_pci_cfg_read(struct pci_dn *pdn, |
Gavin Shan | 9bf41be | 2013-06-27 13:46:48 +0800 | [diff] [blame] | 656 | int where, int size, u32 *val) |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 657 | { |
Gavin Shan | 9bf41be | 2013-06-27 13:46:48 +0800 | [diff] [blame] | 658 | struct pnv_phb *phb = pdn->phb->private_data; |
| 659 | u32 bdfn = (pdn->busno << 8) | pdn->devfn; |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 660 | s64 rc; |
| 661 | |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 662 | switch (size) { |
| 663 | case 1: { |
| 664 | u8 v8; |
| 665 | rc = opal_pci_config_read_byte(phb->opal_id, bdfn, where, &v8); |
| 666 | *val = (rc == OPAL_SUCCESS) ? v8 : 0xff; |
| 667 | break; |
| 668 | } |
| 669 | case 2: { |
Benjamin Herrenschmidt | 3a1a466 | 2013-09-23 12:05:01 +1000 | [diff] [blame] | 670 | __be16 v16; |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 671 | rc = opal_pci_config_read_half_word(phb->opal_id, bdfn, where, |
| 672 | &v16); |
Benjamin Herrenschmidt | 3a1a466 | 2013-09-23 12:05:01 +1000 | [diff] [blame] | 673 | *val = (rc == OPAL_SUCCESS) ? be16_to_cpu(v16) : 0xffff; |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 674 | break; |
| 675 | } |
| 676 | case 4: { |
Benjamin Herrenschmidt | 3a1a466 | 2013-09-23 12:05:01 +1000 | [diff] [blame] | 677 | __be32 v32; |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 678 | rc = opal_pci_config_read_word(phb->opal_id, bdfn, where, &v32); |
Benjamin Herrenschmidt | 3a1a466 | 2013-09-23 12:05:01 +1000 | [diff] [blame] | 679 | *val = (rc == OPAL_SUCCESS) ? be32_to_cpu(v32) : 0xffffffff; |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 680 | break; |
| 681 | } |
| 682 | default: |
| 683 | return PCIBIOS_FUNC_NOT_SUPPORTED; |
| 684 | } |
Gavin Shan | d0914f5 | 2014-04-24 18:00:12 +1000 | [diff] [blame] | 685 | |
Alexey Kardashevskiy | 9e44754 | 2016-05-02 17:06:12 +1000 | [diff] [blame] | 686 | pr_devel("%s: bus: %x devfn: %x +%x/%x -> %08x\n", |
| 687 | __func__, pdn->busno, pdn->devfn, where, size, *val); |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 688 | return PCIBIOS_SUCCESSFUL; |
| 689 | } |
| 690 | |
Gavin Shan | 3532a741 | 2015-03-17 16:15:03 +1100 | [diff] [blame] | 691 | int pnv_pci_cfg_write(struct pci_dn *pdn, |
Gavin Shan | 9bf41be | 2013-06-27 13:46:48 +0800 | [diff] [blame] | 692 | int where, int size, u32 val) |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 693 | { |
Gavin Shan | 9bf41be | 2013-06-27 13:46:48 +0800 | [diff] [blame] | 694 | struct pnv_phb *phb = pdn->phb->private_data; |
| 695 | u32 bdfn = (pdn->busno << 8) | pdn->devfn; |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 696 | |
Alexey Kardashevskiy | 9e44754 | 2016-05-02 17:06:12 +1000 | [diff] [blame] | 697 | pr_devel("%s: bus: %x devfn: %x +%x/%x -> %08x\n", |
| 698 | __func__, pdn->busno, pdn->devfn, where, size, val); |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 699 | switch (size) { |
| 700 | case 1: |
| 701 | opal_pci_config_write_byte(phb->opal_id, bdfn, where, val); |
| 702 | break; |
| 703 | case 2: |
| 704 | opal_pci_config_write_half_word(phb->opal_id, bdfn, where, val); |
| 705 | break; |
| 706 | case 4: |
| 707 | opal_pci_config_write_word(phb->opal_id, bdfn, where, val); |
| 708 | break; |
| 709 | default: |
| 710 | return PCIBIOS_FUNC_NOT_SUPPORTED; |
| 711 | } |
Gavin Shan | be7e744 | 2013-06-20 13:21:15 +0800 | [diff] [blame] | 712 | |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 713 | return PCIBIOS_SUCCESSFUL; |
| 714 | } |
| 715 | |
Gavin Shan | d0914f5 | 2014-04-24 18:00:12 +1000 | [diff] [blame] | 716 | #if CONFIG_EEH |
Gavin Shan | 3532a741 | 2015-03-17 16:15:03 +1100 | [diff] [blame] | 717 | static bool pnv_pci_cfg_check(struct pci_dn *pdn) |
Gavin Shan | d0914f5 | 2014-04-24 18:00:12 +1000 | [diff] [blame] | 718 | { |
| 719 | struct eeh_dev *edev = NULL; |
Gavin Shan | 3532a741 | 2015-03-17 16:15:03 +1100 | [diff] [blame] | 720 | struct pnv_phb *phb = pdn->phb->private_data; |
Gavin Shan | d0914f5 | 2014-04-24 18:00:12 +1000 | [diff] [blame] | 721 | |
| 722 | /* EEH not enabled ? */ |
| 723 | if (!(phb->flags & PNV_PHB_FLAG_EEH)) |
| 724 | return true; |
| 725 | |
Gavin Shan | d2b0f6f | 2014-04-24 18:00:19 +1000 | [diff] [blame] | 726 | /* PE reset or device removed ? */ |
Gavin Shan | 3532a741 | 2015-03-17 16:15:03 +1100 | [diff] [blame] | 727 | edev = pdn->edev; |
Gavin Shan | d2b0f6f | 2014-04-24 18:00:19 +1000 | [diff] [blame] | 728 | if (edev) { |
| 729 | if (edev->pe && |
Gavin Shan | 8a6b371 | 2014-10-01 17:07:50 +1000 | [diff] [blame] | 730 | (edev->pe->state & EEH_PE_CFG_BLOCKED)) |
Gavin Shan | d2b0f6f | 2014-04-24 18:00:19 +1000 | [diff] [blame] | 731 | return false; |
| 732 | |
| 733 | if (edev->mode & EEH_DEV_REMOVED) |
| 734 | return false; |
| 735 | } |
Gavin Shan | d0914f5 | 2014-04-24 18:00:12 +1000 | [diff] [blame] | 736 | |
| 737 | return true; |
| 738 | } |
| 739 | #else |
Gavin Shan | 3532a741 | 2015-03-17 16:15:03 +1100 | [diff] [blame] | 740 | static inline pnv_pci_cfg_check(struct pci_dn *pdn) |
Gavin Shan | d0914f5 | 2014-04-24 18:00:12 +1000 | [diff] [blame] | 741 | { |
| 742 | return true; |
| 743 | } |
| 744 | #endif /* CONFIG_EEH */ |
| 745 | |
Gavin Shan | 9bf41be | 2013-06-27 13:46:48 +0800 | [diff] [blame] | 746 | static int pnv_pci_read_config(struct pci_bus *bus, |
| 747 | unsigned int devfn, |
| 748 | int where, int size, u32 *val) |
| 749 | { |
Gavin Shan | 9bf41be | 2013-06-27 13:46:48 +0800 | [diff] [blame] | 750 | struct pci_dn *pdn; |
Gavin Shan | d0914f5 | 2014-04-24 18:00:12 +1000 | [diff] [blame] | 751 | struct pnv_phb *phb; |
Gavin Shan | d0914f5 | 2014-04-24 18:00:12 +1000 | [diff] [blame] | 752 | int ret; |
Gavin Shan | 9bf41be | 2013-06-27 13:46:48 +0800 | [diff] [blame] | 753 | |
| 754 | *val = 0xFFFFFFFF; |
Gavin Shan | 3532a741 | 2015-03-17 16:15:03 +1100 | [diff] [blame] | 755 | pdn = pci_get_pdn_by_devfn(bus, devfn); |
| 756 | if (!pdn) |
Gavin Shan | d0914f5 | 2014-04-24 18:00:12 +1000 | [diff] [blame] | 757 | return PCIBIOS_DEVICE_NOT_FOUND; |
| 758 | |
Gavin Shan | 3532a741 | 2015-03-17 16:15:03 +1100 | [diff] [blame] | 759 | if (!pnv_pci_cfg_check(pdn)) |
| 760 | return PCIBIOS_DEVICE_NOT_FOUND; |
| 761 | |
| 762 | ret = pnv_pci_cfg_read(pdn, where, size, val); |
| 763 | phb = pdn->phb->private_data; |
| 764 | if (phb->flags & PNV_PHB_FLAG_EEH && pdn->edev) { |
Gavin Shan | d0914f5 | 2014-04-24 18:00:12 +1000 | [diff] [blame] | 765 | if (*val == EEH_IO_ERROR_VALUE(size) && |
Gavin Shan | 3532a741 | 2015-03-17 16:15:03 +1100 | [diff] [blame] | 766 | eeh_dev_check_failure(pdn->edev)) |
Gavin Shan | d0914f5 | 2014-04-24 18:00:12 +1000 | [diff] [blame] | 767 | return PCIBIOS_DEVICE_NOT_FOUND; |
| 768 | } else { |
Gavin Shan | 3532a741 | 2015-03-17 16:15:03 +1100 | [diff] [blame] | 769 | pnv_pci_config_check_eeh(pdn); |
Gavin Shan | d0914f5 | 2014-04-24 18:00:12 +1000 | [diff] [blame] | 770 | } |
| 771 | |
| 772 | return ret; |
Gavin Shan | 9bf41be | 2013-06-27 13:46:48 +0800 | [diff] [blame] | 773 | } |
| 774 | |
| 775 | static int pnv_pci_write_config(struct pci_bus *bus, |
| 776 | unsigned int devfn, |
| 777 | int where, int size, u32 val) |
| 778 | { |
Gavin Shan | 9bf41be | 2013-06-27 13:46:48 +0800 | [diff] [blame] | 779 | struct pci_dn *pdn; |
Gavin Shan | d0914f5 | 2014-04-24 18:00:12 +1000 | [diff] [blame] | 780 | struct pnv_phb *phb; |
Gavin Shan | d0914f5 | 2014-04-24 18:00:12 +1000 | [diff] [blame] | 781 | int ret; |
Gavin Shan | 9bf41be | 2013-06-27 13:46:48 +0800 | [diff] [blame] | 782 | |
Gavin Shan | 3532a741 | 2015-03-17 16:15:03 +1100 | [diff] [blame] | 783 | pdn = pci_get_pdn_by_devfn(bus, devfn); |
| 784 | if (!pdn) |
Gavin Shan | d0914f5 | 2014-04-24 18:00:12 +1000 | [diff] [blame] | 785 | return PCIBIOS_DEVICE_NOT_FOUND; |
| 786 | |
Gavin Shan | 3532a741 | 2015-03-17 16:15:03 +1100 | [diff] [blame] | 787 | if (!pnv_pci_cfg_check(pdn)) |
| 788 | return PCIBIOS_DEVICE_NOT_FOUND; |
| 789 | |
| 790 | ret = pnv_pci_cfg_write(pdn, where, size, val); |
| 791 | phb = pdn->phb->private_data; |
Gavin Shan | d0914f5 | 2014-04-24 18:00:12 +1000 | [diff] [blame] | 792 | if (!(phb->flags & PNV_PHB_FLAG_EEH)) |
Gavin Shan | 3532a741 | 2015-03-17 16:15:03 +1100 | [diff] [blame] | 793 | pnv_pci_config_check_eeh(pdn); |
Gavin Shan | d0914f5 | 2014-04-24 18:00:12 +1000 | [diff] [blame] | 794 | |
| 795 | return ret; |
Gavin Shan | 9bf41be | 2013-06-27 13:46:48 +0800 | [diff] [blame] | 796 | } |
| 797 | |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 798 | struct pci_ops pnv_pci_ops = { |
Gavin Shan | 9bf41be | 2013-06-27 13:46:48 +0800 | [diff] [blame] | 799 | .read = pnv_pci_read_config, |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 800 | .write = pnv_pci_write_config, |
| 801 | }; |
| 802 | |
Alexey Kardashevskiy | 0eaf4de | 2015-06-05 16:35:09 +1000 | [diff] [blame] | 803 | struct iommu_table *pnv_pci_table_alloc(int nid) |
| 804 | { |
| 805 | struct iommu_table *tbl; |
| 806 | |
| 807 | tbl = kzalloc_node(sizeof(struct iommu_table), GFP_KERNEL, nid); |
Alexey Kardashevskiy | 82eae1a | 2017-03-27 19:27:37 +1100 | [diff] [blame] | 808 | if (!tbl) |
| 809 | return NULL; |
| 810 | |
Alexey Kardashevskiy | 0eaf4de | 2015-06-05 16:35:09 +1000 | [diff] [blame] | 811 | INIT_LIST_HEAD_RCU(&tbl->it_group_list); |
Alexey Kardashevskiy | e5afdf9 | 2017-03-22 15:21:50 +1100 | [diff] [blame] | 812 | kref_init(&tbl->it_kref); |
Alexey Kardashevskiy | 0eaf4de | 2015-06-05 16:35:09 +1000 | [diff] [blame] | 813 | |
| 814 | return tbl; |
| 815 | } |
| 816 | |
Daniel Axtens | 92ae035 | 2015-04-28 15:12:05 +1000 | [diff] [blame] | 817 | void pnv_pci_dma_dev_setup(struct pci_dev *pdev) |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 818 | { |
| 819 | struct pci_controller *hose = pci_bus_to_host(pdev->bus); |
| 820 | struct pnv_phb *phb = hose->private_data; |
Wei Yang | 781a868 | 2015-03-25 16:23:57 +0800 | [diff] [blame] | 821 | #ifdef CONFIG_PCI_IOV |
| 822 | struct pnv_ioda_pe *pe; |
| 823 | struct pci_dn *pdn; |
| 824 | |
| 825 | /* Fix the VF pdn PE number */ |
| 826 | if (pdev->is_virtfn) { |
| 827 | pdn = pci_get_pdn(pdev); |
| 828 | WARN_ON(pdn->pe_number != IODA_INVALID_PE); |
| 829 | list_for_each_entry(pe, &phb->ioda.pe_list, list) { |
| 830 | if (pe->rid == ((pdev->bus->number << 8) | |
| 831 | (pdev->devfn & 0xff))) { |
| 832 | pdn->pe_number = pe->pe_number; |
| 833 | pe->pdev = pdev; |
| 834 | break; |
| 835 | } |
| 836 | } |
| 837 | } |
| 838 | #endif /* CONFIG_PCI_IOV */ |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 839 | |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 840 | if (phb && phb->dma_dev_setup) |
| 841 | phb->dma_dev_setup(phb, pdev); |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 842 | } |
| 843 | |
Gavin Shan | 1bc74f1 | 2016-02-09 15:50:22 +1100 | [diff] [blame] | 844 | void pnv_pci_dma_bus_setup(struct pci_bus *bus) |
| 845 | { |
| 846 | struct pci_controller *hose = bus->sysdata; |
| 847 | struct pnv_phb *phb = hose->private_data; |
| 848 | struct pnv_ioda_pe *pe; |
| 849 | |
| 850 | list_for_each_entry(pe, &phb->ioda.pe_list, list) { |
| 851 | if (!(pe->flags & (PNV_IODA_PE_BUS | PNV_IODA_PE_BUS_ALL))) |
| 852 | continue; |
| 853 | |
| 854 | if (!pe->pbus) |
| 855 | continue; |
| 856 | |
| 857 | if (bus->number == ((pe->rid >> 8) & 0xFF)) { |
| 858 | pe->pbus = bus; |
| 859 | break; |
| 860 | } |
| 861 | } |
| 862 | } |
| 863 | |
Frederic Barrat | 2552910 | 2017-08-04 11:55:14 +0200 | [diff] [blame] | 864 | int pnv_pci_set_p2p(struct pci_dev *initiator, struct pci_dev *target, u64 desc) |
| 865 | { |
| 866 | struct pci_controller *hose; |
| 867 | struct pnv_phb *phb_init, *phb_target; |
| 868 | struct pnv_ioda_pe *pe_init; |
| 869 | int rc; |
| 870 | |
| 871 | if (!opal_check_token(OPAL_PCI_SET_P2P)) |
| 872 | return -ENXIO; |
| 873 | |
| 874 | hose = pci_bus_to_host(initiator->bus); |
| 875 | phb_init = hose->private_data; |
| 876 | |
| 877 | hose = pci_bus_to_host(target->bus); |
| 878 | phb_target = hose->private_data; |
| 879 | |
| 880 | pe_init = pnv_ioda_get_pe(initiator); |
| 881 | if (!pe_init) |
| 882 | return -ENODEV; |
| 883 | |
| 884 | /* |
| 885 | * Configuring the initiator's PHB requires to adjust its |
| 886 | * TVE#1 setting. Since the same device can be an initiator |
| 887 | * several times for different target devices, we need to keep |
| 888 | * a reference count to know when we can restore the default |
| 889 | * bypass setting on its TVE#1 when disabling. Opal is not |
| 890 | * tracking PE states, so we add a reference count on the PE |
| 891 | * in linux. |
| 892 | * |
| 893 | * For the target, the configuration is per PHB, so we keep a |
| 894 | * target reference count on the PHB. |
| 895 | */ |
| 896 | mutex_lock(&p2p_mutex); |
| 897 | |
| 898 | if (desc & OPAL_PCI_P2P_ENABLE) { |
| 899 | /* always go to opal to validate the configuration */ |
| 900 | rc = opal_pci_set_p2p(phb_init->opal_id, phb_target->opal_id, |
| 901 | desc, pe_init->pe_number); |
| 902 | |
| 903 | if (rc != OPAL_SUCCESS) { |
| 904 | rc = -EIO; |
| 905 | goto out; |
| 906 | } |
| 907 | |
| 908 | pe_init->p2p_initiator_count++; |
| 909 | phb_target->p2p_target_count++; |
| 910 | } else { |
| 911 | if (!pe_init->p2p_initiator_count || |
| 912 | !phb_target->p2p_target_count) { |
| 913 | rc = -EINVAL; |
| 914 | goto out; |
| 915 | } |
| 916 | |
| 917 | if (--pe_init->p2p_initiator_count == 0) |
| 918 | pnv_pci_ioda2_set_bypass(pe_init, true); |
| 919 | |
| 920 | if (--phb_target->p2p_target_count == 0) { |
| 921 | rc = opal_pci_set_p2p(phb_init->opal_id, |
| 922 | phb_target->opal_id, desc, |
| 923 | pe_init->pe_number); |
| 924 | if (rc != OPAL_SUCCESS) { |
| 925 | rc = -EIO; |
| 926 | goto out; |
| 927 | } |
| 928 | } |
| 929 | } |
| 930 | rc = 0; |
| 931 | out: |
| 932 | mutex_unlock(&p2p_mutex); |
| 933 | return rc; |
| 934 | } |
| 935 | EXPORT_SYMBOL_GPL(pnv_pci_set_p2p); |
| 936 | |
Philippe Bergheaud | d6a90bb | 2018-03-02 10:56:11 +0100 | [diff] [blame] | 937 | struct device_node *pnv_pci_get_phb_node(struct pci_dev *dev) |
| 938 | { |
| 939 | struct pci_controller *hose = pci_bus_to_host(dev->bus); |
| 940 | |
| 941 | return of_node_get(hose->dn); |
| 942 | } |
| 943 | EXPORT_SYMBOL(pnv_pci_get_phb_node); |
| 944 | |
| 945 | int pnv_pci_enable_tunnel(struct pci_dev *dev, u64 *asnind) |
| 946 | { |
| 947 | struct device_node *np; |
| 948 | const __be32 *prop; |
| 949 | struct pnv_ioda_pe *pe; |
| 950 | uint16_t window_id; |
| 951 | int rc; |
| 952 | |
| 953 | if (!radix_enabled()) |
| 954 | return -ENXIO; |
| 955 | |
| 956 | if (!(np = pnv_pci_get_phb_node(dev))) |
| 957 | return -ENXIO; |
| 958 | |
| 959 | prop = of_get_property(np, "ibm,phb-indications", NULL); |
| 960 | of_node_put(np); |
| 961 | |
| 962 | if (!prop || !prop[1]) |
| 963 | return -ENXIO; |
| 964 | |
| 965 | *asnind = (u64)be32_to_cpu(prop[1]); |
| 966 | pe = pnv_ioda_get_pe(dev); |
| 967 | if (!pe) |
| 968 | return -ENODEV; |
| 969 | |
| 970 | /* Increase real window size to accept as_notify messages. */ |
| 971 | window_id = (pe->pe_number << 1 ) + 1; |
| 972 | rc = opal_pci_map_pe_dma_window_real(pe->phb->opal_id, pe->pe_number, |
| 973 | window_id, pe->tce_bypass_base, |
| 974 | (uint64_t)1 << 48); |
| 975 | return opal_error_code(rc); |
| 976 | } |
| 977 | EXPORT_SYMBOL_GPL(pnv_pci_enable_tunnel); |
| 978 | |
| 979 | int pnv_pci_disable_tunnel(struct pci_dev *dev) |
| 980 | { |
| 981 | struct pnv_ioda_pe *pe; |
| 982 | |
| 983 | pe = pnv_ioda_get_pe(dev); |
| 984 | if (!pe) |
| 985 | return -ENODEV; |
| 986 | |
| 987 | /* Restore default real window size. */ |
| 988 | pnv_pci_ioda2_set_bypass(pe, true); |
| 989 | return 0; |
| 990 | } |
| 991 | EXPORT_SYMBOL_GPL(pnv_pci_disable_tunnel); |
| 992 | |
| 993 | int pnv_pci_set_tunnel_bar(struct pci_dev *dev, u64 addr, int enable) |
| 994 | { |
| 995 | __be64 val; |
| 996 | struct pci_controller *hose; |
| 997 | struct pnv_phb *phb; |
| 998 | u64 tunnel_bar; |
| 999 | int rc; |
| 1000 | |
| 1001 | if (!opal_check_token(OPAL_PCI_GET_PBCQ_TUNNEL_BAR)) |
| 1002 | return -ENXIO; |
| 1003 | if (!opal_check_token(OPAL_PCI_SET_PBCQ_TUNNEL_BAR)) |
| 1004 | return -ENXIO; |
| 1005 | |
| 1006 | hose = pci_bus_to_host(dev->bus); |
| 1007 | phb = hose->private_data; |
| 1008 | |
| 1009 | mutex_lock(&tunnel_mutex); |
| 1010 | rc = opal_pci_get_pbcq_tunnel_bar(phb->opal_id, &val); |
| 1011 | if (rc != OPAL_SUCCESS) { |
| 1012 | rc = -EIO; |
| 1013 | goto out; |
| 1014 | } |
| 1015 | tunnel_bar = be64_to_cpu(val); |
| 1016 | if (enable) { |
| 1017 | /* |
| 1018 | * Only one device per PHB can use atomics. |
| 1019 | * Our policy is first-come, first-served. |
| 1020 | */ |
| 1021 | if (tunnel_bar) { |
| 1022 | if (tunnel_bar != addr) |
| 1023 | rc = -EBUSY; |
| 1024 | else |
| 1025 | rc = 0; /* Setting same address twice is ok */ |
| 1026 | goto out; |
| 1027 | } |
| 1028 | } else { |
| 1029 | /* |
| 1030 | * The device that owns atomics and wants to release |
| 1031 | * them must pass the same address with enable == 0. |
| 1032 | */ |
| 1033 | if (tunnel_bar != addr) { |
| 1034 | rc = -EPERM; |
| 1035 | goto out; |
| 1036 | } |
| 1037 | addr = 0x0ULL; |
| 1038 | } |
| 1039 | rc = opal_pci_set_pbcq_tunnel_bar(phb->opal_id, addr); |
| 1040 | rc = opal_error_code(rc); |
| 1041 | out: |
| 1042 | mutex_unlock(&tunnel_mutex); |
| 1043 | return rc; |
| 1044 | } |
| 1045 | EXPORT_SYMBOL_GPL(pnv_pci_set_tunnel_bar); |
| 1046 | |
| 1047 | #ifdef CONFIG_PPC64 /* for thread.tidr */ |
| 1048 | int pnv_pci_get_as_notify_info(struct task_struct *task, u32 *lpid, u32 *pid, |
| 1049 | u32 *tid) |
| 1050 | { |
| 1051 | struct mm_struct *mm = NULL; |
| 1052 | |
| 1053 | if (task == NULL) |
| 1054 | return -EINVAL; |
| 1055 | |
| 1056 | mm = get_task_mm(task); |
| 1057 | if (mm == NULL) |
| 1058 | return -EINVAL; |
| 1059 | |
| 1060 | *pid = mm->context.id; |
| 1061 | mmput(mm); |
| 1062 | |
| 1063 | *tid = task->thread.tidr; |
| 1064 | *lpid = mfspr(SPRN_LPID); |
| 1065 | return 0; |
| 1066 | } |
| 1067 | EXPORT_SYMBOL_GPL(pnv_pci_get_as_notify_info); |
| 1068 | #endif |
| 1069 | |
Benjamin Herrenschmidt | 73ed148 | 2013-05-10 16:59:18 +1000 | [diff] [blame] | 1070 | void pnv_pci_shutdown(void) |
| 1071 | { |
| 1072 | struct pci_controller *hose; |
| 1073 | |
Michael Neuling | 7a8e6bb | 2015-05-27 16:06:59 +1000 | [diff] [blame] | 1074 | list_for_each_entry(hose, &hose_list, list_node) |
| 1075 | if (hose->controller_ops.shutdown) |
| 1076 | hose->controller_ops.shutdown(hose); |
Benjamin Herrenschmidt | 73ed148 | 2013-05-10 16:59:18 +1000 | [diff] [blame] | 1077 | } |
| 1078 | |
Gavin Shan | aa0c033 | 2013-04-25 19:20:57 +0000 | [diff] [blame] | 1079 | /* Fixup wrong class code in p7ioc and p8 root complex */ |
Greg Kroah-Hartman | cad5cef | 2012-12-21 14:04:10 -0800 | [diff] [blame] | 1080 | static void pnv_p7ioc_rc_quirk(struct pci_dev *dev) |
Benjamin Herrenschmidt | ca45cfe | 2011-11-06 18:56:00 +0000 | [diff] [blame] | 1081 | { |
| 1082 | dev->class = PCI_CLASS_BRIDGE_PCI << 8; |
| 1083 | } |
| 1084 | DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_IBM, 0x3b9, pnv_p7ioc_rc_quirk); |
| 1085 | |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 1086 | void __init pnv_pci_init(void) |
| 1087 | { |
| 1088 | struct device_node *np; |
| 1089 | |
Bjorn Helgaas | 673c975 | 2012-02-23 20:18:58 -0700 | [diff] [blame] | 1090 | pci_add_flags(PCI_CAN_SKIP_ISA_ALIGN); |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 1091 | |
Michael Ellerman | 646b54f | 2015-03-12 17:27:11 +1100 | [diff] [blame] | 1092 | /* If we don't have OPAL, eg. in sim, just skip PCI probe */ |
| 1093 | if (!firmware_has_feature(FW_FEATURE_OPAL)) |
| 1094 | return; |
| 1095 | |
Russell Currey | 2de50e9 | 2016-02-08 15:08:20 +1100 | [diff] [blame] | 1096 | /* Look for IODA IO-Hubs. */ |
Michael Ellerman | 646b54f | 2015-03-12 17:27:11 +1100 | [diff] [blame] | 1097 | for_each_compatible_node(np, NULL, "ibm,ioda-hub") { |
| 1098 | pnv_pci_init_ioda_hub(np); |
Benjamin Herrenschmidt | 184cd4a | 2011-11-15 17:29:08 +0000 | [diff] [blame] | 1099 | } |
Benjamin Herrenschmidt | 184cd4a | 2011-11-15 17:29:08 +0000 | [diff] [blame] | 1100 | |
Michael Ellerman | 646b54f | 2015-03-12 17:27:11 +1100 | [diff] [blame] | 1101 | /* Look for ioda2 built-in PHB3's */ |
| 1102 | for_each_compatible_node(np, NULL, "ibm,ioda2-phb") |
| 1103 | pnv_pci_init_ioda2_phb(np); |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 1104 | |
Benjamin Herrenschmidt | fb11133 | 2016-07-08 16:37:09 +1000 | [diff] [blame] | 1105 | /* Look for ioda3 built-in PHB4's, we treat them as IODA2 */ |
| 1106 | for_each_compatible_node(np, NULL, "ibm,ioda3-phb") |
| 1107 | pnv_pci_init_ioda2_phb(np); |
| 1108 | |
Alistair Popple | 5d2aa71 | 2015-12-17 13:43:13 +1100 | [diff] [blame] | 1109 | /* Look for NPU PHBs */ |
| 1110 | for_each_compatible_node(np, NULL, "ibm,ioda2-npu-phb") |
| 1111 | pnv_pci_init_npu_phb(np); |
| 1112 | |
Alistair Popple | 616badd | 2017-01-10 15:41:44 +1100 | [diff] [blame] | 1113 | /* |
| 1114 | * Look for NPU2 PHBs which we treat mostly as NPU PHBs with |
| 1115 | * the exception of TCE kill which requires an OPAL call. |
| 1116 | */ |
| 1117 | for_each_compatible_node(np, NULL, "ibm,ioda2-npu2-phb") |
| 1118 | pnv_pci_init_npu_phb(np); |
| 1119 | |
Frederic Barrat | 7f2c39e | 2018-01-23 12:31:36 +0100 | [diff] [blame] | 1120 | /* Look for NPU2 OpenCAPI PHBs */ |
| 1121 | for_each_compatible_node(np, NULL, "ibm,ioda2-npu2-opencapi-phb") |
| 1122 | pnv_pci_init_npu2_opencapi_phb(np); |
| 1123 | |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 1124 | /* Configure IOMMU DMA hooks */ |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 1125 | set_pci_dma_ops(&dma_iommu_ops); |
Benjamin Herrenschmidt | 61305a9 | 2011-09-19 17:45:05 +0000 | [diff] [blame] | 1126 | } |
Alexey Kardashevskiy | d905c5d | 2013-11-21 17:43:14 +1100 | [diff] [blame] | 1127 | |
Alexey Kardashevskiy | c4e9d3c | 2018-12-19 19:52:21 +1100 | [diff] [blame] | 1128 | static int pnv_tce_iommu_bus_notifier(struct notifier_block *nb, |
| 1129 | unsigned long action, void *data) |
| 1130 | { |
| 1131 | struct device *dev = data; |
| 1132 | struct pci_dev *pdev; |
| 1133 | struct pci_dn *pdn; |
| 1134 | struct pnv_ioda_pe *pe; |
| 1135 | struct pci_controller *hose; |
| 1136 | struct pnv_phb *phb; |
| 1137 | |
| 1138 | switch (action) { |
| 1139 | case BUS_NOTIFY_ADD_DEVICE: |
| 1140 | pdev = to_pci_dev(dev); |
| 1141 | pdn = pci_get_pdn(pdev); |
| 1142 | hose = pci_bus_to_host(pdev->bus); |
| 1143 | phb = hose->private_data; |
| 1144 | |
| 1145 | WARN_ON_ONCE(!phb); |
| 1146 | if (!pdn || pdn->pe_number == IODA_INVALID_PE || !phb) |
| 1147 | return 0; |
| 1148 | |
| 1149 | pe = &phb->ioda.pe_array[pdn->pe_number]; |
| 1150 | iommu_add_device(&pe->table_group, dev); |
| 1151 | return 0; |
| 1152 | case BUS_NOTIFY_DEL_DEVICE: |
| 1153 | iommu_del_device(dev); |
| 1154 | return 0; |
| 1155 | default: |
| 1156 | return 0; |
| 1157 | } |
| 1158 | } |
| 1159 | |
| 1160 | static struct notifier_block pnv_tce_iommu_bus_nb = { |
| 1161 | .notifier_call = pnv_tce_iommu_bus_notifier, |
| 1162 | }; |
| 1163 | |
| 1164 | static int __init pnv_tce_iommu_bus_notifier_init(void) |
| 1165 | { |
| 1166 | bus_register_notifier(&pci_bus_type, &pnv_tce_iommu_bus_nb); |
| 1167 | return 0; |
| 1168 | } |
| 1169 | machine_subsys_initcall_sync(powernv, pnv_tce_iommu_bus_notifier_init); |