diff options
author | Michael Ellerman <michael@ellerman.id.au> | 2011-08-08 08:30:55 -0400 |
---|---|---|
committer | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2011-09-19 19:19:55 -0400 |
commit | f9a71e0fd1b44148d7af6ce2fecfb2cf7a4df636 (patch) | |
tree | 1154460114422872f7a02ad086726e7b23740135 | |
parent | f352c7251255effe6c2326190f1378adbd142aa3 (diff) |
powerpc/wsp: Add MSI support for PCI on PowerEN
Based on a patch by Michael Ellerman <michael@ellerman.id.au>
Patch was simply forward ported upstream.
Jimi Xenidis <jimix@pobox.com>
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r-- | arch/powerpc/platforms/wsp/Makefile | 1 | ||||
-rw-r--r-- | arch/powerpc/platforms/wsp/ics.c | 48 | ||||
-rw-r--r-- | arch/powerpc/platforms/wsp/ics.h | 5 | ||||
-rw-r--r-- | arch/powerpc/platforms/wsp/msi.c | 102 | ||||
-rw-r--r-- | arch/powerpc/platforms/wsp/msi.h | 19 |
5 files changed, 175 insertions, 0 deletions
diff --git a/arch/powerpc/platforms/wsp/Makefile b/arch/powerpc/platforms/wsp/Makefile index 84519f8d894..a1486b436f0 100644 --- a/arch/powerpc/platforms/wsp/Makefile +++ b/arch/powerpc/platforms/wsp/Makefile | |||
@@ -5,3 +5,4 @@ obj-$(CONFIG_PPC_PSR2) += psr2.o opb_pic.o | |||
5 | obj-$(CONFIG_PPC_WSP) += scom_wsp.o | 5 | obj-$(CONFIG_PPC_WSP) += scom_wsp.o |
6 | obj-$(CONFIG_SMP) += smp.o scom_smp.o | 6 | obj-$(CONFIG_SMP) += smp.o scom_smp.o |
7 | obj-$(CONFIG_PCI) += wsp_pci.o | 7 | obj-$(CONFIG_PCI) += wsp_pci.o |
8 | obj-$(CONFIG_PCI_MSI) += msi.o \ No newline at end of file | ||
diff --git a/arch/powerpc/platforms/wsp/ics.c b/arch/powerpc/platforms/wsp/ics.c index e53bd9e7b12..57687439254 100644 --- a/arch/powerpc/platforms/wsp/ics.c +++ b/arch/powerpc/platforms/wsp/ics.c | |||
@@ -710,3 +710,51 @@ void __init wsp_init_irq(void) | |||
710 | /* We need to patch our irq chip's EOI to point to the right ICP */ | 710 | /* We need to patch our irq chip's EOI to point to the right ICP */ |
711 | wsp_irq_chip.irq_eoi = icp_ops->eoi; | 711 | wsp_irq_chip.irq_eoi = icp_ops->eoi; |
712 | } | 712 | } |
713 | |||
714 | #ifdef CONFIG_PCI_MSI | ||
715 | static void wsp_ics_msi_unmask_irq(struct irq_data *d) | ||
716 | { | ||
717 | wsp_chip_unmask_irq(d); | ||
718 | unmask_msi_irq(d); | ||
719 | } | ||
720 | |||
721 | static unsigned int wsp_ics_msi_startup(struct irq_data *d) | ||
722 | { | ||
723 | wsp_ics_msi_unmask_irq(d); | ||
724 | return 0; | ||
725 | } | ||
726 | |||
727 | static void wsp_ics_msi_mask_irq(struct irq_data *d) | ||
728 | { | ||
729 | mask_msi_irq(d); | ||
730 | wsp_chip_mask_irq(d); | ||
731 | } | ||
732 | |||
733 | /* | ||
734 | * we do it this way because we reassinge default EOI handling in | ||
735 | * irq_init() above | ||
736 | */ | ||
737 | static void wsp_ics_eoi(struct irq_data *data) | ||
738 | { | ||
739 | wsp_irq_chip.irq_eoi(data); | ||
740 | } | ||
741 | |||
742 | static struct irq_chip wsp_ics_msi = { | ||
743 | .name = "WSP ICS MSI", | ||
744 | .irq_startup = wsp_ics_msi_startup, | ||
745 | .irq_mask = wsp_ics_msi_mask_irq, | ||
746 | .irq_unmask = wsp_ics_msi_unmask_irq, | ||
747 | .irq_eoi = wsp_ics_eoi, | ||
748 | .irq_set_affinity = wsp_chip_set_affinity | ||
749 | }; | ||
750 | |||
751 | void wsp_ics_set_msi_chip(unsigned int irq) | ||
752 | { | ||
753 | irq_set_chip(irq, &wsp_ics_msi); | ||
754 | } | ||
755 | |||
756 | void wsp_ics_set_std_chip(unsigned int irq) | ||
757 | { | ||
758 | irq_set_chip(irq, &wsp_irq_chip); | ||
759 | } | ||
760 | #endif /* CONFIG_PCI_MSI */ | ||
diff --git a/arch/powerpc/platforms/wsp/ics.h b/arch/powerpc/platforms/wsp/ics.h index e34d5310264..07b644e0cf9 100644 --- a/arch/powerpc/platforms/wsp/ics.h +++ b/arch/powerpc/platforms/wsp/ics.h | |||
@@ -17,4 +17,9 @@ extern void wsp_init_irq(void); | |||
17 | extern int wsp_ics_alloc_irq(struct device_node *dn, int num); | 17 | extern int wsp_ics_alloc_irq(struct device_node *dn, int num); |
18 | extern void wsp_ics_free_irq(struct device_node *dn, unsigned int irq); | 18 | extern void wsp_ics_free_irq(struct device_node *dn, unsigned int irq); |
19 | 19 | ||
20 | #ifdef CONFIG_PCI_MSI | ||
21 | extern void wsp_ics_set_msi_chip(unsigned int irq); | ||
22 | extern void wsp_ics_set_std_chip(unsigned int irq); | ||
23 | #endif /* CONFIG_PCI_MSI */ | ||
24 | |||
20 | #endif /* __ICS_H */ | 25 | #endif /* __ICS_H */ |
diff --git a/arch/powerpc/platforms/wsp/msi.c b/arch/powerpc/platforms/wsp/msi.c new file mode 100644 index 00000000000..380882f27ad --- /dev/null +++ b/arch/powerpc/platforms/wsp/msi.c | |||
@@ -0,0 +1,102 @@ | |||
1 | /* | ||
2 | * Copyright 2011 Michael Ellerman, 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 | #include <linux/kernel.h> | ||
11 | #include <linux/pci.h> | ||
12 | #include <linux/msi.h> | ||
13 | #include <linux/irq.h> | ||
14 | #include <linux/interrupt.h> | ||
15 | |||
16 | #include "msi.h" | ||
17 | #include "ics.h" | ||
18 | #include "wsp_pci.h" | ||
19 | |||
20 | /* Magic addresses for 32 & 64-bit MSIs with hardcoded MVE 0 */ | ||
21 | #define MSI_ADDR_32 0xFFFF0000ul | ||
22 | #define MSI_ADDR_64 0x1000000000000000ul | ||
23 | |||
24 | int wsp_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) | ||
25 | { | ||
26 | struct pci_controller *phb; | ||
27 | struct msi_desc *entry; | ||
28 | struct msi_msg msg; | ||
29 | unsigned int virq; | ||
30 | int hwirq; | ||
31 | |||
32 | phb = pci_bus_to_host(dev->bus); | ||
33 | if (!phb) | ||
34 | return -ENOENT; | ||
35 | |||
36 | entry = list_first_entry(&dev->msi_list, struct msi_desc, list); | ||
37 | if (entry->msi_attrib.is_64) { | ||
38 | msg.address_lo = 0; | ||
39 | msg.address_hi = MSI_ADDR_64 >> 32; | ||
40 | } else { | ||
41 | msg.address_lo = MSI_ADDR_32; | ||
42 | msg.address_hi = 0; | ||
43 | } | ||
44 | |||
45 | list_for_each_entry(entry, &dev->msi_list, list) { | ||
46 | hwirq = wsp_ics_alloc_irq(phb->dn, 1); | ||
47 | if (hwirq < 0) { | ||
48 | dev_warn(&dev->dev, "wsp_msi: hwirq alloc failed!\n"); | ||
49 | return hwirq; | ||
50 | } | ||
51 | |||
52 | virq = irq_create_mapping(NULL, hwirq); | ||
53 | if (virq == NO_IRQ) { | ||
54 | dev_warn(&dev->dev, "wsp_msi: virq alloc failed!\n"); | ||
55 | return -1; | ||
56 | } | ||
57 | |||
58 | dev_dbg(&dev->dev, "wsp_msi: allocated irq %#x/%#x\n", | ||
59 | hwirq, virq); | ||
60 | |||
61 | wsp_ics_set_msi_chip(virq); | ||
62 | irq_set_msi_desc(virq, entry); | ||
63 | msg.data = hwirq & XIVE_ADDR_MASK; | ||
64 | write_msi_msg(virq, &msg); | ||
65 | } | ||
66 | |||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | void wsp_teardown_msi_irqs(struct pci_dev *dev) | ||
71 | { | ||
72 | struct pci_controller *phb; | ||
73 | struct msi_desc *entry; | ||
74 | int hwirq; | ||
75 | |||
76 | phb = pci_bus_to_host(dev->bus); | ||
77 | |||
78 | dev_dbg(&dev->dev, "wsp_msi: tearing down msi irqs\n"); | ||
79 | |||
80 | list_for_each_entry(entry, &dev->msi_list, list) { | ||
81 | if (entry->irq == NO_IRQ) | ||
82 | continue; | ||
83 | |||
84 | irq_set_msi_desc(entry->irq, NULL); | ||
85 | wsp_ics_set_std_chip(entry->irq); | ||
86 | |||
87 | hwirq = virq_to_hw(entry->irq); | ||
88 | /* In this order to avoid racing with irq_create_mapping() */ | ||
89 | irq_dispose_mapping(entry->irq); | ||
90 | wsp_ics_free_irq(phb->dn, hwirq); | ||
91 | } | ||
92 | } | ||
93 | |||
94 | void wsp_setup_phb_msi(struct pci_controller *phb) | ||
95 | { | ||
96 | /* Create a single MVE at offset 0 that matches everything */ | ||
97 | out_be64(phb->cfg_data + PCIE_REG_IODA_ADDR, PCIE_REG_IODA_AD_TBL_MVT); | ||
98 | out_be64(phb->cfg_data + PCIE_REG_IODA_DATA0, 1ull << 63); | ||
99 | |||
100 | ppc_md.setup_msi_irqs = wsp_setup_msi_irqs; | ||
101 | ppc_md.teardown_msi_irqs = wsp_teardown_msi_irqs; | ||
102 | } | ||
diff --git a/arch/powerpc/platforms/wsp/msi.h b/arch/powerpc/platforms/wsp/msi.h new file mode 100644 index 00000000000..0ab27b71b24 --- /dev/null +++ b/arch/powerpc/platforms/wsp/msi.h | |||
@@ -0,0 +1,19 @@ | |||
1 | /* | ||
2 | * Copyright 2011 Michael Ellerman, 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 __WSP_MSI_H | ||
11 | #define __WSP_MSI_H | ||
12 | |||
13 | #ifdef CONFIG_PCI_MSI | ||
14 | extern void wsp_setup_phb_msi(struct pci_controller *phb); | ||
15 | #else | ||
16 | static inline void wsp_setup_phb_msi(struct pci_controller *phb) { } | ||
17 | #endif | ||
18 | |||
19 | #endif /* __WSP_MSI_H */ | ||