diff options
Diffstat (limited to 'arch/powerpc/sysdev/fsl_msi.c')
-rw-r--r-- | arch/powerpc/sysdev/fsl_msi.c | 117 |
1 files changed, 93 insertions, 24 deletions
diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c index a7be144f5874..0f5bee90ee4e 100644 --- a/arch/powerpc/sysdev/fsl_msi.c +++ b/arch/powerpc/sysdev/fsl_msi.c | |||
@@ -1,5 +1,5 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2007-2008 Freescale Semiconductor, Inc. All rights reserved. | 2 | * Copyright (C) 2007-2010 Freescale Semiconductor, Inc. |
3 | * | 3 | * |
4 | * Author: Tony Li <tony.li@freescale.com> | 4 | * Author: Tony Li <tony.li@freescale.com> |
5 | * Jason Jin <Jason.jin@freescale.com> | 5 | * Jason Jin <Jason.jin@freescale.com> |
@@ -22,14 +22,20 @@ | |||
22 | #include <asm/prom.h> | 22 | #include <asm/prom.h> |
23 | #include <asm/hw_irq.h> | 23 | #include <asm/hw_irq.h> |
24 | #include <asm/ppc-pci.h> | 24 | #include <asm/ppc-pci.h> |
25 | #include <asm/mpic.h> | ||
25 | #include "fsl_msi.h" | 26 | #include "fsl_msi.h" |
26 | 27 | ||
28 | LIST_HEAD(msi_head); | ||
29 | |||
27 | struct fsl_msi_feature { | 30 | struct fsl_msi_feature { |
28 | u32 fsl_pic_ip; | 31 | u32 fsl_pic_ip; |
29 | u32 msiir_offset; | 32 | u32 msiir_offset; |
30 | }; | 33 | }; |
31 | 34 | ||
32 | static struct fsl_msi *fsl_msi; | 35 | struct fsl_msi_cascade_data { |
36 | struct fsl_msi *msi_data; | ||
37 | int index; | ||
38 | }; | ||
33 | 39 | ||
34 | static inline u32 fsl_msi_read(u32 __iomem *base, unsigned int reg) | 40 | static inline u32 fsl_msi_read(u32 __iomem *base, unsigned int reg) |
35 | { | 41 | { |
@@ -54,10 +60,12 @@ static struct irq_chip fsl_msi_chip = { | |||
54 | static int fsl_msi_host_map(struct irq_host *h, unsigned int virq, | 60 | static int fsl_msi_host_map(struct irq_host *h, unsigned int virq, |
55 | irq_hw_number_t hw) | 61 | irq_hw_number_t hw) |
56 | { | 62 | { |
63 | struct fsl_msi *msi_data = h->host_data; | ||
57 | struct irq_chip *chip = &fsl_msi_chip; | 64 | struct irq_chip *chip = &fsl_msi_chip; |
58 | 65 | ||
59 | irq_to_desc(virq)->status |= IRQ_TYPE_EDGE_FALLING; | 66 | irq_to_desc(virq)->status |= IRQ_TYPE_EDGE_FALLING; |
60 | 67 | ||
68 | set_irq_chip_data(virq, msi_data); | ||
61 | set_irq_chip_and_handler(virq, chip, handle_edge_irq); | 69 | set_irq_chip_and_handler(virq, chip, handle_edge_irq); |
62 | 70 | ||
63 | return 0; | 71 | return 0; |
@@ -96,11 +104,12 @@ static int fsl_msi_check_device(struct pci_dev *pdev, int nvec, int type) | |||
96 | static void fsl_teardown_msi_irqs(struct pci_dev *pdev) | 104 | static void fsl_teardown_msi_irqs(struct pci_dev *pdev) |
97 | { | 105 | { |
98 | struct msi_desc *entry; | 106 | struct msi_desc *entry; |
99 | struct fsl_msi *msi_data = fsl_msi; | 107 | struct fsl_msi *msi_data; |
100 | 108 | ||
101 | list_for_each_entry(entry, &pdev->msi_list, list) { | 109 | list_for_each_entry(entry, &pdev->msi_list, list) { |
102 | if (entry->irq == NO_IRQ) | 110 | if (entry->irq == NO_IRQ) |
103 | continue; | 111 | continue; |
112 | msi_data = get_irq_data(entry->irq); | ||
104 | set_irq_msi(entry->irq, NULL); | 113 | set_irq_msi(entry->irq, NULL); |
105 | msi_bitmap_free_hwirqs(&msi_data->bitmap, | 114 | msi_bitmap_free_hwirqs(&msi_data->bitmap, |
106 | virq_to_hw(entry->irq), 1); | 115 | virq_to_hw(entry->irq), 1); |
@@ -111,9 +120,10 @@ static void fsl_teardown_msi_irqs(struct pci_dev *pdev) | |||
111 | } | 120 | } |
112 | 121 | ||
113 | static void fsl_compose_msi_msg(struct pci_dev *pdev, int hwirq, | 122 | static void fsl_compose_msi_msg(struct pci_dev *pdev, int hwirq, |
114 | struct msi_msg *msg) | 123 | struct msi_msg *msg, |
124 | struct fsl_msi *fsl_msi_data) | ||
115 | { | 125 | { |
116 | struct fsl_msi *msi_data = fsl_msi; | 126 | struct fsl_msi *msi_data = fsl_msi_data; |
117 | struct pci_controller *hose = pci_bus_to_host(pdev->bus); | 127 | struct pci_controller *hose = pci_bus_to_host(pdev->bus); |
118 | u32 base = 0; | 128 | u32 base = 0; |
119 | 129 | ||
@@ -130,14 +140,19 @@ static void fsl_compose_msi_msg(struct pci_dev *pdev, int hwirq, | |||
130 | 140 | ||
131 | static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) | 141 | static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) |
132 | { | 142 | { |
133 | int rc, hwirq; | 143 | int rc, hwirq = -ENOMEM; |
134 | unsigned int virq; | 144 | unsigned int virq; |
135 | struct msi_desc *entry; | 145 | struct msi_desc *entry; |
136 | struct msi_msg msg; | 146 | struct msi_msg msg; |
137 | struct fsl_msi *msi_data = fsl_msi; | 147 | struct fsl_msi *msi_data; |
138 | 148 | ||
139 | list_for_each_entry(entry, &pdev->msi_list, list) { | 149 | list_for_each_entry(entry, &pdev->msi_list, list) { |
140 | hwirq = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1); | 150 | list_for_each_entry(msi_data, &msi_head, list) { |
151 | hwirq = msi_bitmap_alloc_hwirqs(&msi_data->bitmap, 1); | ||
152 | if (hwirq >= 0) | ||
153 | break; | ||
154 | } | ||
155 | |||
141 | if (hwirq < 0) { | 156 | if (hwirq < 0) { |
142 | rc = hwirq; | 157 | rc = hwirq; |
143 | pr_debug("%s: fail allocating msi interrupt\n", | 158 | pr_debug("%s: fail allocating msi interrupt\n", |
@@ -154,25 +169,31 @@ static int fsl_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) | |||
154 | rc = -ENOSPC; | 169 | rc = -ENOSPC; |
155 | goto out_free; | 170 | goto out_free; |
156 | } | 171 | } |
172 | set_irq_data(virq, msi_data); | ||
157 | set_irq_msi(virq, entry); | 173 | set_irq_msi(virq, entry); |
158 | 174 | ||
159 | fsl_compose_msi_msg(pdev, hwirq, &msg); | 175 | fsl_compose_msi_msg(pdev, hwirq, &msg, msi_data); |
160 | write_msi_msg(virq, &msg); | 176 | write_msi_msg(virq, &msg); |
161 | } | 177 | } |
162 | return 0; | 178 | return 0; |
163 | 179 | ||
164 | out_free: | 180 | out_free: |
181 | /* free by the caller of this function */ | ||
165 | return rc; | 182 | return rc; |
166 | } | 183 | } |
167 | 184 | ||
168 | static void fsl_msi_cascade(unsigned int irq, struct irq_desc *desc) | 185 | static void fsl_msi_cascade(unsigned int irq, struct irq_desc *desc) |
169 | { | 186 | { |
170 | unsigned int cascade_irq; | 187 | unsigned int cascade_irq; |
171 | struct fsl_msi *msi_data = fsl_msi; | 188 | struct fsl_msi *msi_data; |
172 | int msir_index = -1; | 189 | int msir_index = -1; |
173 | u32 msir_value = 0; | 190 | u32 msir_value = 0; |
174 | u32 intr_index; | 191 | u32 intr_index; |
175 | u32 have_shift = 0; | 192 | u32 have_shift = 0; |
193 | struct fsl_msi_cascade_data *cascade_data; | ||
194 | |||
195 | cascade_data = (struct fsl_msi_cascade_data *)get_irq_data(irq); | ||
196 | msi_data = cascade_data->msi_data; | ||
176 | 197 | ||
177 | raw_spin_lock(&desc->lock); | 198 | raw_spin_lock(&desc->lock); |
178 | if ((msi_data->feature & FSL_PIC_IP_MASK) == FSL_PIC_IP_IPIC) { | 199 | if ((msi_data->feature & FSL_PIC_IP_MASK) == FSL_PIC_IP_IPIC) { |
@@ -187,13 +208,13 @@ static void fsl_msi_cascade(unsigned int irq, struct irq_desc *desc) | |||
187 | if (unlikely(desc->status & IRQ_INPROGRESS)) | 208 | if (unlikely(desc->status & IRQ_INPROGRESS)) |
188 | goto unlock; | 209 | goto unlock; |
189 | 210 | ||
190 | msir_index = (int)desc->handler_data; | 211 | msir_index = cascade_data->index; |
191 | 212 | ||
192 | if (msir_index >= NR_MSI_REG) | 213 | if (msir_index >= NR_MSI_REG) |
193 | cascade_irq = NO_IRQ; | 214 | cascade_irq = NO_IRQ; |
194 | 215 | ||
195 | desc->status |= IRQ_INPROGRESS; | 216 | desc->status |= IRQ_INPROGRESS; |
196 | switch (fsl_msi->feature & FSL_PIC_IP_MASK) { | 217 | switch (msi_data->feature & FSL_PIC_IP_MASK) { |
197 | case FSL_PIC_IP_MPIC: | 218 | case FSL_PIC_IP_MPIC: |
198 | msir_value = fsl_msi_read(msi_data->msi_regs, | 219 | msir_value = fsl_msi_read(msi_data->msi_regs, |
199 | msir_index * 0x10); | 220 | msir_index * 0x10); |
@@ -229,6 +250,30 @@ unlock: | |||
229 | raw_spin_unlock(&desc->lock); | 250 | raw_spin_unlock(&desc->lock); |
230 | } | 251 | } |
231 | 252 | ||
253 | static int fsl_of_msi_remove(struct of_device *ofdev) | ||
254 | { | ||
255 | struct fsl_msi *msi = ofdev->dev.platform_data; | ||
256 | int virq, i; | ||
257 | struct fsl_msi_cascade_data *cascade_data; | ||
258 | |||
259 | if (msi->list.prev != NULL) | ||
260 | list_del(&msi->list); | ||
261 | for (i = 0; i < NR_MSI_REG; i++) { | ||
262 | virq = msi->msi_virqs[i]; | ||
263 | if (virq != NO_IRQ) { | ||
264 | cascade_data = get_irq_data(virq); | ||
265 | kfree(cascade_data); | ||
266 | irq_dispose_mapping(virq); | ||
267 | } | ||
268 | } | ||
269 | if (msi->bitmap.bitmap) | ||
270 | msi_bitmap_free(&msi->bitmap); | ||
271 | iounmap(msi->msi_regs); | ||
272 | kfree(msi); | ||
273 | |||
274 | return 0; | ||
275 | } | ||
276 | |||
232 | static int __devinit fsl_of_msi_probe(struct of_device *dev, | 277 | static int __devinit fsl_of_msi_probe(struct of_device *dev, |
233 | const struct of_device_id *match) | 278 | const struct of_device_id *match) |
234 | { | 279 | { |
@@ -239,15 +284,18 @@ static int __devinit fsl_of_msi_probe(struct of_device *dev, | |||
239 | int virt_msir; | 284 | int virt_msir; |
240 | const u32 *p; | 285 | const u32 *p; |
241 | struct fsl_msi_feature *features = match->data; | 286 | struct fsl_msi_feature *features = match->data; |
287 | struct fsl_msi_cascade_data *cascade_data = NULL; | ||
288 | int len; | ||
289 | u32 offset; | ||
242 | 290 | ||
243 | printk(KERN_DEBUG "Setting up Freescale MSI support\n"); | 291 | printk(KERN_DEBUG "Setting up Freescale MSI support\n"); |
244 | 292 | ||
245 | msi = kzalloc(sizeof(struct fsl_msi), GFP_KERNEL); | 293 | msi = kzalloc(sizeof(struct fsl_msi), GFP_KERNEL); |
246 | if (!msi) { | 294 | if (!msi) { |
247 | dev_err(&dev->dev, "No memory for MSI structure\n"); | 295 | dev_err(&dev->dev, "No memory for MSI structure\n"); |
248 | err = -ENOMEM; | 296 | return -ENOMEM; |
249 | goto error_out; | ||
250 | } | 297 | } |
298 | dev->dev.platform_data = msi; | ||
251 | 299 | ||
252 | msi->irqhost = irq_alloc_host(dev->dev.of_node, IRQ_HOST_MAP_LINEAR, | 300 | msi->irqhost = irq_alloc_host(dev->dev.of_node, IRQ_HOST_MAP_LINEAR, |
253 | NR_MSI_IRQS, &fsl_msi_host_ops, 0); | 301 | NR_MSI_IRQS, &fsl_msi_host_ops, 0); |
@@ -298,27 +346,47 @@ static int __devinit fsl_of_msi_probe(struct of_device *dev, | |||
298 | err = -EINVAL; | 346 | err = -EINVAL; |
299 | goto error_out; | 347 | goto error_out; |
300 | } | 348 | } |
349 | offset = 0; | ||
350 | p = of_get_property(dev->node, "msi-available-ranges", &len); | ||
351 | if (p) | ||
352 | offset = *p / IRQS_PER_MSI_REG; | ||
301 | 353 | ||
302 | count /= sizeof(u32); | 354 | count /= sizeof(u32); |
303 | for (i = 0; i < count / 2; i++) { | 355 | for (i = 0; i < min(count / 2, NR_MSI_REG); i++) { |
304 | if (i > NR_MSI_REG) | ||
305 | break; | ||
306 | virt_msir = irq_of_parse_and_map(dev->dev.of_node, i); | 356 | virt_msir = irq_of_parse_and_map(dev->dev.of_node, i); |
307 | if (virt_msir != NO_IRQ) { | 357 | if (virt_msir != NO_IRQ) { |
308 | set_irq_data(virt_msir, (void *)i); | 358 | cascade_data = kzalloc( |
359 | sizeof(struct fsl_msi_cascade_data), | ||
360 | GFP_KERNEL); | ||
361 | if (!cascade_data) { | ||
362 | dev_err(&dev->dev, | ||
363 | "No memory for MSI cascade data\n"); | ||
364 | err = -ENOMEM; | ||
365 | goto error_out; | ||
366 | } | ||
367 | msi->msi_virqs[i] = virt_msir; | ||
368 | cascade_data->index = i + offset; | ||
369 | cascade_data->msi_data = msi; | ||
370 | set_irq_data(virt_msir, (void *)cascade_data); | ||
309 | set_irq_chained_handler(virt_msir, fsl_msi_cascade); | 371 | set_irq_chained_handler(virt_msir, fsl_msi_cascade); |
310 | } | 372 | } |
311 | } | 373 | } |
312 | 374 | ||
313 | fsl_msi = msi; | 375 | list_add_tail(&msi->list, &msi_head); |
314 | 376 | ||
315 | WARN_ON(ppc_md.setup_msi_irqs); | 377 | /* The multiple setting ppc_md.setup_msi_irqs will not harm things */ |
316 | ppc_md.setup_msi_irqs = fsl_setup_msi_irqs; | 378 | if (!ppc_md.setup_msi_irqs) { |
317 | ppc_md.teardown_msi_irqs = fsl_teardown_msi_irqs; | 379 | ppc_md.setup_msi_irqs = fsl_setup_msi_irqs; |
318 | ppc_md.msi_check_device = fsl_msi_check_device; | 380 | ppc_md.teardown_msi_irqs = fsl_teardown_msi_irqs; |
381 | ppc_md.msi_check_device = fsl_msi_check_device; | ||
382 | } else if (ppc_md.setup_msi_irqs != fsl_setup_msi_irqs) { | ||
383 | dev_err(&dev->dev, "Different MSI driver already installed!\n"); | ||
384 | err = -ENODEV; | ||
385 | goto error_out; | ||
386 | } | ||
319 | return 0; | 387 | return 0; |
320 | error_out: | 388 | error_out: |
321 | kfree(msi); | 389 | fsl_of_msi_remove(dev); |
322 | return err; | 390 | return err; |
323 | } | 391 | } |
324 | 392 | ||
@@ -351,6 +419,7 @@ static struct of_platform_driver fsl_of_msi_driver = { | |||
351 | .of_match_table = fsl_of_msi_ids, | 419 | .of_match_table = fsl_of_msi_ids, |
352 | }, | 420 | }, |
353 | .probe = fsl_of_msi_probe, | 421 | .probe = fsl_of_msi_probe, |
422 | .remove = fsl_of_msi_remove, | ||
354 | }; | 423 | }; |
355 | 424 | ||
356 | static __init int fsl_of_msi_init(void) | 425 | static __init int fsl_of_msi_init(void) |