aboutsummaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorOlof Johansson <olof@lixom.net>2007-12-12 01:44:46 -0500
committerPaul Mackerras <paulus@samba.org>2007-12-20 00:15:23 -0500
commit38958dd9113c19cd7a927009ae585bd5aba3295e (patch)
treee4762aae2cfda535679bf3145f7553fddbdf6f6e /arch
parent731e74c43d4e47daf327748128f1a3648e5d39a5 (diff)
[POWERPC] pasemi: Implement MSI support
Implement MSI support for PA Semi PWRficient platforms. MSI is done through a special range of sources on the openpic controller, and they're unfortunately breaking the usual concepts of how sources are programmed: * The source is calculated as 512 + the value written into the MSI register * The vector for this source is added to the source and reported through IACK This means that for simplicity, it makes much more sense to just set the vector to 0 for the source, since that's really the vector we expect to see from IACK. Also, the affinity/priority registers will affect 16 sources at a time. To avoid most (simple) users from being limited by this, allocate 16 sources per device but use only one. This means that there's a total of 32 sources. If we get usage scenarions that need more sources, the allocator should probably be revised to take an alignment argument and size, not just do natural alignment. Finally, since I'm already touching the MPIC names on pasemi, rename the base one from the somewhat odd " PAS-OPIC " to "PASEMI-OPIC". Signed-off-by: Olof Johansson <olof@lixom.net> Acked-by: Michael Ellerman <michael@ellerman.id.au> Signed-off-by: Paul Mackerras <paulus@samba.org>
Diffstat (limited to 'arch')
-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}