Merge git://git.kernel.org/pub/scm/linux/kernel/git/davem/net
[pandora-kernel.git] / arch / powerpc / platforms / wsp / msi.c
1 /*
2  * Copyright 2011 Michael Ellerman, IBM Corp.
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; either version
7  * 2 of the License, or (at your option) any later version.
8  */
9
10 #include <linux/kernel.h>
11 #include <linux/pci.h>
12 #include <linux/msi.h>
13 #include <linux/irq.h>
14 #include <linux/interrupt.h>
15
16 #include "msi.h"
17 #include "ics.h"
18 #include "wsp_pci.h"
19
20 /* Magic addresses for 32 & 64-bit MSIs with hardcoded MVE 0 */
21 #define MSI_ADDR_32             0xFFFF0000ul
22 #define MSI_ADDR_64             0x1000000000000000ul
23
24 int wsp_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
25 {
26         struct pci_controller *phb;
27         struct msi_desc *entry;
28         struct msi_msg msg;
29         unsigned int virq;
30         int hwirq;
31
32         phb = pci_bus_to_host(dev->bus);
33         if (!phb)
34                 return -ENOENT;
35
36         entry = list_first_entry(&dev->msi_list, struct msi_desc, list);
37         if (entry->msi_attrib.is_64) {
38                 msg.address_lo = 0;
39                 msg.address_hi = MSI_ADDR_64 >> 32;
40         } else {
41                 msg.address_lo = MSI_ADDR_32;
42                 msg.address_hi = 0;
43         }
44
45         list_for_each_entry(entry, &dev->msi_list, list) {
46                 hwirq = wsp_ics_alloc_irq(phb->dn, 1);
47                 if (hwirq < 0) {
48                         dev_warn(&dev->dev, "wsp_msi: hwirq alloc failed!\n");
49                         return hwirq;
50                 }
51
52                 virq = irq_create_mapping(NULL, hwirq);
53                 if (virq == NO_IRQ) {
54                         dev_warn(&dev->dev, "wsp_msi: virq alloc failed!\n");
55                         return -1;
56                 }
57
58                 dev_dbg(&dev->dev, "wsp_msi: allocated irq %#x/%#x\n",
59                         hwirq, virq);
60
61                 wsp_ics_set_msi_chip(virq);
62                 irq_set_msi_desc(virq, entry);
63                 msg.data = hwirq & XIVE_ADDR_MASK;
64                 write_msi_msg(virq, &msg);
65         }
66
67         return 0;
68 }
69
70 void wsp_teardown_msi_irqs(struct pci_dev *dev)
71 {
72         struct pci_controller *phb;
73         struct msi_desc *entry;
74         int hwirq;
75
76         phb = pci_bus_to_host(dev->bus);
77
78         dev_dbg(&dev->dev, "wsp_msi: tearing down msi irqs\n");
79
80         list_for_each_entry(entry, &dev->msi_list, list) {
81                 if (entry->irq == NO_IRQ)
82                         continue;
83
84                 irq_set_msi_desc(entry->irq, NULL);
85                 wsp_ics_set_std_chip(entry->irq);
86
87                 hwirq = virq_to_hw(entry->irq);
88                 /* In this order to avoid racing with irq_create_mapping() */
89                 irq_dispose_mapping(entry->irq);
90                 wsp_ics_free_irq(phb->dn, hwirq);
91         }
92 }
93
94 void wsp_setup_phb_msi(struct pci_controller *phb)
95 {
96         /* Create a single MVE at offset 0 that matches everything */
97         out_be64(phb->cfg_data + PCIE_REG_IODA_ADDR, PCIE_REG_IODA_AD_TBL_MVT);
98         out_be64(phb->cfg_data + PCIE_REG_IODA_DATA0, 1ull << 63);
99
100         ppc_md.setup_msi_irqs = wsp_setup_msi_irqs;
101         ppc_md.teardown_msi_irqs = wsp_teardown_msi_irqs;
102 }