]> bbs.cooldavid.org Git - net-next-2.6.git/blame - arch/ia64/sn/kernel/msi_sn.c
pci: Convert msi to new irq_chip functions
[net-next-2.6.git] / arch / ia64 / sn / kernel / msi_sn.c
CommitLineData
fd58e55f
MM
1/*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License. See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * Copyright (C) 2006 Silicon Graphics, Inc. All Rights Reserved.
7 */
8
83821d3f 9#include <linux/types.h>
3b7d1921 10#include <linux/irq.h>
83821d3f
MM
11#include <linux/pci.h>
12#include <linux/cpumask.h>
3b7d1921 13#include <linux/msi.h>
5a0e3ad6 14#include <linux/slab.h>
83821d3f
MM
15
16#include <asm/sn/addrs.h>
17#include <asm/sn/intr.h>
18#include <asm/sn/pcibus_provider_defs.h>
19#include <asm/sn/pcidev.h>
20#include <asm/sn/nodepda.h>
21
83821d3f
MM
22struct sn_msi_info {
23 u64 pci_addr;
24 struct sn_irq_info *sn_irq_info;
25};
26
3b7d1921
EB
27static struct sn_msi_info sn_msi_info[NR_IRQS];
28
29static struct irq_chip sn_msi_chip;
83821d3f 30
3b7d1921 31void sn_teardown_msi_irq(unsigned int irq)
83821d3f
MM
32{
33 nasid_t nasid;
34 int widget;
35 struct pci_dev *pdev;
36 struct pcidev_info *sn_pdev;
37 struct sn_irq_info *sn_irq_info;
38 struct pcibus_bussoft *bussoft;
39 struct sn_pcibus_provider *provider;
40
38bc0361 41 sn_irq_info = sn_msi_info[irq].sn_irq_info;
83821d3f
MM
42 if (sn_irq_info == NULL || sn_irq_info->irq_int_bit >= 0)
43 return;
44
45 sn_pdev = (struct pcidev_info *)sn_irq_info->irq_pciioinfo;
46 pdev = sn_pdev->pdi_linux_pcidev;
47 provider = SN_PCIDEV_BUSPROVIDER(pdev);
48
49 (*provider->dma_unmap)(pdev,
38bc0361 50 sn_msi_info[irq].pci_addr,
83821d3f 51 PCI_DMA_FROMDEVICE);
38bc0361 52 sn_msi_info[irq].pci_addr = 0;
83821d3f
MM
53
54 bussoft = SN_PCIDEV_BUSSOFT(pdev);
55 nasid = NASID_GET(bussoft->bs_base);
56 widget = (nasid & 1) ?
57 TIO_SWIN_WIDGETNUM(bussoft->bs_base) :
58 SWIN_WIDGETNUM(bussoft->bs_base);
59
60 sn_intr_free(nasid, widget, sn_irq_info);
38bc0361 61 sn_msi_info[irq].sn_irq_info = NULL;
83821d3f 62
f7feaca7 63 destroy_irq(irq);
83821d3f 64}
fd58e55f 65
f7feaca7 66int sn_setup_msi_irq(struct pci_dev *pdev, struct msi_desc *entry)
fd58e55f 67{
3b7d1921 68 struct msi_msg msg;
83821d3f
MM
69 int widget;
70 int status;
71 nasid_t nasid;
72 u64 bus_addr;
73 struct sn_irq_info *sn_irq_info;
74 struct pcibus_bussoft *bussoft = SN_PCIDEV_BUSSOFT(pdev);
75 struct sn_pcibus_provider *provider = SN_PCIDEV_BUSPROVIDER(pdev);
f7feaca7 76 int irq;
83821d3f 77
3b7d1921
EB
78 if (!entry->msi_attrib.is_64)
79 return -EINVAL;
80
83821d3f
MM
81 if (bussoft == NULL)
82 return -EINVAL;
83
84 if (provider == NULL || provider->dma_map_consistent == NULL)
85 return -EINVAL;
86
f7feaca7
EB
87 irq = create_irq();
88 if (irq < 0)
89 return irq;
90
83821d3f
MM
91 /*
92 * Set up the vector plumbing. Let the prom (via sn_intr_alloc)
93 * decide which cpu to direct this msi at by default.
94 */
95
96 nasid = NASID_GET(bussoft->bs_base);
97 widget = (nasid & 1) ?
98 TIO_SWIN_WIDGETNUM(bussoft->bs_base) :
99 SWIN_WIDGETNUM(bussoft->bs_base);
100
101 sn_irq_info = kzalloc(sizeof(struct sn_irq_info), GFP_KERNEL);
f7feaca7
EB
102 if (! sn_irq_info) {
103 destroy_irq(irq);
83821d3f 104 return -ENOMEM;
f7feaca7 105 }
83821d3f 106
38bc0361 107 status = sn_intr_alloc(nasid, widget, sn_irq_info, irq, -1, -1);
83821d3f
MM
108 if (status) {
109 kfree(sn_irq_info);
f7feaca7 110 destroy_irq(irq);
83821d3f
MM
111 return -ENOMEM;
112 }
113
114 sn_irq_info->irq_int_bit = -1; /* mark this as an MSI irq */
115 sn_irq_fixup(pdev, sn_irq_info);
116
117 /* Prom probably should fill these in, but doesn't ... */
118 sn_irq_info->irq_bridge_type = bussoft->bs_asic_type;
119 sn_irq_info->irq_bridge = (void *)bussoft->bs_base;
120
fd58e55f 121 /*
83821d3f 122 * Map the xio address into bus space
fd58e55f 123 */
83821d3f
MM
124 bus_addr = (*provider->dma_map_consistent)(pdev,
125 sn_irq_info->irq_xtalkaddr,
126 sizeof(sn_irq_info->irq_xtalkaddr),
127 SN_DMA_MSI|SN_DMA_ADDR_XIO);
128 if (! bus_addr) {
129 sn_intr_free(nasid, widget, sn_irq_info);
130 kfree(sn_irq_info);
f7feaca7 131 destroy_irq(irq);
83821d3f
MM
132 return -ENOMEM;
133 }
134
38bc0361
EB
135 sn_msi_info[irq].sn_irq_info = sn_irq_info;
136 sn_msi_info[irq].pci_addr = bus_addr;
83821d3f 137
3b7d1921
EB
138 msg.address_hi = (u32)(bus_addr >> 32);
139 msg.address_lo = (u32)(bus_addr & 0x00000000ffffffff);
83821d3f
MM
140
141 /*
142 * In the SN platform, bit 16 is a "send vector" bit which
143 * must be present in order to move the vector through the system.
144 */
3b7d1921 145 msg.data = 0x100 + irq;
83821d3f 146
7fe3730d 147 set_irq_msi(irq, entry);
3b7d1921
EB
148 write_msi_msg(irq, &msg);
149 set_irq_chip_and_handler(irq, &sn_msi_chip, handle_edge_irq);
150
7fe3730d 151 return 0;
83821d3f
MM
152}
153
3b7d1921 154#ifdef CONFIG_SMP
d5dedd45 155static int sn_set_msi_irq_affinity(unsigned int irq,
0de26520 156 const struct cpumask *cpu_mask)
83821d3f 157{
3b7d1921 158 struct msi_msg msg;
83821d3f
MM
159 int slice;
160 nasid_t nasid;
161 u64 bus_addr;
162 struct pci_dev *pdev;
163 struct pcidev_info *sn_pdev;
164 struct sn_irq_info *sn_irq_info;
165 struct sn_irq_info *new_irq_info;
166 struct sn_pcibus_provider *provider;
38bc0361 167 unsigned int cpu;
83821d3f 168
0de26520 169 cpu = cpumask_first(cpu_mask);
38bc0361 170 sn_irq_info = sn_msi_info[irq].sn_irq_info;
83821d3f 171 if (sn_irq_info == NULL || sn_irq_info->irq_int_bit >= 0)
d5dedd45 172 return -1;
83821d3f
MM
173
174 /*
175 * Release XIO resources for the old MSI PCI address
176 */
177
30da5524 178 get_cached_msi_msg(irq, &msg);
83821d3f
MM
179 sn_pdev = (struct pcidev_info *)sn_irq_info->irq_pciioinfo;
180 pdev = sn_pdev->pdi_linux_pcidev;
181 provider = SN_PCIDEV_BUSPROVIDER(pdev);
182
3b7d1921 183 bus_addr = (u64)(msg.address_hi) << 32 | (u64)(msg.address_lo);
83821d3f 184 (*provider->dma_unmap)(pdev, bus_addr, PCI_DMA_FROMDEVICE);
38bc0361 185 sn_msi_info[irq].pci_addr = 0;
83821d3f
MM
186
187 nasid = cpuid_to_nasid(cpu);
188 slice = cpuid_to_slice(cpu);
189
190 new_irq_info = sn_retarget_vector(sn_irq_info, nasid, slice);
38bc0361 191 sn_msi_info[irq].sn_irq_info = new_irq_info;
83821d3f 192 if (new_irq_info == NULL)
d5dedd45 193 return -1;
83821d3f
MM
194
195 /*
196 * Map the xio address into bus space
197 */
198
199 bus_addr = (*provider->dma_map_consistent)(pdev,
200 new_irq_info->irq_xtalkaddr,
201 sizeof(new_irq_info->irq_xtalkaddr),
202 SN_DMA_MSI|SN_DMA_ADDR_XIO);
203
38bc0361 204 sn_msi_info[irq].pci_addr = bus_addr;
3b7d1921
EB
205 msg.address_hi = (u32)(bus_addr >> 32);
206 msg.address_lo = (u32)(bus_addr & 0x00000000ffffffff);
207
208 write_msi_msg(irq, &msg);
e65e49d0 209 cpumask_copy(irq_desc[irq].affinity, cpu_mask);
d5dedd45
YL
210
211 return 0;
83821d3f 212}
3b7d1921 213#endif /* CONFIG_SMP */
83821d3f 214
3b7d1921
EB
215static void sn_ack_msi_irq(unsigned int irq)
216{
217 move_native_irq(irq);
218 ia64_eoi();
219}
83821d3f 220
3b7d1921 221static int sn_msi_retrigger_irq(unsigned int irq)
83821d3f 222{
3b7d1921
EB
223 unsigned int vector = irq;
224 ia64_resend_irq(vector);
83821d3f 225
3b7d1921 226 return 1;
fd58e55f 227}
3b7d1921
EB
228
229static struct irq_chip sn_msi_chip = {
230 .name = "PCI-MSI",
1c9db525
TG
231 .irq_mask = mask_msi_irq,
232 .irq_unmask = unmask_msi_irq,
3b7d1921
EB
233 .ack = sn_ack_msi_irq,
234#ifdef CONFIG_SMP
235 .set_affinity = sn_set_msi_irq_affinity,
236#endif
237 .retrigger = sn_msi_retrigger_irq,
238};