aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--arch/powerpc/platforms/pasemi/setup.c2
-rw-r--r--arch/powerpc/sysdev/Makefile2
-rw-r--r--arch/powerpc/sysdev/mpic.c20
-rw-r--r--arch/powerpc/sysdev/mpic.h7
-rw-r--r--arch/powerpc/sysdev/mpic_pasemi_msi.c172
5 files changed, 201 insertions, 2 deletions
diff --git a/arch/powerpc/platforms/pasemi/setup.c b/arch/powerpc/platforms/pasemi/setup.c
index 6d7d068ceba0..b5dfd4252110 100644
--- a/arch/powerpc/platforms/pasemi/setup.c
+++ b/arch/powerpc/platforms/pasemi/setup.c
@@ -223,7 +223,7 @@ static __init void pas_init_IRQ(void)
223 223
224 mpic = mpic_alloc(mpic_node, openpic_addr, 224 mpic = mpic_alloc(mpic_node, openpic_addr,
225 MPIC_PRIMARY|MPIC_LARGE_VECTORS, 225 MPIC_PRIMARY|MPIC_LARGE_VECTORS,
226 0, 0, " PAS-OPIC "); 226 0, 0, "PASEMI-OPIC");
227 BUG_ON(!mpic); 227 BUG_ON(!mpic);
228 228
229 mpic_assign_isu(mpic, 0, openpic_addr + 0x10000); 229 mpic_assign_isu(mpic, 0, openpic_addr + 0x10000);
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile
index 99a77d743d48..85cf8c60f0be 100644
--- a/arch/powerpc/sysdev/Makefile
+++ b/arch/powerpc/sysdev/Makefile
@@ -2,7 +2,7 @@ ifeq ($(CONFIG_PPC64),y)
2EXTRA_CFLAGS += -mno-minimal-toc 2EXTRA_CFLAGS += -mno-minimal-toc
3endif 3endif
4 4
5mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o 5mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o mpic_pasemi_msi.o
6obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y) 6obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y)
7 7
8obj-$(CONFIG_PPC_MPC106) += grackle.o 8obj-$(CONFIG_PPC_MPC106) += grackle.o
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c
index 116173ab58ba..f74fe26b787e 100644
--- a/arch/powerpc/sysdev/mpic.c
+++ b/arch/powerpc/sysdev/mpic.c
@@ -841,6 +841,24 @@ int mpic_set_irq_type(unsigned int virq, unsigned int flow_type)
841 return 0; 841 return 0;
842} 842}
843 843
844void mpic_set_vector(unsigned int virq, unsigned int vector)
845{
846 struct mpic *mpic = mpic_from_irq(virq);
847 unsigned int src = mpic_irq_to_hw(virq);
848 unsigned int vecpri;
849
850 DBG("mpic: set_vector(mpic:@%p,virq:%d,src:%d,vector:0x%x)\n",
851 mpic, virq, src, vector);
852
853 if (src >= mpic->irq_count)
854 return;
855
856 vecpri = mpic_irq_read(src, MPIC_INFO(IRQ_VECTOR_PRI));
857 vecpri = vecpri & ~MPIC_INFO(VECPRI_VECTOR_MASK);
858 vecpri |= vector;
859 mpic_irq_write(src, MPIC_INFO(IRQ_VECTOR_PRI), vecpri);
860}
861
844static struct irq_chip mpic_irq_chip = { 862static struct irq_chip mpic_irq_chip = {
845 .mask = mpic_mask_irq, 863 .mask = mpic_mask_irq,
846 .unmask = mpic_unmask_irq, 864 .unmask = mpic_unmask_irq,
@@ -1229,6 +1247,8 @@ void __init mpic_init(struct mpic *mpic)
1229 mpic_u3msi_init(mpic); 1247 mpic_u3msi_init(mpic);
1230 } 1248 }
1231 1249
1250 mpic_pasemi_msi_init(mpic);
1251
1232 for (i = 0; i < mpic->num_sources; i++) { 1252 for (i = 0; i < mpic->num_sources; i++) {
1233 /* start with vector = source number, and masked */ 1253 /* start with vector = source number, and masked */
1234 u32 vecpri = MPIC_VECPRI_MASK | i | 1254 u32 vecpri = MPIC_VECPRI_MASK | i |
diff --git a/arch/powerpc/sysdev/mpic.h b/arch/powerpc/sysdev/mpic.h
index 1cb6bd841027..4783c6e9f30d 100644
--- a/arch/powerpc/sysdev/mpic.h
+++ b/arch/powerpc/sysdev/mpic.h
@@ -17,6 +17,7 @@ extern int mpic_msi_init_allocator(struct mpic *mpic);
17extern irq_hw_number_t mpic_msi_alloc_hwirqs(struct mpic *mpic, int num); 17extern irq_hw_number_t mpic_msi_alloc_hwirqs(struct mpic *mpic, int num);
18extern void mpic_msi_free_hwirqs(struct mpic *mpic, int offset, int num); 18extern void mpic_msi_free_hwirqs(struct mpic *mpic, int offset, int num);
19extern int mpic_u3msi_init(struct mpic *mpic); 19extern int mpic_u3msi_init(struct mpic *mpic);
20extern int mpic_pasemi_msi_init(struct mpic *mpic);
20#else 21#else
21static inline void mpic_msi_reserve_hwirq(struct mpic *mpic, 22static inline void mpic_msi_reserve_hwirq(struct mpic *mpic,
22 irq_hw_number_t hwirq) 23 irq_hw_number_t hwirq)
@@ -28,9 +29,15 @@ static inline int mpic_u3msi_init(struct mpic *mpic)
28{ 29{
29 return -1; 30 return -1;
30} 31}
32
33static inline int mpic_pasemi_msi_init(struct mpic *mpic)
34{
35 return -1;
36}
31#endif 37#endif
32 38
33extern int mpic_set_irq_type(unsigned int virq, unsigned int flow_type); 39extern int mpic_set_irq_type(unsigned int virq, unsigned int flow_type);
40extern void mpic_set_vector(unsigned int virq, unsigned int vector);
34extern void mpic_end_irq(unsigned int irq); 41extern void mpic_end_irq(unsigned int irq);
35extern void mpic_mask_irq(unsigned int irq); 42extern void mpic_mask_irq(unsigned int irq);
36extern void mpic_unmask_irq(unsigned int irq); 43extern void mpic_unmask_irq(unsigned int irq);
diff --git a/arch/powerpc/sysdev/mpic_pasemi_msi.c b/arch/powerpc/sysdev/mpic_pasemi_msi.c
new file mode 100644
index 000000000000..d6bfda30ac87
--- /dev/null
+++ b/arch/powerpc/sysdev/mpic_pasemi_msi.c
@@ -0,0 +1,172 @@
1/*
2 * Copyright 2007, Olof Johansson, PA Semi
3 *
4 * Based on arch/powerpc/sysdev/mpic_u3msi.c:
5 *
6 * Copyright 2006, Segher Boessenkool, IBM Corporation.
7 * Copyright 2006-2007, Michael Ellerman, IBM Corporation.
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; version 2 of the
12 * License.
13 *
14 */
15
16#undef DEBUG
17
18#include <linux/irq.h>
19#include <linux/bootmem.h>
20#include <linux/msi.h>
21#include <asm/mpic.h>
22#include <asm/prom.h>
23#include <asm/hw_irq.h>
24#include <asm/ppc-pci.h>
25
26#include "mpic.h"
27
28/* Allocate 16 interrupts per device, to give an alignment of 16,
29 * since that's the size of the grouping w.r.t. affinity. If someone
30 * needs more than 32 MSI's down the road we'll have to rethink this,
31 * but it should be OK for now.
32 */
33#define ALLOC_CHUNK 16
34
35#define PASEMI_MSI_ADDR 0xfc080000
36
37/* A bit ugly, can we get this from the pci_dev somehow? */
38static struct mpic *msi_mpic;
39
40
41static void mpic_pasemi_msi_mask_irq(unsigned int irq)
42{
43 pr_debug("mpic_pasemi_msi_mask_irq %d\n", irq);
44 mask_msi_irq(irq);
45 mpic_mask_irq(irq);
46}
47
48static void mpic_pasemi_msi_unmask_irq(unsigned int irq)
49{
50 pr_debug("mpic_pasemi_msi_unmask_irq %d\n", irq);
51 mpic_unmask_irq(irq);
52 unmask_msi_irq(irq);
53}
54
55static struct irq_chip mpic_pasemi_msi_chip = {
56 .shutdown = mpic_pasemi_msi_mask_irq,
57 .mask = mpic_pasemi_msi_mask_irq,
58 .unmask = mpic_pasemi_msi_unmask_irq,
59 .eoi = mpic_end_irq,
60 .set_type = mpic_set_irq_type,
61 .set_affinity = mpic_set_affinity,
62 .typename = "PASEMI-MSI ",
63};
64
65static int pasemi_msi_check_device(struct pci_dev *pdev, int nvec, int type)
66{
67 if (type == PCI_CAP_ID_MSIX)
68 pr_debug("pasemi_msi: MSI-X untested, trying anyway\n");
69
70 return 0;
71}
72
73static void pasemi_msi_teardown_msi_irqs(struct pci_dev *pdev)
74{
75 struct msi_desc *entry;
76
77 pr_debug("pasemi_msi_teardown_msi_irqs, pdev %p\n", pdev);
78
79 list_for_each_entry(entry, &pdev->msi_list, list) {
80 if (entry->irq == NO_IRQ)
81 continue;
82
83 set_irq_msi(entry->irq, NULL);
84 mpic_msi_free_hwirqs(msi_mpic, virq_to_hw(entry->irq),
85 ALLOC_CHUNK);
86 irq_dispose_mapping(entry->irq);
87 }
88
89 return;
90}
91
92static int pasemi_msi_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type)
93{
94 irq_hw_number_t hwirq;
95 unsigned int virq;
96 struct msi_desc *entry;
97 struct msi_msg msg;
98 u64 addr;
99
100 pr_debug("pasemi_msi_setup_msi_irqs, pdev %p nvec %d type %d\n",
101 pdev, nvec, type);
102
103 msg.address_hi = 0;
104 msg.address_lo = PASEMI_MSI_ADDR;
105
106 list_for_each_entry(entry, &pdev->msi_list, list) {
107 /* Allocate 16 interrupts for now, since that's the grouping for
108 * affinity. This can be changed later if it turns out 32 is too
109 * few MSIs for someone, but restrictions will apply to how the
110 * sources can be changed independently.
111 */
112 hwirq = mpic_msi_alloc_hwirqs(msi_mpic, ALLOC_CHUNK);
113 if (hwirq < 0) {
114 pr_debug("pasemi_msi: failed allocating hwirq\n");
115 return hwirq;
116 }
117
118 virq = irq_create_mapping(msi_mpic->irqhost, hwirq);
119 if (virq == NO_IRQ) {
120 pr_debug("pasemi_msi: failed mapping hwirq 0x%lx\n", hwirq);
121 mpic_msi_free_hwirqs(msi_mpic, hwirq, ALLOC_CHUNK);
122 return -ENOSPC;
123 }
124
125 /* Vector on MSI is really an offset, the hardware adds
126 * it to the value written at the magic address. So set
127 * it to 0 to remain sane.
128 */
129 mpic_set_vector(virq, 0);
130
131 set_irq_msi(virq, entry);
132 set_irq_chip(virq, &mpic_pasemi_msi_chip);
133 set_irq_type(virq, IRQ_TYPE_EDGE_RISING);
134
135 pr_debug("pasemi_msi: allocated virq 0x%x (hw 0x%lx) addr 0x%lx\n",
136 virq, hwirq, addr);
137
138 /* Likewise, the device writes [0...511] into the target
139 * register to generate MSI [512...1023]
140 */
141 msg.data = hwirq-0x200;
142 write_msi_msg(virq, &msg);
143 }
144
145 return 0;
146}
147
148int mpic_pasemi_msi_init(struct mpic *mpic)
149{
150 int rc;
151
152 if (!mpic->irqhost->of_node ||
153 !of_device_is_compatible(mpic->irqhost->of_node,
154 "pasemi,pwrficient-openpic"))
155 return -ENODEV;
156
157 rc = mpic_msi_init_allocator(mpic);
158 if (rc) {
159 pr_debug("pasemi_msi: Error allocating bitmap!\n");
160 return rc;
161 }
162
163 pr_debug("pasemi_msi: Registering PA Semi MPIC MSI callbacks\n");
164
165 msi_mpic = mpic;
166 WARN_ON(ppc_md.setup_msi_irqs);
167 ppc_md.setup_msi_irqs = pasemi_msi_setup_msi_irqs;
168 ppc_md.teardown_msi_irqs = pasemi_msi_teardown_msi_irqs;
169 ppc_md.msi_check_device = pasemi_msi_check_device;
170
171 return 0;
172}