diff options
author | Ian Munsie <imunsie@au1.ibm.com> | 2014-10-08 04:54:57 -0400 |
---|---|---|
committer | Michael Ellerman <mpe@ellerman.id.au> | 2014-10-08 05:15:44 -0400 |
commit | 80c49c7e4a397bb245d72a78e41640eeb0b6e953 (patch) | |
tree | b11549904a62033262d981e1e981b9ba01ec5bb0 /arch | |
parent | 1cd258d7faccb330145f08d838608b2c6ad41604 (diff) |
powerpc/powerpc: Add new PCIe functions for allocating cxl interrupts
This adds a number of functions for allocating IRQs under powernv PCIe for cxl.
Signed-off-by: Ian Munsie <imunsie@au1.ibm.com>
Signed-off-by: Michael Neuling <mikey@neuling.org>
Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Diffstat (limited to 'arch')
-rw-r--r-- | arch/powerpc/include/asm/pnv-pci.h | 31 | ||||
-rw-r--r-- | arch/powerpc/platforms/powernv/pci-ioda.c | 154 |
2 files changed, 185 insertions, 0 deletions
diff --git a/arch/powerpc/include/asm/pnv-pci.h b/arch/powerpc/include/asm/pnv-pci.h new file mode 100644 index 000000000000..f09a22fa1bd7 --- /dev/null +++ b/arch/powerpc/include/asm/pnv-pci.h | |||
@@ -0,0 +1,31 @@ | |||
1 | /* | ||
2 | * Copyright 2014 IBM Corp. | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or | ||
5 | * modify it under the terms of the GNU General Public License | ||
6 | * as published by the Free Software Foundation; either version | ||
7 | * 2 of the License, or (at your option) any later version. | ||
8 | */ | ||
9 | |||
10 | #ifndef _ASM_PNV_PCI_H | ||
11 | #define _ASM_PNV_PCI_H | ||
12 | |||
13 | #include <linux/pci.h> | ||
14 | #include <misc/cxl.h> | ||
15 | |||
16 | int pnv_phb_to_cxl(struct pci_dev *dev); | ||
17 | int pnv_cxl_ioda_msi_setup(struct pci_dev *dev, unsigned int hwirq, | ||
18 | unsigned int virq); | ||
19 | int pnv_cxl_alloc_hwirqs(struct pci_dev *dev, int num); | ||
20 | void pnv_cxl_release_hwirqs(struct pci_dev *dev, int hwirq, int num); | ||
21 | int pnv_cxl_get_irq_count(struct pci_dev *dev); | ||
22 | struct device_node *pnv_pci_to_phb_node(struct pci_dev *dev); | ||
23 | |||
24 | #ifdef CONFIG_CXL_BASE | ||
25 | int pnv_cxl_alloc_hwirq_ranges(struct cxl_irq_ranges *irqs, | ||
26 | struct pci_dev *dev, int num); | ||
27 | void pnv_cxl_release_hwirq_ranges(struct cxl_irq_ranges *irqs, | ||
28 | struct pci_dev *dev); | ||
29 | #endif | ||
30 | |||
31 | #endif | ||
diff --git a/arch/powerpc/platforms/powernv/pci-ioda.c b/arch/powerpc/platforms/powernv/pci-ioda.c index 56abb5240995..468a0f23c7f2 100644 --- a/arch/powerpc/platforms/powernv/pci-ioda.c +++ b/arch/powerpc/platforms/powernv/pci-ioda.c | |||
@@ -37,6 +37,9 @@ | |||
37 | #include <asm/xics.h> | 37 | #include <asm/xics.h> |
38 | #include <asm/debug.h> | 38 | #include <asm/debug.h> |
39 | #include <asm/firmware.h> | 39 | #include <asm/firmware.h> |
40 | #include <asm/pnv-pci.h> | ||
41 | |||
42 | #include <misc/cxl.h> | ||
40 | 43 | ||
41 | #include "powernv.h" | 44 | #include "powernv.h" |
42 | #include "pci.h" | 45 | #include "pci.h" |
@@ -1350,6 +1353,157 @@ static void set_msi_irq_chip(struct pnv_phb *phb, unsigned int virq) | |||
1350 | irq_set_chip(virq, &phb->ioda.irq_chip); | 1353 | irq_set_chip(virq, &phb->ioda.irq_chip); |
1351 | } | 1354 | } |
1352 | 1355 | ||
1356 | #ifdef CONFIG_CXL_BASE | ||
1357 | |||
1358 | struct device_node *pnv_pci_to_phb_node(struct pci_dev *dev) | ||
1359 | { | ||
1360 | struct pci_controller *hose = pci_bus_to_host(dev->bus); | ||
1361 | |||
1362 | return hose->dn; | ||
1363 | } | ||
1364 | EXPORT_SYMBOL(pnv_pci_to_phb_node); | ||
1365 | |||
1366 | int pnv_phb_to_cxl(struct pci_dev *dev) | ||
1367 | { | ||
1368 | struct pci_controller *hose = pci_bus_to_host(dev->bus); | ||
1369 | struct pnv_phb *phb = hose->private_data; | ||
1370 | struct pnv_ioda_pe *pe; | ||
1371 | int rc; | ||
1372 | |||
1373 | pe = pnv_ioda_get_pe(dev); | ||
1374 | if (!pe) | ||
1375 | return -ENODEV; | ||
1376 | |||
1377 | pe_info(pe, "Switching PHB to CXL\n"); | ||
1378 | |||
1379 | rc = opal_pci_set_phb_cxl_mode(phb->opal_id, 1, pe->pe_number); | ||
1380 | if (rc) | ||
1381 | dev_err(&dev->dev, "opal_pci_set_phb_cxl_mode failed: %i\n", rc); | ||
1382 | |||
1383 | return rc; | ||
1384 | } | ||
1385 | EXPORT_SYMBOL(pnv_phb_to_cxl); | ||
1386 | |||
1387 | /* Find PHB for cxl dev and allocate MSI hwirqs? | ||
1388 | * Returns the absolute hardware IRQ number | ||
1389 | */ | ||
1390 | int pnv_cxl_alloc_hwirqs(struct pci_dev *dev, int num) | ||
1391 | { | ||
1392 | struct pci_controller *hose = pci_bus_to_host(dev->bus); | ||
1393 | struct pnv_phb *phb = hose->private_data; | ||
1394 | int hwirq = msi_bitmap_alloc_hwirqs(&phb->msi_bmp, num); | ||
1395 | |||
1396 | if (hwirq < 0) { | ||
1397 | dev_warn(&dev->dev, "Failed to find a free MSI\n"); | ||
1398 | return -ENOSPC; | ||
1399 | } | ||
1400 | |||
1401 | return phb->msi_base + hwirq; | ||
1402 | } | ||
1403 | EXPORT_SYMBOL(pnv_cxl_alloc_hwirqs); | ||
1404 | |||
1405 | void pnv_cxl_release_hwirqs(struct pci_dev *dev, int hwirq, int num) | ||
1406 | { | ||
1407 | struct pci_controller *hose = pci_bus_to_host(dev->bus); | ||
1408 | struct pnv_phb *phb = hose->private_data; | ||
1409 | |||
1410 | msi_bitmap_free_hwirqs(&phb->msi_bmp, hwirq - phb->msi_base, num); | ||
1411 | } | ||
1412 | EXPORT_SYMBOL(pnv_cxl_release_hwirqs); | ||
1413 | |||
1414 | void pnv_cxl_release_hwirq_ranges(struct cxl_irq_ranges *irqs, | ||
1415 | struct pci_dev *dev) | ||
1416 | { | ||
1417 | struct pci_controller *hose = pci_bus_to_host(dev->bus); | ||
1418 | struct pnv_phb *phb = hose->private_data; | ||
1419 | int i, hwirq; | ||
1420 | |||
1421 | for (i = 1; i < CXL_IRQ_RANGES; i++) { | ||
1422 | if (!irqs->range[i]) | ||
1423 | continue; | ||
1424 | pr_devel("cxl release irq range 0x%x: offset: 0x%lx limit: %ld\n", | ||
1425 | i, irqs->offset[i], | ||
1426 | irqs->range[i]); | ||
1427 | hwirq = irqs->offset[i] - phb->msi_base; | ||
1428 | msi_bitmap_free_hwirqs(&phb->msi_bmp, hwirq, | ||
1429 | irqs->range[i]); | ||
1430 | } | ||
1431 | } | ||
1432 | EXPORT_SYMBOL(pnv_cxl_release_hwirq_ranges); | ||
1433 | |||
1434 | int pnv_cxl_alloc_hwirq_ranges(struct cxl_irq_ranges *irqs, | ||
1435 | struct pci_dev *dev, int num) | ||
1436 | { | ||
1437 | struct pci_controller *hose = pci_bus_to_host(dev->bus); | ||
1438 | struct pnv_phb *phb = hose->private_data; | ||
1439 | int i, hwirq, try; | ||
1440 | |||
1441 | memset(irqs, 0, sizeof(struct cxl_irq_ranges)); | ||
1442 | |||
1443 | /* 0 is reserved for the multiplexed PSL DSI interrupt */ | ||
1444 | for (i = 1; i < CXL_IRQ_RANGES && num; i++) { | ||
1445 | try = num; | ||
1446 | while (try) { | ||
1447 | hwirq = msi_bitmap_alloc_hwirqs(&phb->msi_bmp, try); | ||
1448 | if (hwirq >= 0) | ||
1449 | break; | ||
1450 | try /= 2; | ||
1451 | } | ||
1452 | if (!try) | ||
1453 | goto fail; | ||
1454 | |||
1455 | irqs->offset[i] = phb->msi_base + hwirq; | ||
1456 | irqs->range[i] = try; | ||
1457 | pr_devel("cxl alloc irq range 0x%x: offset: 0x%lx limit: %li\n", | ||
1458 | i, irqs->offset[i], irqs->range[i]); | ||
1459 | num -= try; | ||
1460 | } | ||
1461 | if (num) | ||
1462 | goto fail; | ||
1463 | |||
1464 | return 0; | ||
1465 | fail: | ||
1466 | pnv_cxl_release_hwirq_ranges(irqs, dev); | ||
1467 | return -ENOSPC; | ||
1468 | } | ||
1469 | EXPORT_SYMBOL(pnv_cxl_alloc_hwirq_ranges); | ||
1470 | |||
1471 | int pnv_cxl_get_irq_count(struct pci_dev *dev) | ||
1472 | { | ||
1473 | struct pci_controller *hose = pci_bus_to_host(dev->bus); | ||
1474 | struct pnv_phb *phb = hose->private_data; | ||
1475 | |||
1476 | return phb->msi_bmp.irq_count; | ||
1477 | } | ||
1478 | EXPORT_SYMBOL(pnv_cxl_get_irq_count); | ||
1479 | |||
1480 | int pnv_cxl_ioda_msi_setup(struct pci_dev *dev, unsigned int hwirq, | ||
1481 | unsigned int virq) | ||
1482 | { | ||
1483 | struct pci_controller *hose = pci_bus_to_host(dev->bus); | ||
1484 | struct pnv_phb *phb = hose->private_data; | ||
1485 | unsigned int xive_num = hwirq - phb->msi_base; | ||
1486 | struct pnv_ioda_pe *pe; | ||
1487 | int rc; | ||
1488 | |||
1489 | if (!(pe = pnv_ioda_get_pe(dev))) | ||
1490 | return -ENODEV; | ||
1491 | |||
1492 | /* Assign XIVE to PE */ | ||
1493 | rc = opal_pci_set_xive_pe(phb->opal_id, pe->pe_number, xive_num); | ||
1494 | if (rc) { | ||
1495 | pe_warn(pe, "%s: OPAL error %d setting msi_base 0x%x " | ||
1496 | "hwirq 0x%x XIVE 0x%x PE\n", | ||
1497 | pci_name(dev), rc, phb->msi_base, hwirq, xive_num); | ||
1498 | return -EIO; | ||
1499 | } | ||
1500 | set_msi_irq_chip(phb, virq); | ||
1501 | |||
1502 | return 0; | ||
1503 | } | ||
1504 | EXPORT_SYMBOL(pnv_cxl_ioda_msi_setup); | ||
1505 | #endif | ||
1506 | |||
1353 | static int pnv_pci_ioda_msi_setup(struct pnv_phb *phb, struct pci_dev *dev, | 1507 | static int pnv_pci_ioda_msi_setup(struct pnv_phb *phb, struct pci_dev *dev, |
1354 | unsigned int hwirq, unsigned int virq, | 1508 | unsigned int hwirq, unsigned int virq, |
1355 | unsigned int is_64, struct msi_msg *msg) | 1509 | unsigned int is_64, struct msi_msg *msg) |