diff options
-rw-r--r-- | arch/powerpc/platforms/pasemi/setup.c | 2 | ||||
-rw-r--r-- | arch/powerpc/sysdev/Makefile | 2 | ||||
-rw-r--r-- | arch/powerpc/sysdev/mpic.c | 20 | ||||
-rw-r--r-- | arch/powerpc/sysdev/mpic.h | 7 | ||||
-rw-r--r-- | arch/powerpc/sysdev/mpic_pasemi_msi.c | 172 |
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) | |||
2 | EXTRA_CFLAGS += -mno-minimal-toc | 2 | EXTRA_CFLAGS += -mno-minimal-toc |
3 | endif | 3 | endif |
4 | 4 | ||
5 | mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o | 5 | mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o mpic_pasemi_msi.o |
6 | obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y) | 6 | obj-$(CONFIG_MPIC) += mpic.o $(mpic-msi-obj-y) |
7 | 7 | ||
8 | obj-$(CONFIG_PPC_MPC106) += grackle.o | 8 | obj-$(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 | ||
844 | void 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 | |||
844 | static struct irq_chip mpic_irq_chip = { | 862 | static 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); | |||
17 | extern irq_hw_number_t mpic_msi_alloc_hwirqs(struct mpic *mpic, int num); | 17 | extern irq_hw_number_t mpic_msi_alloc_hwirqs(struct mpic *mpic, int num); |
18 | extern void mpic_msi_free_hwirqs(struct mpic *mpic, int offset, int num); | 18 | extern void mpic_msi_free_hwirqs(struct mpic *mpic, int offset, int num); |
19 | extern int mpic_u3msi_init(struct mpic *mpic); | 19 | extern int mpic_u3msi_init(struct mpic *mpic); |
20 | extern int mpic_pasemi_msi_init(struct mpic *mpic); | ||
20 | #else | 21 | #else |
21 | static inline void mpic_msi_reserve_hwirq(struct mpic *mpic, | 22 | static 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 | |||
33 | static inline int mpic_pasemi_msi_init(struct mpic *mpic) | ||
34 | { | ||
35 | return -1; | ||
36 | } | ||
31 | #endif | 37 | #endif |
32 | 38 | ||
33 | extern int mpic_set_irq_type(unsigned int virq, unsigned int flow_type); | 39 | extern int mpic_set_irq_type(unsigned int virq, unsigned int flow_type); |
40 | extern void mpic_set_vector(unsigned int virq, unsigned int vector); | ||
34 | extern void mpic_end_irq(unsigned int irq); | 41 | extern void mpic_end_irq(unsigned int irq); |
35 | extern void mpic_mask_irq(unsigned int irq); | 42 | extern void mpic_mask_irq(unsigned int irq); |
36 | extern void mpic_unmask_irq(unsigned int irq); | 43 | extern 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? */ | ||
38 | static struct mpic *msi_mpic; | ||
39 | |||
40 | |||
41 | static 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 | |||
48 | static 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 | |||
55 | static 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 | |||
65 | static 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 | |||
73 | static 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 | |||
92 | static 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 | |||
148 | int 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 | } | ||