diff options
-rw-r--r-- | arch/powerpc/Kconfig.debug | 5 | ||||
-rw-r--r-- | arch/powerpc/include/asm/msi_bitmap.h | 35 | ||||
-rw-r--r-- | arch/powerpc/sysdev/Kconfig | 6 | ||||
-rw-r--r-- | arch/powerpc/sysdev/Makefile | 1 | ||||
-rw-r--r-- | arch/powerpc/sysdev/msi_bitmap.c | 247 |
5 files changed, 294 insertions, 0 deletions
diff --git a/arch/powerpc/Kconfig.debug b/arch/powerpc/Kconfig.debug index 4ebc52a19f0a..15eb27861fc7 100644 --- a/arch/powerpc/Kconfig.debug +++ b/arch/powerpc/Kconfig.debug | |||
@@ -51,6 +51,11 @@ config FTR_FIXUP_SELFTEST | |||
51 | depends on DEBUG_KERNEL | 51 | depends on DEBUG_KERNEL |
52 | default n | 52 | default n |
53 | 53 | ||
54 | config MSI_BITMAP_SELFTEST | ||
55 | bool "Run self-tests of the MSI bitmap code." | ||
56 | depends on DEBUG_KERNEL | ||
57 | default n | ||
58 | |||
54 | config XMON | 59 | config XMON |
55 | bool "Include xmon kernel debugger" | 60 | bool "Include xmon kernel debugger" |
56 | depends on DEBUG_KERNEL | 61 | depends on DEBUG_KERNEL |
diff --git a/arch/powerpc/include/asm/msi_bitmap.h b/arch/powerpc/include/asm/msi_bitmap.h new file mode 100644 index 000000000000..97ac3f46ae0d --- /dev/null +++ b/arch/powerpc/include/asm/msi_bitmap.h | |||
@@ -0,0 +1,35 @@ | |||
1 | #ifndef _POWERPC_SYSDEV_MSI_BITMAP_H | ||
2 | #define _POWERPC_SYSDEV_MSI_BITMAP_H | ||
3 | |||
4 | /* | ||
5 | * Copyright 2008, Michael Ellerman, IBM Corporation. | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or | ||
8 | * modify it under the terms of the GNU General Public License | ||
9 | * as published by the Free Software Foundation; version 2 of the | ||
10 | * License. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/of.h> | ||
15 | #include <asm/irq.h> | ||
16 | |||
17 | struct msi_bitmap { | ||
18 | struct device_node *of_node; | ||
19 | unsigned long *bitmap; | ||
20 | spinlock_t lock; | ||
21 | unsigned int irq_count; | ||
22 | }; | ||
23 | |||
24 | int msi_bitmap_alloc_hwirqs(struct msi_bitmap *bmp, int num); | ||
25 | void msi_bitmap_free_hwirqs(struct msi_bitmap *bmp, unsigned int offset, | ||
26 | unsigned int num); | ||
27 | void msi_bitmap_reserve_hwirq(struct msi_bitmap *bmp, unsigned int hwirq); | ||
28 | |||
29 | int msi_bitmap_reserve_dt_hwirqs(struct msi_bitmap *bmp); | ||
30 | |||
31 | int msi_bitmap_alloc(struct msi_bitmap *bmp, unsigned int irq_count, | ||
32 | struct device_node *of_node); | ||
33 | void msi_bitmap_free(struct msi_bitmap *bmp); | ||
34 | |||
35 | #endif /* _POWERPC_SYSDEV_MSI_BITMAP_H */ | ||
diff --git a/arch/powerpc/sysdev/Kconfig b/arch/powerpc/sysdev/Kconfig index 72fb35b9ebca..396582835cb5 100644 --- a/arch/powerpc/sysdev/Kconfig +++ b/arch/powerpc/sysdev/Kconfig | |||
@@ -6,3 +6,9 @@ config PPC4xx_PCI_EXPRESS | |||
6 | bool | 6 | bool |
7 | depends on PCI && 4xx | 7 | depends on PCI && 4xx |
8 | default n | 8 | default n |
9 | |||
10 | config PPC_MSI_BITMAP | ||
11 | bool | ||
12 | depends on PCI_MSI | ||
13 | default y if MPIC | ||
14 | default y if FSL_PCI | ||
diff --git a/arch/powerpc/sysdev/Makefile b/arch/powerpc/sysdev/Makefile index a90054b56d5c..b6c269eb2650 100644 --- a/arch/powerpc/sysdev/Makefile +++ b/arch/powerpc/sysdev/Makefile | |||
@@ -5,6 +5,7 @@ endif | |||
5 | mpic-msi-obj-$(CONFIG_PCI_MSI) += mpic_msi.o mpic_u3msi.o mpic_pasemi_msi.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 | fsl-msi-obj-$(CONFIG_PCI_MSI) += fsl_msi.o | 7 | fsl-msi-obj-$(CONFIG_PCI_MSI) += fsl_msi.o |
8 | obj-$(CONFIG_PPC_MSI_BITMAP) += msi_bitmap.o | ||
8 | 9 | ||
9 | obj-$(CONFIG_PPC_MPC106) += grackle.o | 10 | obj-$(CONFIG_PPC_MPC106) += grackle.o |
10 | obj-$(CONFIG_PPC_DCR_NATIVE) += dcr-low.o | 11 | obj-$(CONFIG_PPC_DCR_NATIVE) += dcr-low.o |
diff --git a/arch/powerpc/sysdev/msi_bitmap.c b/arch/powerpc/sysdev/msi_bitmap.c new file mode 100644 index 000000000000..f84217b8863a --- /dev/null +++ b/arch/powerpc/sysdev/msi_bitmap.c | |||
@@ -0,0 +1,247 @@ | |||
1 | /* | ||
2 | * Copyright 2006-2008, Michael Ellerman, IBM Corporation. | ||
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; version 2 of the | ||
7 | * License. | ||
8 | * | ||
9 | */ | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/bitmap.h> | ||
13 | #include <asm/msi_bitmap.h> | ||
14 | |||
15 | int msi_bitmap_alloc_hwirqs(struct msi_bitmap *bmp, int num) | ||
16 | { | ||
17 | unsigned long flags; | ||
18 | int offset, order = get_count_order(num); | ||
19 | |||
20 | spin_lock_irqsave(&bmp->lock, flags); | ||
21 | /* | ||
22 | * This is fast, but stricter than we need. We might want to add | ||
23 | * a fallback routine which does a linear search with no alignment. | ||
24 | */ | ||
25 | offset = bitmap_find_free_region(bmp->bitmap, bmp->irq_count, order); | ||
26 | spin_unlock_irqrestore(&bmp->lock, flags); | ||
27 | |||
28 | pr_debug("msi_bitmap: allocated 0x%x (2^%d) at offset 0x%x\n", | ||
29 | num, order, offset); | ||
30 | |||
31 | return offset; | ||
32 | } | ||
33 | |||
34 | void msi_bitmap_free_hwirqs(struct msi_bitmap *bmp, unsigned int offset, | ||
35 | unsigned int num) | ||
36 | { | ||
37 | unsigned long flags; | ||
38 | int order = get_count_order(num); | ||
39 | |||
40 | pr_debug("msi_bitmap: freeing 0x%x (2^%d) at offset 0x%x\n", | ||
41 | num, order, offset); | ||
42 | |||
43 | spin_lock_irqsave(&bmp->lock, flags); | ||
44 | bitmap_release_region(bmp->bitmap, offset, order); | ||
45 | spin_unlock_irqrestore(&bmp->lock, flags); | ||
46 | } | ||
47 | |||
48 | void msi_bitmap_reserve_hwirq(struct msi_bitmap *bmp, unsigned int hwirq) | ||
49 | { | ||
50 | unsigned long flags; | ||
51 | |||
52 | pr_debug("msi_bitmap: reserving hwirq 0x%x\n", hwirq); | ||
53 | |||
54 | spin_lock_irqsave(&bmp->lock, flags); | ||
55 | bitmap_allocate_region(bmp->bitmap, hwirq, 0); | ||
56 | spin_unlock_irqrestore(&bmp->lock, flags); | ||
57 | } | ||
58 | |||
59 | /** | ||
60 | * msi_bitmap_reserve_dt_hwirqs - Reserve irqs specified in the device tree. | ||
61 | * @bmp: pointer to the MSI bitmap. | ||
62 | * | ||
63 | * Looks in the device tree to see if there is a property specifying which | ||
64 | * irqs can be used for MSI. If found those irqs reserved in the device tree | ||
65 | * are reserved in the bitmap. | ||
66 | * | ||
67 | * Returns 0 for success, < 0 if there was an error, and > 0 if no property | ||
68 | * was found in the device tree. | ||
69 | **/ | ||
70 | int msi_bitmap_reserve_dt_hwirqs(struct msi_bitmap *bmp) | ||
71 | { | ||
72 | int i, j, len; | ||
73 | const u32 *p; | ||
74 | |||
75 | if (!bmp->of_node) | ||
76 | return 1; | ||
77 | |||
78 | p = of_get_property(bmp->of_node, "msi-available-ranges", &len); | ||
79 | if (!p) { | ||
80 | pr_debug("msi_bitmap: no msi-available-ranges property " \ | ||
81 | "found on %s\n", bmp->of_node->full_name); | ||
82 | return 1; | ||
83 | } | ||
84 | |||
85 | if (len % (2 * sizeof(u32)) != 0) { | ||
86 | printk(KERN_WARNING "msi_bitmap: Malformed msi-available-ranges" | ||
87 | " property on %s\n", bmp->of_node->full_name); | ||
88 | return -EINVAL; | ||
89 | } | ||
90 | |||
91 | bitmap_allocate_region(bmp->bitmap, 0, get_count_order(bmp->irq_count)); | ||
92 | |||
93 | spin_lock(&bmp->lock); | ||
94 | |||
95 | /* Format is: (<u32 start> <u32 count>)+ */ | ||
96 | len /= 2 * sizeof(u32); | ||
97 | for (i = 0; i < len; i++, p += 2) { | ||
98 | for (j = 0; j < *(p + 1); j++) | ||
99 | bitmap_release_region(bmp->bitmap, *p + j, 0); | ||
100 | } | ||
101 | |||
102 | spin_unlock(&bmp->lock); | ||
103 | |||
104 | return 0; | ||
105 | } | ||
106 | |||
107 | int msi_bitmap_alloc(struct msi_bitmap *bmp, unsigned int irq_count, | ||
108 | struct device_node *of_node) | ||
109 | { | ||
110 | int size; | ||
111 | |||
112 | if (!irq_count) | ||
113 | return -EINVAL; | ||
114 | |||
115 | size = BITS_TO_LONGS(irq_count) * sizeof(long); | ||
116 | pr_debug("msi_bitmap: allocator bitmap size is 0x%x bytes\n", size); | ||
117 | |||
118 | bmp->bitmap = zalloc_maybe_bootmem(size, GFP_KERNEL); | ||
119 | if (!bmp->bitmap) { | ||
120 | pr_debug("msi_bitmap: ENOMEM allocating allocator bitmap!\n"); | ||
121 | return -ENOMEM; | ||
122 | } | ||
123 | |||
124 | /* We zalloc'ed the bitmap, so all irqs are free by default */ | ||
125 | spin_lock_init(&bmp->lock); | ||
126 | bmp->of_node = of_node_get(of_node); | ||
127 | bmp->irq_count = irq_count; | ||
128 | |||
129 | return 0; | ||
130 | } | ||
131 | |||
132 | void msi_bitmap_free(struct msi_bitmap *bmp) | ||
133 | { | ||
134 | /* we can't free the bitmap we don't know if it's bootmem etc. */ | ||
135 | of_node_put(bmp->of_node); | ||
136 | bmp->bitmap = NULL; | ||
137 | } | ||
138 | |||
139 | #ifdef CONFIG_MSI_BITMAP_SELFTEST | ||
140 | |||
141 | #define check(x) \ | ||
142 | if (!(x)) printk("msi_bitmap: test failed at line %d\n", __LINE__); | ||
143 | |||
144 | void test_basics(void) | ||
145 | { | ||
146 | struct msi_bitmap bmp; | ||
147 | int i, size = 512; | ||
148 | |||
149 | /* Can't allocate a bitmap of 0 irqs */ | ||
150 | check(msi_bitmap_alloc(&bmp, 0, NULL) != 0); | ||
151 | |||
152 | /* of_node may be NULL */ | ||
153 | check(0 == msi_bitmap_alloc(&bmp, size, NULL)); | ||
154 | |||
155 | /* Should all be free by default */ | ||
156 | check(0 == bitmap_find_free_region(bmp.bitmap, size, | ||
157 | get_count_order(size))); | ||
158 | bitmap_release_region(bmp.bitmap, 0, get_count_order(size)); | ||
159 | |||
160 | /* With no node, there's no msi-available-ranges, so expect > 0 */ | ||
161 | check(msi_bitmap_reserve_dt_hwirqs(&bmp) > 0); | ||
162 | |||
163 | /* Should all still be free */ | ||
164 | check(0 == bitmap_find_free_region(bmp.bitmap, size, | ||
165 | get_count_order(size))); | ||
166 | bitmap_release_region(bmp.bitmap, 0, get_count_order(size)); | ||
167 | |||
168 | /* Check we can fill it up and then no more */ | ||
169 | for (i = 0; i < size; i++) | ||
170 | check(msi_bitmap_alloc_hwirqs(&bmp, 1) >= 0); | ||
171 | |||
172 | check(msi_bitmap_alloc_hwirqs(&bmp, 1) < 0); | ||
173 | |||
174 | /* Should all be allocated */ | ||
175 | check(bitmap_find_free_region(bmp.bitmap, size, 0) < 0); | ||
176 | |||
177 | /* And if we free one we can then allocate another */ | ||
178 | msi_bitmap_free_hwirqs(&bmp, size / 2, 1); | ||
179 | check(msi_bitmap_alloc_hwirqs(&bmp, 1) == size / 2); | ||
180 | |||
181 | msi_bitmap_free(&bmp); | ||
182 | |||
183 | /* Clients may check bitmap == NULL for "not-allocated" */ | ||
184 | check(bmp.bitmap == NULL); | ||
185 | |||
186 | kfree(bmp.bitmap); | ||
187 | } | ||
188 | |||
189 | void test_of_node(void) | ||
190 | { | ||
191 | u32 prop_data[] = { 10, 10, 25, 3, 40, 1, 100, 100, 200, 20 }; | ||
192 | const char *expected_str = "0-9,20-24,28-39,41-99,220-255"; | ||
193 | char *prop_name = "msi-available-ranges"; | ||
194 | char *node_name = "/fakenode"; | ||
195 | struct device_node of_node; | ||
196 | struct property prop; | ||
197 | struct msi_bitmap bmp; | ||
198 | int size = 256; | ||
199 | DECLARE_BITMAP(expected, size); | ||
200 | |||
201 | /* There should really be a struct device_node allocator */ | ||
202 | memset(&of_node, 0, sizeof(of_node)); | ||
203 | kref_init(&of_node.kref); | ||
204 | of_node.full_name = node_name; | ||
205 | |||
206 | check(0 == msi_bitmap_alloc(&bmp, size, &of_node)); | ||
207 | |||
208 | /* No msi-available-ranges, so expect > 0 */ | ||
209 | check(msi_bitmap_reserve_dt_hwirqs(&bmp) > 0); | ||
210 | |||
211 | /* Should all still be free */ | ||
212 | check(0 == bitmap_find_free_region(bmp.bitmap, size, | ||
213 | get_count_order(size))); | ||
214 | bitmap_release_region(bmp.bitmap, 0, get_count_order(size)); | ||
215 | |||
216 | /* Now create a fake msi-available-ranges property */ | ||
217 | |||
218 | /* There should really .. oh whatever */ | ||
219 | memset(&prop, 0, sizeof(prop)); | ||
220 | prop.name = prop_name; | ||
221 | prop.value = &prop_data; | ||
222 | prop.length = sizeof(prop_data); | ||
223 | |||
224 | of_node.properties = ∝ | ||
225 | |||
226 | /* msi-available-ranges, so expect == 0 */ | ||
227 | check(msi_bitmap_reserve_dt_hwirqs(&bmp) == 0); | ||
228 | |||
229 | /* Check we got the expected result */ | ||
230 | check(0 == bitmap_parselist(expected_str, expected, size)); | ||
231 | check(bitmap_equal(expected, bmp.bitmap, size)); | ||
232 | |||
233 | msi_bitmap_free(&bmp); | ||
234 | kfree(bmp.bitmap); | ||
235 | } | ||
236 | |||
237 | int msi_bitmap_selftest(void) | ||
238 | { | ||
239 | printk(KERN_DEBUG "Running MSI bitmap self-tests ...\n"); | ||
240 | |||
241 | test_basics(); | ||
242 | test_of_node(); | ||
243 | |||
244 | return 0; | ||
245 | } | ||
246 | late_initcall(msi_bitmap_selftest); | ||
247 | #endif /* CONFIG_MSI_BITMAP_SELFTEST */ | ||