Blame view
drivers/pci/msi.c
25.6 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 6 7 |
/* * File: msi.c * Purpose: PCI Message Signaled Interrupt (MSI) * * Copyright (C) 2003-2004 Intel * Copyright (C) Tom Long Nguyen (tom.l.nguyen@intel.com) */ |
1ce03373a [PATCH] genirq: m... |
8 |
#include <linux/err.h> |
1da177e4c Linux-2.6.12-rc2 |
9 10 11 12 |
#include <linux/mm.h> #include <linux/irq.h> #include <linux/interrupt.h> #include <linux/init.h> |
363c75db1 pci: Fix files ne... |
13 |
#include <linux/export.h> |
1da177e4c Linux-2.6.12-rc2 |
14 |
#include <linux/ioport.h> |
1da177e4c Linux-2.6.12-rc2 |
15 16 |
#include <linux/pci.h> #include <linux/proc_fs.h> |
3b7d1921f [PATCH] msi: refa... |
17 |
#include <linux/msi.h> |
4fdadebc3 msi: fix ARM compile |
18 |
#include <linux/smp.h> |
500559a92 PCI MSI: Style cl... |
19 20 |
#include <linux/errno.h> #include <linux/io.h> |
5a0e3ad6a include cleanup: ... |
21 |
#include <linux/slab.h> |
1da177e4c Linux-2.6.12-rc2 |
22 23 24 |
#include "pci.h" #include "msi.h" |
1da177e4c Linux-2.6.12-rc2 |
25 |
static int pci_msi_enable = 1; |
1da177e4c Linux-2.6.12-rc2 |
26 |
|
6a9e7f203 PCI: drivers/pci/... |
27 |
/* Arch hooks */ |
11df1f055 PCI/MSI: Use #ifd... |
28 29 |
#ifndef arch_msi_check_device int arch_msi_check_device(struct pci_dev *dev, int nvec, int type) |
6a9e7f203 PCI: drivers/pci/... |
30 31 32 |
{ return 0; } |
11df1f055 PCI/MSI: Use #ifd... |
33 |
#endif |
6a9e7f203 PCI: drivers/pci/... |
34 |
|
11df1f055 PCI/MSI: Use #ifd... |
35 |
#ifndef arch_setup_msi_irqs |
1525bf0d8 msi: Introduce de... |
36 37 38 39 40 41 |
# define arch_setup_msi_irqs default_setup_msi_irqs # define HAVE_DEFAULT_MSI_SETUP_IRQS #endif #ifdef HAVE_DEFAULT_MSI_SETUP_IRQS int default_setup_msi_irqs(struct pci_dev *dev, int nvec, int type) |
6a9e7f203 PCI: drivers/pci/... |
42 43 44 |
{ struct msi_desc *entry; int ret; |
1c8d7b0a5 PCI MSI: Add supp... |
45 46 47 48 49 50 |
/* * If an architecture wants to support multiple MSI, it needs to * override arch_setup_msi_irqs() */ if (type == PCI_CAP_ID_MSI && nvec > 1) return 1; |
6a9e7f203 PCI: drivers/pci/... |
51 52 |
list_for_each_entry(entry, &dev->msi_list, list) { ret = arch_setup_msi_irq(dev, entry); |
b5fbf5332 PCI/MSI: Allow ar... |
53 |
if (ret < 0) |
6a9e7f203 PCI: drivers/pci/... |
54 |
return ret; |
b5fbf5332 PCI/MSI: Allow ar... |
55 56 |
if (ret > 0) return -ENOSPC; |
6a9e7f203 PCI: drivers/pci/... |
57 58 59 60 |
} return 0; } |
11df1f055 PCI/MSI: Use #ifd... |
61 |
#endif |
6a9e7f203 PCI: drivers/pci/... |
62 |
|
11df1f055 PCI/MSI: Use #ifd... |
63 |
#ifndef arch_teardown_msi_irqs |
1525bf0d8 msi: Introduce de... |
64 65 66 67 68 69 |
# define arch_teardown_msi_irqs default_teardown_msi_irqs # define HAVE_DEFAULT_MSI_TEARDOWN_IRQS #endif #ifdef HAVE_DEFAULT_MSI_TEARDOWN_IRQS void default_teardown_msi_irqs(struct pci_dev *dev) |
6a9e7f203 PCI: drivers/pci/... |
70 71 72 73 |
{ struct msi_desc *entry; list_for_each_entry(entry, &dev->msi_list, list) { |
1c8d7b0a5 PCI MSI: Add supp... |
74 75 76 77 78 79 |
int i, nvec; if (entry->irq == 0) continue; nvec = 1 << entry->msi_attrib.multiple; for (i = 0; i < nvec; i++) arch_teardown_msi_irq(entry->irq + i); |
6a9e7f203 PCI: drivers/pci/... |
80 81 |
} } |
11df1f055 PCI/MSI: Use #ifd... |
82 |
#endif |
6a9e7f203 PCI: drivers/pci/... |
83 |
|
76ccc2970 x86/PCI: Expand t... |
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
#ifndef arch_restore_msi_irqs # define arch_restore_msi_irqs default_restore_msi_irqs # define HAVE_DEFAULT_MSI_RESTORE_IRQS #endif #ifdef HAVE_DEFAULT_MSI_RESTORE_IRQS void default_restore_msi_irqs(struct pci_dev *dev, int irq) { struct msi_desc *entry; entry = NULL; if (dev->msix_enabled) { list_for_each_entry(entry, &dev->msi_list, list) { if (irq == entry->irq) break; } } else if (dev->msi_enabled) { entry = irq_get_msi_desc(irq); } if (entry) write_msi_msg(irq, &entry->msg); } #endif |
110828c9c PCI: remove redun... |
108 |
static void msi_set_enable(struct pci_dev *dev, int pos, int enable) |
b1cbf4e4d [PATCH] msi: fix ... |
109 |
{ |
b1cbf4e4d [PATCH] msi: fix ... |
110 |
u16 control; |
110828c9c PCI: remove redun... |
111 |
BUG_ON(!pos); |
b1cbf4e4d [PATCH] msi: fix ... |
112 |
|
110828c9c PCI: remove redun... |
113 114 115 116 117 |
pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control); control &= ~PCI_MSI_FLAGS_ENABLE; if (enable) control |= PCI_MSI_FLAGS_ENABLE; pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control); |
5ca5c02f0 PCI/MSI: skip cal... |
118 |
} |
b1cbf4e4d [PATCH] msi: fix ... |
119 120 121 122 123 124 125 126 127 128 129 130 131 132 |
static void msix_set_enable(struct pci_dev *dev, int enable) { int pos; u16 control; pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); if (pos) { pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control); control &= ~PCI_MSIX_FLAGS_ENABLE; if (enable) control |= PCI_MSIX_FLAGS_ENABLE; pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); } } |
bffac3c59 PCI MSI: Fix unde... |
133 134 |
static inline __attribute_const__ u32 msi_mask(unsigned x) { |
0b49ec37a PCI/MSI: fix msi_... |
135 136 137 138 |
/* Don't shift by >= width of type */ if (x >= 5) return 0xffffffff; return (1 << (1 << x)) - 1; |
bffac3c59 PCI MSI: Fix unde... |
139 |
} |
f2440d9ac PCI MSI: Refactor... |
140 |
static inline __attribute_const__ u32 msi_capable_mask(u16 control) |
988cbb15e PCI: Flush MSI-X ... |
141 |
{ |
f2440d9ac PCI MSI: Refactor... |
142 143 |
return msi_mask((control >> 1) & 7); } |
988cbb15e PCI: Flush MSI-X ... |
144 |
|
f2440d9ac PCI MSI: Refactor... |
145 146 147 |
static inline __attribute_const__ u32 msi_enabled_mask(u16 control) { return msi_mask((control >> 4) & 7); |
988cbb15e PCI: Flush MSI-X ... |
148 |
} |
ce6fce429 PCI MSI: Don't di... |
149 150 151 152 153 |
/* * PCI 2.3 does not specify mask bits for each MSI interrupt. Attempting to * mask all MSI interrupts by clearing the MSI enable bit does not work * reliably as devices without an INTx disable bit will then generate a * level IRQ which will never be cleared. |
ce6fce429 PCI MSI: Don't di... |
154 |
*/ |
12abb8ba8 PCI MSI: Fix rest... |
155 |
static u32 __msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) |
1da177e4c Linux-2.6.12-rc2 |
156 |
{ |
f2440d9ac PCI MSI: Refactor... |
157 |
u32 mask_bits = desc->masked; |
1da177e4c Linux-2.6.12-rc2 |
158 |
|
f2440d9ac PCI MSI: Refactor... |
159 |
if (!desc->msi_attrib.maskbit) |
12abb8ba8 PCI MSI: Fix rest... |
160 |
return 0; |
f2440d9ac PCI MSI: Refactor... |
161 162 163 164 |
mask_bits &= ~mask; mask_bits |= flag; pci_write_config_dword(desc->dev, desc->mask_pos, mask_bits); |
12abb8ba8 PCI MSI: Fix rest... |
165 166 167 168 169 170 171 |
return mask_bits; } static void msi_mask_irq(struct msi_desc *desc, u32 mask, u32 flag) { desc->masked = __msi_mask_irq(desc, mask, flag); |
f2440d9ac PCI MSI: Refactor... |
172 173 174 175 176 177 178 179 180 |
} /* * This internal function does not flush PCI writes to the device. * All users must ensure that they read from the device before either * assuming that the device state is up to date, or returning out of this * file. This saves a few milliseconds when initialising devices with lots * of MSI-X interrupts. */ |
12abb8ba8 PCI MSI: Fix rest... |
181 |
static u32 __msix_mask_irq(struct msi_desc *desc, u32 flag) |
f2440d9ac PCI MSI: Refactor... |
182 183 184 |
{ u32 mask_bits = desc->masked; unsigned offset = desc->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + |
2c21fd4b3 PCI MSI: shorten ... |
185 |
PCI_MSIX_ENTRY_VECTOR_CTRL; |
8d8052869 PCI: Add mask bit... |
186 187 188 |
mask_bits &= ~PCI_MSIX_ENTRY_CTRL_MASKBIT; if (flag) mask_bits |= PCI_MSIX_ENTRY_CTRL_MASKBIT; |
f2440d9ac PCI MSI: Refactor... |
189 |
writel(mask_bits, desc->mask_base + offset); |
12abb8ba8 PCI MSI: Fix rest... |
190 191 192 193 194 195 196 |
return mask_bits; } static void msix_mask_irq(struct msi_desc *desc, u32 flag) { desc->masked = __msix_mask_irq(desc, flag); |
f2440d9ac PCI MSI: Refactor... |
197 |
} |
24d275533 PCI MSI: Replace ... |
198 |
|
1c9db5253 pci: Convert msi ... |
199 |
static void msi_set_mask_bit(struct irq_data *data, u32 flag) |
f2440d9ac PCI MSI: Refactor... |
200 |
{ |
1c9db5253 pci: Convert msi ... |
201 |
struct msi_desc *desc = irq_data_get_msi(data); |
24d275533 PCI MSI: Replace ... |
202 |
|
f2440d9ac PCI MSI: Refactor... |
203 204 205 206 |
if (desc->msi_attrib.is_msix) { msix_mask_irq(desc, flag); readl(desc->mask_base); /* Flush write to device */ } else { |
1c9db5253 pci: Convert msi ... |
207 |
unsigned offset = data->irq - desc->dev->irq; |
1c8d7b0a5 PCI MSI: Add supp... |
208 |
msi_mask_irq(desc, 1 << offset, flag << offset); |
1da177e4c Linux-2.6.12-rc2 |
209 |
} |
f2440d9ac PCI MSI: Refactor... |
210 |
} |
1c9db5253 pci: Convert msi ... |
211 |
void mask_msi_irq(struct irq_data *data) |
f2440d9ac PCI MSI: Refactor... |
212 |
{ |
1c9db5253 pci: Convert msi ... |
213 |
msi_set_mask_bit(data, 1); |
f2440d9ac PCI MSI: Refactor... |
214 |
} |
1c9db5253 pci: Convert msi ... |
215 |
void unmask_msi_irq(struct irq_data *data) |
f2440d9ac PCI MSI: Refactor... |
216 |
{ |
1c9db5253 pci: Convert msi ... |
217 |
msi_set_mask_bit(data, 0); |
1da177e4c Linux-2.6.12-rc2 |
218 |
} |
39431acb1 pci: Cleanup the ... |
219 |
void __read_msi_msg(struct msi_desc *entry, struct msi_msg *msg) |
1da177e4c Linux-2.6.12-rc2 |
220 |
{ |
30da55242 PCI: MSI: Restore... |
221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
BUG_ON(entry->dev->current_state != PCI_D0); if (entry->msi_attrib.is_msix) { void __iomem *base = entry->mask_base + entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE; msg->address_lo = readl(base + PCI_MSIX_ENTRY_LOWER_ADDR); msg->address_hi = readl(base + PCI_MSIX_ENTRY_UPPER_ADDR); msg->data = readl(base + PCI_MSIX_ENTRY_DATA); } else { struct pci_dev *dev = entry->dev; int pos = entry->msi_attrib.pos; u16 data; pci_read_config_dword(dev, msi_lower_address_reg(pos), &msg->address_lo); if (entry->msi_attrib.is_64) { pci_read_config_dword(dev, msi_upper_address_reg(pos), &msg->address_hi); pci_read_config_word(dev, msi_data_reg(pos, 1), &data); } else { msg->address_hi = 0; pci_read_config_word(dev, msi_data_reg(pos, 0), &data); } msg->data = data; } } void read_msi_msg(unsigned int irq, struct msi_msg *msg) { |
dced35aeb drivers: Final ir... |
251 |
struct msi_desc *entry = irq_get_msi_desc(irq); |
30da55242 PCI: MSI: Restore... |
252 |
|
39431acb1 pci: Cleanup the ... |
253 |
__read_msi_msg(entry, msg); |
30da55242 PCI: MSI: Restore... |
254 |
} |
39431acb1 pci: Cleanup the ... |
255 |
void __get_cached_msi_msg(struct msi_desc *entry, struct msi_msg *msg) |
30da55242 PCI: MSI: Restore... |
256 |
{ |
30da55242 PCI: MSI: Restore... |
257 |
/* Assert that the cache is valid, assuming that |
fcd097f31 PCI: MSI: Remove ... |
258 259 260 |
* valid messages are not all-zeroes. */ BUG_ON(!(entry->msg.address_hi | entry->msg.address_lo | entry->msg.data)); |
0366f8f71 [PATCH] genirq: m... |
261 |
|
fcd097f31 PCI: MSI: Remove ... |
262 |
*msg = entry->msg; |
0366f8f71 [PATCH] genirq: m... |
263 |
} |
1da177e4c Linux-2.6.12-rc2 |
264 |
|
30da55242 PCI: MSI: Restore... |
265 |
void get_cached_msi_msg(unsigned int irq, struct msi_msg *msg) |
0366f8f71 [PATCH] genirq: m... |
266 |
{ |
dced35aeb drivers: Final ir... |
267 |
struct msi_desc *entry = irq_get_msi_desc(irq); |
3145e941f x86, MSI: pass ir... |
268 |
|
39431acb1 pci: Cleanup the ... |
269 |
__get_cached_msi_msg(entry, msg); |
3145e941f x86, MSI: pass ir... |
270 |
} |
39431acb1 pci: Cleanup the ... |
271 |
void __write_msi_msg(struct msi_desc *entry, struct msi_msg *msg) |
3145e941f x86, MSI: pass ir... |
272 |
{ |
fcd097f31 PCI: MSI: Remove ... |
273 274 275 |
if (entry->dev->current_state != PCI_D0) { /* Don't touch the hardware now */ } else if (entry->msi_attrib.is_msix) { |
24d275533 PCI MSI: Replace ... |
276 277 278 |
void __iomem *base; base = entry->mask_base + entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE; |
2c21fd4b3 PCI MSI: shorten ... |
279 280 281 |
writel(msg->address_lo, base + PCI_MSIX_ENTRY_LOWER_ADDR); writel(msg->address_hi, base + PCI_MSIX_ENTRY_UPPER_ADDR); writel(msg->data, base + PCI_MSIX_ENTRY_DATA); |
24d275533 PCI MSI: Replace ... |
282 |
} else { |
0366f8f71 [PATCH] genirq: m... |
283 284 |
struct pci_dev *dev = entry->dev; int pos = entry->msi_attrib.pos; |
1c8d7b0a5 PCI MSI: Add supp... |
285 286 287 288 289 290 |
u16 msgctl; pci_read_config_word(dev, msi_control_reg(pos), &msgctl); msgctl &= ~PCI_MSI_FLAGS_QSIZE; msgctl |= entry->msi_attrib.multiple << 4; pci_write_config_word(dev, msi_control_reg(pos), msgctl); |
0366f8f71 [PATCH] genirq: m... |
291 292 293 294 295 296 297 298 299 300 301 302 |
pci_write_config_dword(dev, msi_lower_address_reg(pos), msg->address_lo); if (entry->msi_attrib.is_64) { pci_write_config_dword(dev, msi_upper_address_reg(pos), msg->address_hi); pci_write_config_word(dev, msi_data_reg(pos, 1), msg->data); } else { pci_write_config_word(dev, msi_data_reg(pos, 0), msg->data); } |
1da177e4c Linux-2.6.12-rc2 |
303 |
} |
392ee1e6d [PATCH] msi: Safe... |
304 |
entry->msg = *msg; |
1da177e4c Linux-2.6.12-rc2 |
305 |
} |
0366f8f71 [PATCH] genirq: m... |
306 |
|
3145e941f x86, MSI: pass ir... |
307 308 |
void write_msi_msg(unsigned int irq, struct msi_msg *msg) { |
dced35aeb drivers: Final ir... |
309 |
struct msi_desc *entry = irq_get_msi_desc(irq); |
3145e941f x86, MSI: pass ir... |
310 |
|
39431acb1 pci: Cleanup the ... |
311 |
__write_msi_msg(entry, msg); |
3145e941f x86, MSI: pass ir... |
312 |
} |
f56e44813 PCI MSI: Unify ms... |
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 |
static void free_msi_irqs(struct pci_dev *dev) { struct msi_desc *entry, *tmp; list_for_each_entry(entry, &dev->msi_list, list) { int i, nvec; if (!entry->irq) continue; nvec = 1 << entry->msi_attrib.multiple; for (i = 0; i < nvec; i++) BUG_ON(irq_has_action(entry->irq + i)); } arch_teardown_msi_irqs(dev); list_for_each_entry_safe(entry, tmp, &dev->msi_list, list) { if (entry->msi_attrib.is_msix) { if (list_is_last(&entry->list, &dev->msi_list)) iounmap(entry->mask_base); } |
424eb3915 PCI: msi: fix imb... |
333 334 335 336 337 338 339 340 341 342 343 |
/* * Its possible that we get into this path * When populate_msi_sysfs fails, which means the entries * were not registered with sysfs. In that case don't * unregister them. */ if (entry->kobj.parent) { kobject_del(&entry->kobj); kobject_put(&entry->kobj); } |
f56e44813 PCI MSI: Unify ms... |
344 345 346 347 |
list_del(&entry->list); kfree(entry); } } |
c54c18790 PCI: cleanup MSI ... |
348 |
|
379f5327a PCI MSI: msi_desc... |
349 |
static struct msi_desc *alloc_msi_entry(struct pci_dev *dev) |
1da177e4c Linux-2.6.12-rc2 |
350 |
{ |
379f5327a PCI MSI: msi_desc... |
351 352 |
struct msi_desc *desc = kzalloc(sizeof(*desc), GFP_KERNEL); if (!desc) |
1da177e4c Linux-2.6.12-rc2 |
353 |
return NULL; |
379f5327a PCI MSI: msi_desc... |
354 355 |
INIT_LIST_HEAD(&desc->list); desc->dev = dev; |
1da177e4c Linux-2.6.12-rc2 |
356 |
|
379f5327a PCI MSI: msi_desc... |
357 |
return desc; |
1da177e4c Linux-2.6.12-rc2 |
358 |
} |
ba698ad4b PCI: Add quirk fo... |
359 360 361 362 363 |
static void pci_intx_for_msi(struct pci_dev *dev, int enable) { if (!(dev->dev_flags & PCI_DEV_FLAGS_MSI_INTX_DISABLE_BUG)) pci_intx(dev, enable); } |
8fed4b652 MSI: Combine pci_... |
364 |
static void __pci_restore_msi_state(struct pci_dev *dev) |
41017f0ca [PATCH] PCI: MSI(... |
365 |
{ |
392ee1e6d [PATCH] msi: Safe... |
366 |
int pos; |
41017f0ca [PATCH] PCI: MSI(... |
367 |
u16 control; |
392ee1e6d [PATCH] msi: Safe... |
368 |
struct msi_desc *entry; |
41017f0ca [PATCH] PCI: MSI(... |
369 |
|
b1cbf4e4d [PATCH] msi: fix ... |
370 371 |
if (!dev->msi_enabled) return; |
dced35aeb drivers: Final ir... |
372 |
entry = irq_get_msi_desc(dev->irq); |
392ee1e6d [PATCH] msi: Safe... |
373 |
pos = entry->msi_attrib.pos; |
41017f0ca [PATCH] PCI: MSI(... |
374 |
|
ba698ad4b PCI: Add quirk fo... |
375 |
pci_intx_for_msi(dev, 0); |
110828c9c PCI: remove redun... |
376 |
msi_set_enable(dev, pos, 0); |
76ccc2970 x86/PCI: Expand t... |
377 |
arch_restore_msi_irqs(dev, dev->irq); |
392ee1e6d [PATCH] msi: Safe... |
378 379 |
pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &control); |
f2440d9ac PCI MSI: Refactor... |
380 |
msi_mask_irq(entry, msi_capable_mask(control), entry->masked); |
abad2ec98 PCI: fully restor... |
381 |
control &= ~PCI_MSI_FLAGS_QSIZE; |
1c8d7b0a5 PCI MSI: Add supp... |
382 |
control |= (entry->msi_attrib.multiple << 4) | PCI_MSI_FLAGS_ENABLE; |
41017f0ca [PATCH] PCI: MSI(... |
383 |
pci_write_config_word(dev, pos + PCI_MSI_FLAGS, control); |
8fed4b652 MSI: Combine pci_... |
384 385 386 |
} static void __pci_restore_msix_state(struct pci_dev *dev) |
41017f0ca [PATCH] PCI: MSI(... |
387 |
{ |
41017f0ca [PATCH] PCI: MSI(... |
388 |
int pos; |
41017f0ca [PATCH] PCI: MSI(... |
389 |
struct msi_desc *entry; |
392ee1e6d [PATCH] msi: Safe... |
390 |
u16 control; |
41017f0ca [PATCH] PCI: MSI(... |
391 |
|
ded86d8d3 msi: Kill msi_loo... |
392 393 |
if (!dev->msix_enabled) return; |
f598282f5 PCI: Fix the NIU ... |
394 |
BUG_ON(list_empty(&dev->msi_list)); |
9cc8d5481 PCI MSI: Use list... |
395 |
entry = list_first_entry(&dev->msi_list, struct msi_desc, list); |
f598282f5 PCI: Fix the NIU ... |
396 397 |
pos = entry->msi_attrib.pos; pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control); |
ded86d8d3 msi: Kill msi_loo... |
398 |
|
41017f0ca [PATCH] PCI: MSI(... |
399 |
/* route the table */ |
ba698ad4b PCI: Add quirk fo... |
400 |
pci_intx_for_msi(dev, 0); |
f598282f5 PCI: Fix the NIU ... |
401 402 |
control |= PCI_MSIX_FLAGS_ENABLE | PCI_MSIX_FLAGS_MASKALL; pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); |
41017f0ca [PATCH] PCI: MSI(... |
403 |
|
4aa9bc955 MSI: Use a list i... |
404 |
list_for_each_entry(entry, &dev->msi_list, list) { |
76ccc2970 x86/PCI: Expand t... |
405 |
arch_restore_msi_irqs(dev, entry->irq); |
f2440d9ac PCI MSI: Refactor... |
406 |
msix_mask_irq(entry, entry->masked); |
41017f0ca [PATCH] PCI: MSI(... |
407 |
} |
41017f0ca [PATCH] PCI: MSI(... |
408 |
|
392ee1e6d [PATCH] msi: Safe... |
409 |
control &= ~PCI_MSIX_FLAGS_MASKALL; |
392ee1e6d [PATCH] msi: Safe... |
410 |
pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); |
41017f0ca [PATCH] PCI: MSI(... |
411 |
} |
8fed4b652 MSI: Combine pci_... |
412 413 414 415 416 417 |
void pci_restore_msi_state(struct pci_dev *dev) { __pci_restore_msi_state(dev); __pci_restore_msix_state(dev); } |
94688cf24 PCI: export pci_r... |
418 |
EXPORT_SYMBOL_GPL(pci_restore_msi_state); |
41017f0ca [PATCH] PCI: MSI(... |
419 |
|
da8d1c8ba PCI/sysfs: add pe... |
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 |
#define to_msi_attr(obj) container_of(obj, struct msi_attribute, attr) #define to_msi_desc(obj) container_of(obj, struct msi_desc, kobj) struct msi_attribute { struct attribute attr; ssize_t (*show)(struct msi_desc *entry, struct msi_attribute *attr, char *buf); ssize_t (*store)(struct msi_desc *entry, struct msi_attribute *attr, const char *buf, size_t count); }; static ssize_t show_msi_mode(struct msi_desc *entry, struct msi_attribute *atr, char *buf) { return sprintf(buf, "%s ", entry->msi_attrib.is_msix ? "msix" : "msi"); } static ssize_t msi_irq_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) { struct msi_attribute *attribute = to_msi_attr(attr); struct msi_desc *entry = to_msi_desc(kobj); if (!attribute->show) return -EIO; return attribute->show(entry, attribute, buf); } static const struct sysfs_ops msi_irq_sysfs_ops = { .show = msi_irq_attr_show, }; static struct msi_attribute mode_attribute = __ATTR(mode, S_IRUGO, show_msi_mode, NULL); struct attribute *msi_irq_default_attrs[] = { &mode_attribute.attr, NULL }; void msi_kobj_release(struct kobject *kobj) { struct msi_desc *entry = to_msi_desc(kobj); pci_dev_put(entry->dev); } static struct kobj_type msi_irq_ktype = { .release = msi_kobj_release, .sysfs_ops = &msi_irq_sysfs_ops, .default_attrs = msi_irq_default_attrs, }; static int populate_msi_sysfs(struct pci_dev *pdev) { struct msi_desc *entry; struct kobject *kobj; int ret; int count = 0; pdev->msi_kset = kset_create_and_add("msi_irqs", NULL, &pdev->dev.kobj); if (!pdev->msi_kset) return -ENOMEM; list_for_each_entry(entry, &pdev->msi_list, list) { kobj = &entry->kobj; kobj->kset = pdev->msi_kset; pci_dev_get(pdev); ret = kobject_init_and_add(kobj, &msi_irq_ktype, NULL, "%u", entry->irq); if (ret) goto out_unroll; count++; } return 0; out_unroll: list_for_each_entry(entry, &pdev->msi_list, list) { if (!count) break; kobject_del(&entry->kobj); kobject_put(&entry->kobj); count--; } return ret; } |
1da177e4c Linux-2.6.12-rc2 |
512 513 514 |
/** * msi_capability_init - configure device's MSI capability structure * @dev: pointer to the pci_dev data structure of MSI device function |
1c8d7b0a5 PCI MSI: Add supp... |
515 |
* @nvec: number of interrupts to allocate |
1da177e4c Linux-2.6.12-rc2 |
516 |
* |
1c8d7b0a5 PCI MSI: Add supp... |
517 518 519 520 521 522 523 |
* Setup the MSI capability structure of the device with the requested * number of interrupts. A return value of zero indicates the successful * setup of an entry with the new MSI irq. A negative return value indicates * an error, and a positive return value indicates the number of interrupts * which could have been allocated. */ static int msi_capability_init(struct pci_dev *dev, int nvec) |
1da177e4c Linux-2.6.12-rc2 |
524 525 |
{ struct msi_desc *entry; |
7fe3730de MSI: arch must co... |
526 |
int pos, ret; |
1da177e4c Linux-2.6.12-rc2 |
527 |
u16 control; |
f2440d9ac PCI MSI: Refactor... |
528 |
unsigned mask; |
1da177e4c Linux-2.6.12-rc2 |
529 |
|
500559a92 PCI MSI: Style cl... |
530 |
pos = pci_find_capability(dev, PCI_CAP_ID_MSI); |
110828c9c PCI: remove redun... |
531 |
msi_set_enable(dev, pos, 0); /* Disable MSI during set up */ |
1da177e4c Linux-2.6.12-rc2 |
532 533 |
pci_read_config_word(dev, msi_control_reg(pos), &control); /* MSI Entry Initialization */ |
379f5327a PCI MSI: msi_desc... |
534 |
entry = alloc_msi_entry(dev); |
f7feaca77 msi: Make MSI use... |
535 536 |
if (!entry) return -ENOMEM; |
1ce03373a [PATCH] genirq: m... |
537 |
|
500559a92 PCI MSI: Style cl... |
538 539 540 541 542 543 |
entry->msi_attrib.is_msix = 0; entry->msi_attrib.is_64 = is_64bit_address(control); entry->msi_attrib.entry_nr = 0; entry->msi_attrib.maskbit = is_mask_bit_support(control); entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */ entry->msi_attrib.pos = pos; |
f2440d9ac PCI MSI: Refactor... |
544 |
|
67b5db650 PCI MSI: Define P... |
545 |
entry->mask_pos = msi_mask_reg(pos, entry->msi_attrib.is_64); |
f2440d9ac PCI MSI: Refactor... |
546 547 548 549 550 |
/* All MSIs are unmasked by default, Mask them all */ if (entry->msi_attrib.maskbit) pci_read_config_dword(dev, entry->mask_pos, &entry->masked); mask = msi_capable_mask(control); msi_mask_irq(entry, mask, mask); |
0dd11f9be msi: fix the orde... |
551 |
list_add_tail(&entry->list, &dev->msi_list); |
9c8313343 MSI: Give archs t... |
552 |
|
1da177e4c Linux-2.6.12-rc2 |
553 |
/* Configure MSI capability structure */ |
1c8d7b0a5 PCI MSI: Add supp... |
554 |
ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSI); |
7fe3730de MSI: arch must co... |
555 |
if (ret) { |
7ba1930db PCI MSI: Unmask M... |
556 |
msi_mask_irq(entry, mask, ~mask); |
f56e44813 PCI MSI: Unify ms... |
557 |
free_msi_irqs(dev); |
7fe3730de MSI: arch must co... |
558 |
return ret; |
fd58e55fc [PATCH] PCI: msi ... |
559 |
} |
f7feaca77 msi: Make MSI use... |
560 |
|
da8d1c8ba PCI/sysfs: add pe... |
561 562 563 564 565 566 |
ret = populate_msi_sysfs(dev); if (ret) { msi_mask_irq(entry, mask, ~mask); free_msi_irqs(dev); return ret; } |
1da177e4c Linux-2.6.12-rc2 |
567 |
/* Set MSI enabled bits */ |
ba698ad4b PCI: Add quirk fo... |
568 |
pci_intx_for_msi(dev, 0); |
110828c9c PCI: remove redun... |
569 |
msi_set_enable(dev, pos, 1); |
b1cbf4e4d [PATCH] msi: fix ... |
570 |
dev->msi_enabled = 1; |
1da177e4c Linux-2.6.12-rc2 |
571 |
|
7fe3730de MSI: arch must co... |
572 |
dev->irq = entry->irq; |
1da177e4c Linux-2.6.12-rc2 |
573 574 |
return 0; } |
5a05a9d81 PCI MSI: MSI-X cl... |
575 576 577 |
static void __iomem *msix_map_region(struct pci_dev *dev, unsigned pos, unsigned nr_entries) { |
4302e0fb7 PCI: fix wrong me... |
578 |
resource_size_t phys_addr; |
5a05a9d81 PCI MSI: MSI-X cl... |
579 580 581 582 583 584 585 586 587 588 |
u32 table_offset; u8 bir; pci_read_config_dword(dev, msix_table_offset_reg(pos), &table_offset); bir = (u8)(table_offset & PCI_MSIX_FLAGS_BIRMASK); table_offset &= ~PCI_MSIX_FLAGS_BIRMASK; phys_addr = pci_resource_start(dev, bir) + table_offset; return ioremap_nocache(phys_addr, nr_entries * PCI_MSIX_ENTRY_SIZE); } |
d9d7070e6 PCI MSI: MSI-X cl... |
589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 |
static int msix_setup_entries(struct pci_dev *dev, unsigned pos, void __iomem *base, struct msix_entry *entries, int nvec) { struct msi_desc *entry; int i; for (i = 0; i < nvec; i++) { entry = alloc_msi_entry(dev); if (!entry) { if (!i) iounmap(base); else free_msi_irqs(dev); /* No enough memory. Don't try again */ return -ENOMEM; } entry->msi_attrib.is_msix = 1; entry->msi_attrib.is_64 = 1; entry->msi_attrib.entry_nr = entries[i].entry; entry->msi_attrib.default_irq = dev->irq; entry->msi_attrib.pos = pos; entry->mask_base = base; list_add_tail(&entry->list, &dev->msi_list); } return 0; } |
75cb34268 PCI MSI: MSI-X cl... |
619 620 621 622 623 624 625 626 627 628 629 |
static void msix_program_entries(struct pci_dev *dev, struct msix_entry *entries) { struct msi_desc *entry; int i = 0; list_for_each_entry(entry, &dev->msi_list, list) { int offset = entries[i].entry * PCI_MSIX_ENTRY_SIZE + PCI_MSIX_ENTRY_VECTOR_CTRL; entries[i].vector = entry->irq; |
dced35aeb drivers: Final ir... |
630 |
irq_set_msi_desc(entry->irq, entry); |
75cb34268 PCI MSI: MSI-X cl... |
631 632 633 634 635 |
entry->masked = readl(entry->mask_base + offset); msix_mask_irq(entry, 1); i++; } } |
1da177e4c Linux-2.6.12-rc2 |
636 637 638 |
/** * msix_capability_init - configure device's MSI-X capability * @dev: pointer to the pci_dev data structure of MSI-X device function |
8f7020d36 [PATCH] kernel-do... |
639 640 |
* @entries: pointer to an array of struct msix_entry entries * @nvec: number of @entries |
1da177e4c Linux-2.6.12-rc2 |
641 |
* |
eaae4b3a8 [PATCH] PCI: Spel... |
642 |
* Setup the MSI-X capability structure of device function with a |
1ce03373a [PATCH] genirq: m... |
643 644 |
* single MSI-X irq. A return of zero indicates the successful setup of * requested MSI-X entries with allocated irqs or non-zero for otherwise. |
1da177e4c Linux-2.6.12-rc2 |
645 646 647 648 |
**/ static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries, int nvec) { |
d9d7070e6 PCI MSI: MSI-X cl... |
649 |
int pos, ret; |
5a05a9d81 PCI MSI: MSI-X cl... |
650 |
u16 control; |
1da177e4c Linux-2.6.12-rc2 |
651 |
void __iomem *base; |
500559a92 PCI MSI: Style cl... |
652 |
pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); |
f598282f5 PCI: Fix the NIU ... |
653 654 655 656 657 |
pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control); /* Ensure MSI-X is disabled while it is set up */ control &= ~PCI_MSIX_FLAGS_ENABLE; pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); |
1da177e4c Linux-2.6.12-rc2 |
658 |
/* Request & Map MSI-X table region */ |
5a05a9d81 PCI MSI: MSI-X cl... |
659 660 |
base = msix_map_region(dev, pos, multi_msix_capable(control)); if (!base) |
1da177e4c Linux-2.6.12-rc2 |
661 |
return -ENOMEM; |
d9d7070e6 PCI MSI: MSI-X cl... |
662 663 664 |
ret = msix_setup_entries(dev, pos, base, entries, nvec); if (ret) return ret; |
9c8313343 MSI: Give archs t... |
665 666 |
ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX); |
583871d43 PCI MSI: Relocate... |
667 668 |
if (ret) goto error; |
9c8313343 MSI: Give archs t... |
669 |
|
f598282f5 PCI: Fix the NIU ... |
670 671 672 673 674 675 676 |
/* * Some devices require MSI-X to be enabled before we can touch the * MSI-X registers. We need to mask all the vectors to prevent * interrupts coming in before they're fully set up. */ control |= PCI_MSIX_FLAGS_MASKALL | PCI_MSIX_FLAGS_ENABLE; pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); |
75cb34268 PCI MSI: MSI-X cl... |
677 |
msix_program_entries(dev, entries); |
f598282f5 PCI: Fix the NIU ... |
678 |
|
da8d1c8ba PCI/sysfs: add pe... |
679 680 681 682 683 |
ret = populate_msi_sysfs(dev); if (ret) { ret = 0; goto error; } |
f598282f5 PCI: Fix the NIU ... |
684 |
/* Set MSI-X enabled bits and unmask the function */ |
ba698ad4b PCI: Add quirk fo... |
685 |
pci_intx_for_msi(dev, 0); |
b1cbf4e4d [PATCH] msi: fix ... |
686 |
dev->msix_enabled = 1; |
1da177e4c Linux-2.6.12-rc2 |
687 |
|
f598282f5 PCI: Fix the NIU ... |
688 689 |
control &= ~PCI_MSIX_FLAGS_MASKALL; pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); |
8d1810185 PCI MSI: Fix MSI-... |
690 |
|
1da177e4c Linux-2.6.12-rc2 |
691 |
return 0; |
583871d43 PCI MSI: Relocate... |
692 693 694 695 696 697 698 |
error: if (ret < 0) { /* * If we had some success, report the number of irqs * we succeeded in setting up. */ |
d9d7070e6 PCI MSI: MSI-X cl... |
699 |
struct msi_desc *entry; |
583871d43 PCI MSI: Relocate... |
700 701 702 703 704 705 706 707 708 709 710 711 712 |
int avail = 0; list_for_each_entry(entry, &dev->msi_list, list) { if (entry->irq != 0) avail++; } if (avail != 0) ret = avail; } free_msi_irqs(dev); return ret; |
1da177e4c Linux-2.6.12-rc2 |
713 714 715 |
} /** |
17bbc12ac MSI: Rename pci_m... |
716 |
* pci_msi_check_device - check whether MSI may be enabled on a device |
24334a125 MSI: Factorize co... |
717 |
* @dev: pointer to the pci_dev data structure of MSI device function |
c9953a73e MSI: Add an arch_... |
718 |
* @nvec: how many MSIs have been requested ? |
b1e2303db MSI: Expand pci_m... |
719 |
* @type: are we checking for MSI or MSI-X ? |
24334a125 MSI: Factorize co... |
720 |
* |
0306ebfa3 PCI: Improve pci_... |
721 |
* Look at global flags, the device itself, and its parent busses |
17bbc12ac MSI: Rename pci_m... |
722 723 |
* to determine if MSI/-X are supported for the device. If MSI/-X is * supported return 0, else return an error code. |
24334a125 MSI: Factorize co... |
724 |
**/ |
500559a92 PCI MSI: Style cl... |
725 |
static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type) |
24334a125 MSI: Factorize co... |
726 727 |
{ struct pci_bus *bus; |
c9953a73e MSI: Add an arch_... |
728 |
int ret; |
24334a125 MSI: Factorize co... |
729 |
|
0306ebfa3 PCI: Improve pci_... |
730 |
/* MSI must be globally enabled and supported by the device */ |
24334a125 MSI: Factorize co... |
731 732 |
if (!pci_msi_enable || !dev || dev->no_msi) return -EINVAL; |
314e77b3e MSI: Remove dev->... |
733 734 735 736 737 738 739 |
/* * You can't ask to have 0 or less MSIs configured. * a) it's stupid .. * b) the list manipulation code assumes nvec >= 1. */ if (nvec < 1) return -ERANGE; |
500559a92 PCI MSI: Style cl... |
740 741 742 |
/* * Any bridge which does NOT route MSI transactions from its * secondary bus to its primary bus must set NO_MSI flag on |
0306ebfa3 PCI: Improve pci_... |
743 744 745 746 |
* the secondary pci_bus. * We expect only arch-specific PCI host bus controller driver * or quirks for specific PCI bridges to be setting NO_MSI. */ |
24334a125 MSI: Factorize co... |
747 748 749 |
for (bus = dev->bus; bus; bus = bus->parent) if (bus->bus_flags & PCI_BUS_FLAGS_NO_MSI) return -EINVAL; |
c9953a73e MSI: Add an arch_... |
750 751 752 |
ret = arch_msi_check_device(dev, nvec, type); if (ret) return ret; |
b1e2303db MSI: Expand pci_m... |
753 754 |
if (!pci_find_capability(dev, type)) return -EINVAL; |
24334a125 MSI: Factorize co... |
755 756 757 758 |
return 0; } /** |
1c8d7b0a5 PCI MSI: Add supp... |
759 760 761 |
* pci_enable_msi_block - configure device's MSI capability structure * @dev: device to configure * @nvec: number of interrupts to configure |
1da177e4c Linux-2.6.12-rc2 |
762 |
* |
1c8d7b0a5 PCI MSI: Add supp... |
763 764 765 766 767 768 769 770 771 |
* Allocate IRQs for a device with the MSI capability. * This function returns a negative errno if an error occurs. If it * is unable to allocate the number of interrupts requested, it returns * the number of interrupts it might be able to allocate. If it successfully * allocates at least the number of interrupts requested, it returns 0 and * updates the @dev's irq member to the lowest new interrupt number; the * other interrupt numbers allocated to this device are consecutive. */ int pci_enable_msi_block(struct pci_dev *dev, unsigned int nvec) |
1da177e4c Linux-2.6.12-rc2 |
772 |
{ |
1c8d7b0a5 PCI MSI: Add supp... |
773 774 775 776 777 778 779 780 781 782 |
int status, pos, maxvec; u16 msgctl; pos = pci_find_capability(dev, PCI_CAP_ID_MSI); if (!pos) return -EINVAL; pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &msgctl); maxvec = 1 << ((msgctl & PCI_MSI_FLAGS_QMASK) >> 1); if (nvec > maxvec) return maxvec; |
1da177e4c Linux-2.6.12-rc2 |
783 |
|
1c8d7b0a5 PCI MSI: Add supp... |
784 |
status = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSI); |
c9953a73e MSI: Add an arch_... |
785 786 |
if (status) return status; |
1da177e4c Linux-2.6.12-rc2 |
787 |
|
ded86d8d3 msi: Kill msi_loo... |
788 |
WARN_ON(!!dev->msi_enabled); |
1da177e4c Linux-2.6.12-rc2 |
789 |
|
1c8d7b0a5 PCI MSI: Add supp... |
790 |
/* Check whether driver already requested MSI-X irqs */ |
b1cbf4e4d [PATCH] msi: fix ... |
791 |
if (dev->msix_enabled) { |
80ccba118 PCI: use dev_prin... |
792 793 794 |
dev_info(&dev->dev, "can't enable MSI " "(MSI-X already enabled) "); |
b1cbf4e4d [PATCH] msi: fix ... |
795 |
return -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
796 |
} |
1c8d7b0a5 PCI MSI: Add supp... |
797 798 |
status = msi_capability_init(dev, nvec); |
1da177e4c Linux-2.6.12-rc2 |
799 800 |
return status; } |
1c8d7b0a5 PCI MSI: Add supp... |
801 |
EXPORT_SYMBOL(pci_enable_msi_block); |
1da177e4c Linux-2.6.12-rc2 |
802 |
|
f2440d9ac PCI MSI: Refactor... |
803 |
void pci_msi_shutdown(struct pci_dev *dev) |
1da177e4c Linux-2.6.12-rc2 |
804 |
{ |
f2440d9ac PCI MSI: Refactor... |
805 806 807 |
struct msi_desc *desc; u32 mask; u16 ctrl; |
110828c9c PCI: remove redun... |
808 |
unsigned pos; |
1da177e4c Linux-2.6.12-rc2 |
809 |
|
128bc5fce MSI: Consolidate ... |
810 |
if (!pci_msi_enable || !dev || !dev->msi_enabled) |
ded86d8d3 msi: Kill msi_loo... |
811 |
return; |
110828c9c PCI: remove redun... |
812 813 814 815 816 |
BUG_ON(list_empty(&dev->msi_list)); desc = list_first_entry(&dev->msi_list, struct msi_desc, list); pos = desc->msi_attrib.pos; msi_set_enable(dev, pos, 0); |
ba698ad4b PCI: Add quirk fo... |
817 |
pci_intx_for_msi(dev, 1); |
b1cbf4e4d [PATCH] msi: fix ... |
818 |
dev->msi_enabled = 0; |
7bd007e48 [PATCH] genirq: m... |
819 |
|
12abb8ba8 PCI MSI: Fix rest... |
820 |
/* Return the device with MSI unmasked as initial states */ |
110828c9c PCI: remove redun... |
821 |
pci_read_config_word(dev, pos + PCI_MSI_FLAGS, &ctrl); |
f2440d9ac PCI MSI: Refactor... |
822 |
mask = msi_capable_mask(ctrl); |
12abb8ba8 PCI MSI: Fix rest... |
823 824 |
/* Keep cached state to be restored */ __msi_mask_irq(desc, mask, ~mask); |
e387b9eef MSI: Simplify BUG... |
825 826 |
/* Restore dev->irq to its default pin-assertion irq */ |
f2440d9ac PCI MSI: Refactor... |
827 |
dev->irq = desc->msi_attrib.default_irq; |
d52877c7b pci/irq: let pci_... |
828 |
} |
24d275533 PCI MSI: Replace ... |
829 |
|
500559a92 PCI MSI: Style cl... |
830 |
void pci_disable_msi(struct pci_dev *dev) |
d52877c7b pci/irq: let pci_... |
831 |
{ |
d52877c7b pci/irq: let pci_... |
832 833 834 835 |
if (!pci_msi_enable || !dev || !dev->msi_enabled) return; pci_msi_shutdown(dev); |
f56e44813 PCI MSI: Unify ms... |
836 |
free_msi_irqs(dev); |
da8d1c8ba PCI/sysfs: add pe... |
837 838 |
kset_unregister(dev->msi_kset); dev->msi_kset = NULL; |
1da177e4c Linux-2.6.12-rc2 |
839 |
} |
4cc086fa5 MSI: Move EXPORT_... |
840 |
EXPORT_SYMBOL(pci_disable_msi); |
1da177e4c Linux-2.6.12-rc2 |
841 |
|
1da177e4c Linux-2.6.12-rc2 |
842 |
/** |
a52e2e351 PCI/MSI: Introduc... |
843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 |
* pci_msix_table_size - return the number of device's MSI-X table entries * @dev: pointer to the pci_dev data structure of MSI-X device function */ int pci_msix_table_size(struct pci_dev *dev) { int pos; u16 control; pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); if (!pos) return 0; pci_read_config_word(dev, msi_control_reg(pos), &control); return multi_msix_capable(control); } /** |
1da177e4c Linux-2.6.12-rc2 |
860 861 |
* pci_enable_msix - configure device's MSI-X capability structure * @dev: pointer to the pci_dev data structure of MSI-X device function |
70549ad9c [PATCH] PCI: clea... |
862 |
* @entries: pointer to an array of MSI-X entries |
1ce03373a [PATCH] genirq: m... |
863 |
* @nvec: number of MSI-X irqs requested for allocation by device driver |
1da177e4c Linux-2.6.12-rc2 |
864 865 |
* * Setup the MSI-X capability structure of device function with the number |
1ce03373a [PATCH] genirq: m... |
866 |
* of requested irqs upon its software driver call to request for |
1da177e4c Linux-2.6.12-rc2 |
867 868 |
* MSI-X mode enabled on its hardware device function. A return of zero * indicates the successful configuration of MSI-X capability structure |
1ce03373a [PATCH] genirq: m... |
869 |
* with new allocated MSI-X irqs. A return of < 0 indicates a failure. |
1da177e4c Linux-2.6.12-rc2 |
870 |
* Or a return of > 0 indicates that driver request is exceeding the number |
57fbf52c8 PCI MSI: let driv... |
871 872 |
* of irqs or MSI-X vectors available. Driver should use the returned value to * re-send its request. |
1da177e4c Linux-2.6.12-rc2 |
873 |
**/ |
500559a92 PCI MSI: Style cl... |
874 |
int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec) |
1da177e4c Linux-2.6.12-rc2 |
875 |
{ |
a52e2e351 PCI/MSI: Introduc... |
876 |
int status, nr_entries; |
ded86d8d3 msi: Kill msi_loo... |
877 |
int i, j; |
1da177e4c Linux-2.6.12-rc2 |
878 |
|
c9953a73e MSI: Add an arch_... |
879 |
if (!entries) |
500559a92 PCI MSI: Style cl... |
880 |
return -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
881 |
|
c9953a73e MSI: Add an arch_... |
882 883 884 |
status = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSIX); if (status) return status; |
a52e2e351 PCI/MSI: Introduc... |
885 |
nr_entries = pci_msix_table_size(dev); |
1da177e4c Linux-2.6.12-rc2 |
886 |
if (nvec > nr_entries) |
57fbf52c8 PCI MSI: let driv... |
887 |
return nr_entries; |
1da177e4c Linux-2.6.12-rc2 |
888 889 890 891 892 893 894 895 896 897 |
/* Check for any invalid entries */ for (i = 0; i < nvec; i++) { if (entries[i].entry >= nr_entries) return -EINVAL; /* invalid entry */ for (j = i + 1; j < nvec; j++) { if (entries[i].entry == entries[j].entry) return -EINVAL; /* duplicate entry */ } } |
ded86d8d3 msi: Kill msi_loo... |
898 |
WARN_ON(!!dev->msix_enabled); |
7bd007e48 [PATCH] genirq: m... |
899 |
|
1ce03373a [PATCH] genirq: m... |
900 |
/* Check whether driver already requested for MSI irq */ |
500559a92 PCI MSI: Style cl... |
901 |
if (dev->msi_enabled) { |
80ccba118 PCI: use dev_prin... |
902 903 904 |
dev_info(&dev->dev, "can't enable MSI-X " "(MSI IRQ already assigned) "); |
1da177e4c Linux-2.6.12-rc2 |
905 906 |
return -EINVAL; } |
1da177e4c Linux-2.6.12-rc2 |
907 |
status = msix_capability_init(dev, entries, nvec); |
1da177e4c Linux-2.6.12-rc2 |
908 909 |
return status; } |
4cc086fa5 MSI: Move EXPORT_... |
910 |
EXPORT_SYMBOL(pci_enable_msix); |
1da177e4c Linux-2.6.12-rc2 |
911 |
|
500559a92 PCI MSI: Style cl... |
912 |
void pci_msix_shutdown(struct pci_dev *dev) |
fc4afc7b2 MSI: Consolidate ... |
913 |
{ |
12abb8ba8 PCI MSI: Fix rest... |
914 |
struct msi_desc *entry; |
128bc5fce MSI: Consolidate ... |
915 |
if (!pci_msi_enable || !dev || !dev->msix_enabled) |
ded86d8d3 msi: Kill msi_loo... |
916 |
return; |
12abb8ba8 PCI MSI: Fix rest... |
917 918 919 920 921 |
/* Return the device with MSI-X masked as initial states */ list_for_each_entry(entry, &dev->msi_list, list) { /* Keep cached states to be restored */ __msix_mask_irq(entry, 1); } |
b1cbf4e4d [PATCH] msi: fix ... |
922 |
msix_set_enable(dev, 0); |
ba698ad4b PCI: Add quirk fo... |
923 |
pci_intx_for_msi(dev, 1); |
b1cbf4e4d [PATCH] msi: fix ... |
924 |
dev->msix_enabled = 0; |
d52877c7b pci/irq: let pci_... |
925 |
} |
c901851fd PCI MSI: Remove a... |
926 |
|
500559a92 PCI MSI: Style cl... |
927 |
void pci_disable_msix(struct pci_dev *dev) |
d52877c7b pci/irq: let pci_... |
928 929 930 931 932 |
{ if (!pci_msi_enable || !dev || !dev->msix_enabled) return; pci_msix_shutdown(dev); |
f56e44813 PCI MSI: Unify ms... |
933 |
free_msi_irqs(dev); |
da8d1c8ba PCI/sysfs: add pe... |
934 935 |
kset_unregister(dev->msi_kset); dev->msi_kset = NULL; |
1da177e4c Linux-2.6.12-rc2 |
936 |
} |
4cc086fa5 MSI: Move EXPORT_... |
937 |
EXPORT_SYMBOL(pci_disable_msix); |
1da177e4c Linux-2.6.12-rc2 |
938 939 |
/** |
1ce03373a [PATCH] genirq: m... |
940 |
* msi_remove_pci_irq_vectors - reclaim MSI(X) irqs to unused state |
1da177e4c Linux-2.6.12-rc2 |
941 942 |
* @dev: pointer to the pci_dev data structure of MSI(X) device function * |
eaae4b3a8 [PATCH] PCI: Spel... |
943 |
* Being called during hotplug remove, from which the device function |
1ce03373a [PATCH] genirq: m... |
944 |
* is hot-removed. All previous assigned MSI/MSI-X irqs, if |
1da177e4c Linux-2.6.12-rc2 |
945 946 947 |
* allocated for this device function, are reclaimed to unused state, * which may be used later on. **/ |
500559a92 PCI MSI: Style cl... |
948 |
void msi_remove_pci_irq_vectors(struct pci_dev *dev) |
1da177e4c Linux-2.6.12-rc2 |
949 |
{ |
1da177e4c Linux-2.6.12-rc2 |
950 |
if (!pci_msi_enable || !dev) |
500559a92 PCI MSI: Style cl... |
951 |
return; |
1da177e4c Linux-2.6.12-rc2 |
952 |
|
f56e44813 PCI MSI: Unify ms... |
953 954 |
if (dev->msi_enabled || dev->msix_enabled) free_msi_irqs(dev); |
1da177e4c Linux-2.6.12-rc2 |
955 |
} |
309e57df7 [PATCH] PCI: Prov... |
956 957 958 959 |
void pci_no_msi(void) { pci_msi_enable = 0; } |
c9953a73e MSI: Add an arch_... |
960 |
|
07ae95f98 ACPI/PCI: PCI MSI... |
961 962 963 964 965 966 967 |
/** * pci_msi_enabled - is MSI enabled? * * Returns true if MSI has not been disabled by the command-line option * pci=nomsi. **/ int pci_msi_enabled(void) |
d389fec6a ACPI/PCI: Set sup... |
968 |
{ |
07ae95f98 ACPI/PCI: PCI MSI... |
969 |
return pci_msi_enable; |
d389fec6a ACPI/PCI: Set sup... |
970 |
} |
07ae95f98 ACPI/PCI: PCI MSI... |
971 |
EXPORT_SYMBOL(pci_msi_enabled); |
d389fec6a ACPI/PCI: Set sup... |
972 |
|
07ae95f98 ACPI/PCI: PCI MSI... |
973 |
void pci_msi_init_pci_dev(struct pci_dev *dev) |
d389fec6a ACPI/PCI: Set sup... |
974 |
{ |
d5dea7d95 PCI: msi: Disable... |
975 |
int pos; |
07ae95f98 ACPI/PCI: PCI MSI... |
976 |
INIT_LIST_HEAD(&dev->msi_list); |
d5dea7d95 PCI: msi: Disable... |
977 978 979 980 981 982 983 984 985 |
/* Disable the msi hardware to avoid screaming interrupts * during boot. This is the power on reset default so * usually this should be a noop. */ pos = pci_find_capability(dev, PCI_CAP_ID_MSI); if (pos) msi_set_enable(dev, pos, 0); msix_set_enable(dev, 0); |
d389fec6a ACPI/PCI: Set sup... |
986 |
} |