diff options
author | Mark Maule <maule@sgi.com> | 2006-04-10 22:17:48 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-06-21 14:59:58 -0400 |
commit | fd58e55fcf5568e51da2ed54d7acd049c3fdb184 (patch) | |
tree | 2cf41864d66b8db39f637549d4652c7664256155 /drivers/pci | |
parent | c34b4c734482dda750deb6089521f7c891b48736 (diff) |
[PATCH] PCI: msi abstractions and support for altix
Abstract portions of the MSI core for platforms that do not use standard
APIC interrupt controllers. This is implemented through a new arch-specific
msi setup routine, and a set of msi ops which can be set on a per platform
basis.
Signed-off-by: Mark Maule <maule@sgi.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/Makefile | 6 | ||||
-rw-r--r-- | drivers/pci/msi-altix.c | 18 | ||||
-rw-r--r-- | drivers/pci/msi-apic.c | 100 | ||||
-rw-r--r-- | drivers/pci/msi.c | 212 | ||||
-rw-r--r-- | drivers/pci/msi.h | 133 |
5 files changed, 325 insertions, 144 deletions
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 6707df968934..f2d152b818f0 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile | |||
@@ -26,7 +26,11 @@ obj-$(CONFIG_PPC32) += setup-irq.o | |||
26 | obj-$(CONFIG_PPC64) += setup-bus.o | 26 | obj-$(CONFIG_PPC64) += setup-bus.o |
27 | obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o | 27 | obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o |
28 | obj-$(CONFIG_X86_VISWS) += setup-irq.o | 28 | obj-$(CONFIG_X86_VISWS) += setup-irq.o |
29 | obj-$(CONFIG_PCI_MSI) += msi.o | 29 | |
30 | msiobj-y := msi.o msi-apic.o | ||
31 | msiobj-$(CONFIG_IA64_GENERIC) += msi-altix.o | ||
32 | msiobj-$(CONFIG_IA64_SGI_SN2) += msi-altix.o | ||
33 | obj-$(CONFIG_PCI_MSI) += $(msiobj-y) | ||
30 | 34 | ||
31 | # | 35 | # |
32 | # ACPI Related PCI FW Functions | 36 | # ACPI Related PCI FW Functions |
diff --git a/drivers/pci/msi-altix.c b/drivers/pci/msi-altix.c new file mode 100644 index 000000000000..9bd240602c1e --- /dev/null +++ b/drivers/pci/msi-altix.c | |||
@@ -0,0 +1,18 @@ | |||
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 | |||
9 | #include <asm/errno.h> | ||
10 | |||
11 | int | ||
12 | sn_msi_init(void) | ||
13 | { | ||
14 | /* | ||
15 | * return error until MSI is supported on altix platforms | ||
16 | */ | ||
17 | return -EINVAL; | ||
18 | } | ||
diff --git a/drivers/pci/msi-apic.c b/drivers/pci/msi-apic.c new file mode 100644 index 000000000000..0eb5fe9003a2 --- /dev/null +++ b/drivers/pci/msi-apic.c | |||
@@ -0,0 +1,100 @@ | |||
1 | /* | ||
2 | * MSI hooks for standard x86 apic | ||
3 | */ | ||
4 | |||
5 | #include <linux/pci.h> | ||
6 | #include <linux/irq.h> | ||
7 | |||
8 | #include "msi.h" | ||
9 | |||
10 | /* | ||
11 | * Shifts for APIC-based data | ||
12 | */ | ||
13 | |||
14 | #define MSI_DATA_VECTOR_SHIFT 0 | ||
15 | #define MSI_DATA_VECTOR(v) (((u8)v) << MSI_DATA_VECTOR_SHIFT) | ||
16 | |||
17 | #define MSI_DATA_DELIVERY_SHIFT 8 | ||
18 | #define MSI_DATA_DELIVERY_FIXED (0 << MSI_DATA_DELIVERY_SHIFT) | ||
19 | #define MSI_DATA_DELIVERY_LOWPRI (1 << MSI_DATA_DELIVERY_SHIFT) | ||
20 | |||
21 | #define MSI_DATA_LEVEL_SHIFT 14 | ||
22 | #define MSI_DATA_LEVEL_DEASSERT (0 << MSI_DATA_LEVEL_SHIFT) | ||
23 | #define MSI_DATA_LEVEL_ASSERT (1 << MSI_DATA_LEVEL_SHIFT) | ||
24 | |||
25 | #define MSI_DATA_TRIGGER_SHIFT 15 | ||
26 | #define MSI_DATA_TRIGGER_EDGE (0 << MSI_DATA_TRIGGER_SHIFT) | ||
27 | #define MSI_DATA_TRIGGER_LEVEL (1 << MSI_DATA_TRIGGER_SHIFT) | ||
28 | |||
29 | /* | ||
30 | * Shift/mask fields for APIC-based bus address | ||
31 | */ | ||
32 | |||
33 | #define MSI_ADDR_HEADER 0xfee00000 | ||
34 | |||
35 | #define MSI_ADDR_DESTID_MASK 0xfff0000f | ||
36 | #define MSI_ADDR_DESTID_CPU(cpu) ((cpu) << MSI_TARGET_CPU_SHIFT) | ||
37 | |||
38 | #define MSI_ADDR_DESTMODE_SHIFT 2 | ||
39 | #define MSI_ADDR_DESTMODE_PHYS (0 << MSI_ADDR_DESTMODE_SHIFT) | ||
40 | #define MSI_ADDR_DESTMODE_LOGIC (1 << MSI_ADDR_DESTMODE_SHIFT) | ||
41 | |||
42 | #define MSI_ADDR_REDIRECTION_SHIFT 3 | ||
43 | #define MSI_ADDR_REDIRECTION_CPU (0 << MSI_ADDR_REDIRECTION_SHIFT) | ||
44 | #define MSI_ADDR_REDIRECTION_LOWPRI (1 << MSI_ADDR_REDIRECTION_SHIFT) | ||
45 | |||
46 | |||
47 | static void | ||
48 | msi_target_apic(unsigned int vector, | ||
49 | unsigned int dest_cpu, | ||
50 | u32 *address_hi, /* in/out */ | ||
51 | u32 *address_lo) /* in/out */ | ||
52 | { | ||
53 | u32 addr = *address_lo; | ||
54 | |||
55 | addr &= MSI_ADDR_DESTID_MASK; | ||
56 | addr |= MSI_ADDR_DESTID_CPU(cpu_physical_id(dest_cpu)); | ||
57 | |||
58 | *address_lo = addr; | ||
59 | } | ||
60 | |||
61 | static int | ||
62 | msi_setup_apic(struct pci_dev *pdev, /* unused in generic */ | ||
63 | unsigned int vector, | ||
64 | u32 *address_hi, | ||
65 | u32 *address_lo, | ||
66 | u32 *data) | ||
67 | { | ||
68 | unsigned long dest_phys_id; | ||
69 | |||
70 | dest_phys_id = cpu_physical_id(first_cpu(cpu_online_map)); | ||
71 | |||
72 | *address_hi = 0; | ||
73 | *address_lo = MSI_ADDR_HEADER | | ||
74 | MSI_ADDR_DESTMODE_PHYS | | ||
75 | MSI_ADDR_REDIRECTION_CPU | | ||
76 | MSI_ADDR_DESTID_CPU(dest_phys_id); | ||
77 | |||
78 | *data = MSI_DATA_TRIGGER_EDGE | | ||
79 | MSI_DATA_LEVEL_ASSERT | | ||
80 | MSI_DATA_DELIVERY_FIXED | | ||
81 | MSI_DATA_VECTOR(vector); | ||
82 | |||
83 | return 0; | ||
84 | } | ||
85 | |||
86 | static void | ||
87 | msi_teardown_apic(unsigned int vector) | ||
88 | { | ||
89 | return; /* no-op */ | ||
90 | } | ||
91 | |||
92 | /* | ||
93 | * Generic ops used on most IA archs/platforms. Set with msi_register() | ||
94 | */ | ||
95 | |||
96 | struct msi_ops msi_apic_ops = { | ||
97 | .setup = msi_setup_apic, | ||
98 | .teardown = msi_teardown_apic, | ||
99 | .target = msi_target_apic, | ||
100 | }; | ||
diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 9855c4c920b8..55ff52df5fe7 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c | |||
@@ -23,8 +23,6 @@ | |||
23 | #include "pci.h" | 23 | #include "pci.h" |
24 | #include "msi.h" | 24 | #include "msi.h" |
25 | 25 | ||
26 | #define MSI_TARGET_CPU first_cpu(cpu_online_map) | ||
27 | |||
28 | static DEFINE_SPINLOCK(msi_lock); | 26 | static DEFINE_SPINLOCK(msi_lock); |
29 | static struct msi_desc* msi_desc[NR_IRQS] = { [0 ... NR_IRQS-1] = NULL }; | 27 | static struct msi_desc* msi_desc[NR_IRQS] = { [0 ... NR_IRQS-1] = NULL }; |
30 | static kmem_cache_t* msi_cachep; | 28 | static kmem_cache_t* msi_cachep; |
@@ -40,6 +38,15 @@ int vector_irq[NR_VECTORS] = { [0 ... NR_VECTORS - 1] = -1}; | |||
40 | u8 irq_vector[NR_IRQ_VECTORS] = { FIRST_DEVICE_VECTOR , 0 }; | 38 | u8 irq_vector[NR_IRQ_VECTORS] = { FIRST_DEVICE_VECTOR , 0 }; |
41 | #endif | 39 | #endif |
42 | 40 | ||
41 | static struct msi_ops *msi_ops; | ||
42 | |||
43 | int | ||
44 | msi_register(struct msi_ops *ops) | ||
45 | { | ||
46 | msi_ops = ops; | ||
47 | return 0; | ||
48 | } | ||
49 | |||
43 | static void msi_cache_ctor(void *p, kmem_cache_t *cache, unsigned long flags) | 50 | static void msi_cache_ctor(void *p, kmem_cache_t *cache, unsigned long flags) |
44 | { | 51 | { |
45 | memset(p, 0, NR_IRQS * sizeof(struct msi_desc)); | 52 | memset(p, 0, NR_IRQS * sizeof(struct msi_desc)); |
@@ -92,7 +99,7 @@ static void msi_set_mask_bit(unsigned int vector, int flag) | |||
92 | static void set_msi_affinity(unsigned int vector, cpumask_t cpu_mask) | 99 | static void set_msi_affinity(unsigned int vector, cpumask_t cpu_mask) |
93 | { | 100 | { |
94 | struct msi_desc *entry; | 101 | struct msi_desc *entry; |
95 | struct msg_address address; | 102 | u32 address_hi, address_lo; |
96 | unsigned int irq = vector; | 103 | unsigned int irq = vector; |
97 | unsigned int dest_cpu = first_cpu(cpu_mask); | 104 | unsigned int dest_cpu = first_cpu(cpu_mask); |
98 | 105 | ||
@@ -108,28 +115,36 @@ static void set_msi_affinity(unsigned int vector, cpumask_t cpu_mask) | |||
108 | if (!pos) | 115 | if (!pos) |
109 | return; | 116 | return; |
110 | 117 | ||
118 | pci_read_config_dword(entry->dev, msi_upper_address_reg(pos), | ||
119 | &address_hi); | ||
111 | pci_read_config_dword(entry->dev, msi_lower_address_reg(pos), | 120 | pci_read_config_dword(entry->dev, msi_lower_address_reg(pos), |
112 | &address.lo_address.value); | 121 | &address_lo); |
113 | address.lo_address.value &= MSI_ADDRESS_DEST_ID_MASK; | 122 | |
114 | address.lo_address.value |= (cpu_physical_id(dest_cpu) << | 123 | msi_ops->target(vector, dest_cpu, &address_hi, &address_lo); |
115 | MSI_TARGET_CPU_SHIFT); | 124 | |
116 | entry->msi_attrib.current_cpu = cpu_physical_id(dest_cpu); | 125 | pci_write_config_dword(entry->dev, msi_upper_address_reg(pos), |
126 | address_hi); | ||
117 | pci_write_config_dword(entry->dev, msi_lower_address_reg(pos), | 127 | pci_write_config_dword(entry->dev, msi_lower_address_reg(pos), |
118 | address.lo_address.value); | 128 | address_lo); |
119 | set_native_irq_info(irq, cpu_mask); | 129 | set_native_irq_info(irq, cpu_mask); |
120 | break; | 130 | break; |
121 | } | 131 | } |
122 | case PCI_CAP_ID_MSIX: | 132 | case PCI_CAP_ID_MSIX: |
123 | { | 133 | { |
124 | int offset = entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + | 134 | int offset_hi = |
125 | PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET; | 135 | entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + |
126 | 136 | PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET; | |
127 | address.lo_address.value = readl(entry->mask_base + offset); | 137 | int offset_lo = |
128 | address.lo_address.value &= MSI_ADDRESS_DEST_ID_MASK; | 138 | entry->msi_attrib.entry_nr * PCI_MSIX_ENTRY_SIZE + |
129 | address.lo_address.value |= (cpu_physical_id(dest_cpu) << | 139 | PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET; |
130 | MSI_TARGET_CPU_SHIFT); | 140 | |
131 | entry->msi_attrib.current_cpu = cpu_physical_id(dest_cpu); | 141 | address_hi = readl(entry->mask_base + offset_hi); |
132 | writel(address.lo_address.value, entry->mask_base + offset); | 142 | address_lo = readl(entry->mask_base + offset_lo); |
143 | |||
144 | msi_ops->target(vector, dest_cpu, &address_hi, &address_lo); | ||
145 | |||
146 | writel(address_hi, entry->mask_base + offset_hi); | ||
147 | writel(address_lo, entry->mask_base + offset_lo); | ||
133 | set_native_irq_info(irq, cpu_mask); | 148 | set_native_irq_info(irq, cpu_mask); |
134 | break; | 149 | break; |
135 | } | 150 | } |
@@ -251,30 +266,6 @@ static struct hw_interrupt_type msi_irq_wo_maskbit_type = { | |||
251 | .set_affinity = set_msi_affinity | 266 | .set_affinity = set_msi_affinity |
252 | }; | 267 | }; |
253 | 268 | ||
254 | static void msi_data_init(struct msg_data *msi_data, | ||
255 | unsigned int vector) | ||
256 | { | ||
257 | memset(msi_data, 0, sizeof(struct msg_data)); | ||
258 | msi_data->vector = (u8)vector; | ||
259 | msi_data->delivery_mode = MSI_DELIVERY_MODE; | ||
260 | msi_data->level = MSI_LEVEL_MODE; | ||
261 | msi_data->trigger = MSI_TRIGGER_MODE; | ||
262 | } | ||
263 | |||
264 | static void msi_address_init(struct msg_address *msi_address) | ||
265 | { | ||
266 | unsigned int dest_id; | ||
267 | unsigned long dest_phys_id = cpu_physical_id(MSI_TARGET_CPU); | ||
268 | |||
269 | memset(msi_address, 0, sizeof(struct msg_address)); | ||
270 | msi_address->hi_address = (u32)0; | ||
271 | dest_id = (MSI_ADDRESS_HEADER << MSI_ADDRESS_HEADER_SHIFT); | ||
272 | msi_address->lo_address.u.dest_mode = MSI_PHYSICAL_MODE; | ||
273 | msi_address->lo_address.u.redirection_hint = MSI_REDIRECTION_HINT_MODE; | ||
274 | msi_address->lo_address.u.dest_id = dest_id; | ||
275 | msi_address->lo_address.value |= (dest_phys_id << MSI_TARGET_CPU_SHIFT); | ||
276 | } | ||
277 | |||
278 | static int msi_free_vector(struct pci_dev* dev, int vector, int reassign); | 269 | static int msi_free_vector(struct pci_dev* dev, int vector, int reassign); |
279 | static int assign_msi_vector(void) | 270 | static int assign_msi_vector(void) |
280 | { | 271 | { |
@@ -369,13 +360,29 @@ static int msi_init(void) | |||
369 | return status; | 360 | return status; |
370 | } | 361 | } |
371 | 362 | ||
363 | status = msi_arch_init(); | ||
364 | if (status < 0) { | ||
365 | pci_msi_enable = 0; | ||
366 | printk(KERN_WARNING | ||
367 | "PCI: MSI arch init failed. MSI disabled.\n"); | ||
368 | return status; | ||
369 | } | ||
370 | |||
371 | if (! msi_ops) { | ||
372 | printk(KERN_WARNING | ||
373 | "PCI: MSI ops not registered. MSI disabled.\n"); | ||
374 | status = -EINVAL; | ||
375 | return status; | ||
376 | } | ||
377 | |||
378 | last_alloc_vector = assign_irq_vector(AUTO_ASSIGN); | ||
372 | status = msi_cache_init(); | 379 | status = msi_cache_init(); |
373 | if (status < 0) { | 380 | if (status < 0) { |
374 | pci_msi_enable = 0; | 381 | pci_msi_enable = 0; |
375 | printk(KERN_WARNING "PCI: MSI cache init failed\n"); | 382 | printk(KERN_WARNING "PCI: MSI cache init failed\n"); |
376 | return status; | 383 | return status; |
377 | } | 384 | } |
378 | last_alloc_vector = assign_irq_vector(AUTO_ASSIGN); | 385 | |
379 | if (last_alloc_vector < 0) { | 386 | if (last_alloc_vector < 0) { |
380 | pci_msi_enable = 0; | 387 | pci_msi_enable = 0; |
381 | printk(KERN_WARNING "PCI: No interrupt vectors available for MSI\n"); | 388 | printk(KERN_WARNING "PCI: No interrupt vectors available for MSI\n"); |
@@ -575,6 +582,8 @@ void pci_restore_msi_state(struct pci_dev *dev) | |||
575 | int pci_save_msix_state(struct pci_dev *dev) | 582 | int pci_save_msix_state(struct pci_dev *dev) |
576 | { | 583 | { |
577 | int pos; | 584 | int pos; |
585 | int temp; | ||
586 | int vector, head, tail = 0; | ||
578 | u16 control; | 587 | u16 control; |
579 | struct pci_cap_saved_state *save_state; | 588 | struct pci_cap_saved_state *save_state; |
580 | 589 | ||
@@ -582,6 +591,7 @@ int pci_save_msix_state(struct pci_dev *dev) | |||
582 | if (pos <= 0 || dev->no_msi) | 591 | if (pos <= 0 || dev->no_msi) |
583 | return 0; | 592 | return 0; |
584 | 593 | ||
594 | /* save the capability */ | ||
585 | pci_read_config_word(dev, msi_control_reg(pos), &control); | 595 | pci_read_config_word(dev, msi_control_reg(pos), &control); |
586 | if (!(control & PCI_MSIX_FLAGS_ENABLE)) | 596 | if (!(control & PCI_MSIX_FLAGS_ENABLE)) |
587 | return 0; | 597 | return 0; |
@@ -593,6 +603,38 @@ int pci_save_msix_state(struct pci_dev *dev) | |||
593 | } | 603 | } |
594 | *((u16 *)&save_state->data[0]) = control; | 604 | *((u16 *)&save_state->data[0]) = control; |
595 | 605 | ||
606 | /* save the table */ | ||
607 | temp = dev->irq; | ||
608 | if (msi_lookup_vector(dev, PCI_CAP_ID_MSIX)) { | ||
609 | kfree(save_state); | ||
610 | return -EINVAL; | ||
611 | } | ||
612 | |||
613 | vector = head = dev->irq; | ||
614 | while (head != tail) { | ||
615 | int j; | ||
616 | void __iomem *base; | ||
617 | struct msi_desc *entry; | ||
618 | |||
619 | entry = msi_desc[vector]; | ||
620 | base = entry->mask_base; | ||
621 | j = entry->msi_attrib.entry_nr; | ||
622 | |||
623 | entry->address_lo_save = | ||
624 | readl(base + j * PCI_MSIX_ENTRY_SIZE + | ||
625 | PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); | ||
626 | entry->address_hi_save = | ||
627 | readl(base + j * PCI_MSIX_ENTRY_SIZE + | ||
628 | PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); | ||
629 | entry->data_save = | ||
630 | readl(base + j * PCI_MSIX_ENTRY_SIZE + | ||
631 | PCI_MSIX_ENTRY_DATA_OFFSET); | ||
632 | |||
633 | tail = msi_desc[vector]->link.tail; | ||
634 | vector = tail; | ||
635 | } | ||
636 | dev->irq = temp; | ||
637 | |||
596 | disable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); | 638 | disable_msi_mode(dev, pos, PCI_CAP_ID_MSIX); |
597 | save_state->cap_nr = PCI_CAP_ID_MSIX; | 639 | save_state->cap_nr = PCI_CAP_ID_MSIX; |
598 | pci_add_saved_cap(dev, save_state); | 640 | pci_add_saved_cap(dev, save_state); |
@@ -606,8 +648,6 @@ void pci_restore_msix_state(struct pci_dev *dev) | |||
606 | int vector, head, tail = 0; | 648 | int vector, head, tail = 0; |
607 | void __iomem *base; | 649 | void __iomem *base; |
608 | int j; | 650 | int j; |
609 | struct msg_address address; | ||
610 | struct msg_data data; | ||
611 | struct msi_desc *entry; | 651 | struct msi_desc *entry; |
612 | int temp; | 652 | int temp; |
613 | struct pci_cap_saved_state *save_state; | 653 | struct pci_cap_saved_state *save_state; |
@@ -633,20 +673,13 @@ void pci_restore_msix_state(struct pci_dev *dev) | |||
633 | base = entry->mask_base; | 673 | base = entry->mask_base; |
634 | j = entry->msi_attrib.entry_nr; | 674 | j = entry->msi_attrib.entry_nr; |
635 | 675 | ||
636 | msi_address_init(&address); | 676 | writel(entry->address_lo_save, |
637 | msi_data_init(&data, vector); | ||
638 | |||
639 | address.lo_address.value &= MSI_ADDRESS_DEST_ID_MASK; | ||
640 | address.lo_address.value |= entry->msi_attrib.current_cpu << | ||
641 | MSI_TARGET_CPU_SHIFT; | ||
642 | |||
643 | writel(address.lo_address.value, | ||
644 | base + j * PCI_MSIX_ENTRY_SIZE + | 677 | base + j * PCI_MSIX_ENTRY_SIZE + |
645 | PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); | 678 | PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); |
646 | writel(address.hi_address, | 679 | writel(entry->address_hi_save, |
647 | base + j * PCI_MSIX_ENTRY_SIZE + | 680 | base + j * PCI_MSIX_ENTRY_SIZE + |
648 | PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); | 681 | PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); |
649 | writel(*(u32*)&data, | 682 | writel(entry->data_save, |
650 | base + j * PCI_MSIX_ENTRY_SIZE + | 683 | base + j * PCI_MSIX_ENTRY_SIZE + |
651 | PCI_MSIX_ENTRY_DATA_OFFSET); | 684 | PCI_MSIX_ENTRY_DATA_OFFSET); |
652 | 685 | ||
@@ -660,30 +693,32 @@ void pci_restore_msix_state(struct pci_dev *dev) | |||
660 | } | 693 | } |
661 | #endif | 694 | #endif |
662 | 695 | ||
663 | static void msi_register_init(struct pci_dev *dev, struct msi_desc *entry) | 696 | static int msi_register_init(struct pci_dev *dev, struct msi_desc *entry) |
664 | { | 697 | { |
665 | struct msg_address address; | 698 | int status; |
666 | struct msg_data data; | 699 | u32 address_hi; |
700 | u32 address_lo; | ||
701 | u32 data; | ||
667 | int pos, vector = dev->irq; | 702 | int pos, vector = dev->irq; |
668 | u16 control; | 703 | u16 control; |
669 | 704 | ||
670 | pos = pci_find_capability(dev, PCI_CAP_ID_MSI); | 705 | pos = pci_find_capability(dev, PCI_CAP_ID_MSI); |
671 | pci_read_config_word(dev, msi_control_reg(pos), &control); | 706 | pci_read_config_word(dev, msi_control_reg(pos), &control); |
707 | |||
672 | /* Configure MSI capability structure */ | 708 | /* Configure MSI capability structure */ |
673 | msi_address_init(&address); | 709 | status = msi_ops->setup(dev, vector, &address_hi, &address_lo, &data); |
674 | msi_data_init(&data, vector); | 710 | if (status < 0) |
675 | entry->msi_attrib.current_cpu = ((address.lo_address.u.dest_id >> | 711 | return status; |
676 | MSI_TARGET_CPU_SHIFT) & MSI_TARGET_CPU_MASK); | 712 | |
677 | pci_write_config_dword(dev, msi_lower_address_reg(pos), | 713 | pci_write_config_dword(dev, msi_lower_address_reg(pos), address_lo); |
678 | address.lo_address.value); | ||
679 | if (is_64bit_address(control)) { | 714 | if (is_64bit_address(control)) { |
680 | pci_write_config_dword(dev, | 715 | pci_write_config_dword(dev, |
681 | msi_upper_address_reg(pos), address.hi_address); | 716 | msi_upper_address_reg(pos), address_hi); |
682 | pci_write_config_word(dev, | 717 | pci_write_config_word(dev, |
683 | msi_data_reg(pos, 1), *((u32*)&data)); | 718 | msi_data_reg(pos, 1), data); |
684 | } else | 719 | } else |
685 | pci_write_config_word(dev, | 720 | pci_write_config_word(dev, |
686 | msi_data_reg(pos, 0), *((u32*)&data)); | 721 | msi_data_reg(pos, 0), data); |
687 | if (entry->msi_attrib.maskbit) { | 722 | if (entry->msi_attrib.maskbit) { |
688 | unsigned int maskbits, temp; | 723 | unsigned int maskbits, temp; |
689 | /* All MSIs are unmasked by default, Mask them all */ | 724 | /* All MSIs are unmasked by default, Mask them all */ |
@@ -697,6 +732,8 @@ static void msi_register_init(struct pci_dev *dev, struct msi_desc *entry) | |||
697 | msi_mask_bits_reg(pos, is_64bit_address(control)), | 732 | msi_mask_bits_reg(pos, is_64bit_address(control)), |
698 | maskbits); | 733 | maskbits); |
699 | } | 734 | } |
735 | |||
736 | return 0; | ||
700 | } | 737 | } |
701 | 738 | ||
702 | /** | 739 | /** |
@@ -710,6 +747,7 @@ static void msi_register_init(struct pci_dev *dev, struct msi_desc *entry) | |||
710 | **/ | 747 | **/ |
711 | static int msi_capability_init(struct pci_dev *dev) | 748 | static int msi_capability_init(struct pci_dev *dev) |
712 | { | 749 | { |
750 | int status; | ||
713 | struct msi_desc *entry; | 751 | struct msi_desc *entry; |
714 | int pos, vector; | 752 | int pos, vector; |
715 | u16 control; | 753 | u16 control; |
@@ -742,7 +780,12 @@ static int msi_capability_init(struct pci_dev *dev) | |||
742 | /* Replace with MSI handler */ | 780 | /* Replace with MSI handler */ |
743 | irq_handler_init(PCI_CAP_ID_MSI, vector, entry->msi_attrib.maskbit); | 781 | irq_handler_init(PCI_CAP_ID_MSI, vector, entry->msi_attrib.maskbit); |
744 | /* Configure MSI capability structure */ | 782 | /* Configure MSI capability structure */ |
745 | msi_register_init(dev, entry); | 783 | status = msi_register_init(dev, entry); |
784 | if (status != 0) { | ||
785 | dev->irq = entry->msi_attrib.default_vector; | ||
786 | kmem_cache_free(msi_cachep, entry); | ||
787 | return status; | ||
788 | } | ||
746 | 789 | ||
747 | attach_msi_entry(entry, vector); | 790 | attach_msi_entry(entry, vector); |
748 | /* Set MSI enabled bits */ | 791 | /* Set MSI enabled bits */ |
@@ -765,8 +808,10 @@ static int msix_capability_init(struct pci_dev *dev, | |||
765 | struct msix_entry *entries, int nvec) | 808 | struct msix_entry *entries, int nvec) |
766 | { | 809 | { |
767 | struct msi_desc *head = NULL, *tail = NULL, *entry = NULL; | 810 | struct msi_desc *head = NULL, *tail = NULL, *entry = NULL; |
768 | struct msg_address address; | 811 | u32 address_hi; |
769 | struct msg_data data; | 812 | u32 address_lo; |
813 | u32 data; | ||
814 | int status; | ||
770 | int vector, pos, i, j, nr_entries, temp = 0; | 815 | int vector, pos, i, j, nr_entries, temp = 0; |
771 | unsigned long phys_addr; | 816 | unsigned long phys_addr; |
772 | u32 table_offset; | 817 | u32 table_offset; |
@@ -822,18 +867,20 @@ static int msix_capability_init(struct pci_dev *dev, | |||
822 | /* Replace with MSI-X handler */ | 867 | /* Replace with MSI-X handler */ |
823 | irq_handler_init(PCI_CAP_ID_MSIX, vector, 1); | 868 | irq_handler_init(PCI_CAP_ID_MSIX, vector, 1); |
824 | /* Configure MSI-X capability structure */ | 869 | /* Configure MSI-X capability structure */ |
825 | msi_address_init(&address); | 870 | status = msi_ops->setup(dev, vector, |
826 | msi_data_init(&data, vector); | 871 | &address_hi, |
827 | entry->msi_attrib.current_cpu = | 872 | &address_lo, |
828 | ((address.lo_address.u.dest_id >> | 873 | &data); |
829 | MSI_TARGET_CPU_SHIFT) & MSI_TARGET_CPU_MASK); | 874 | if (status < 0) |
830 | writel(address.lo_address.value, | 875 | break; |
876 | |||
877 | writel(address_lo, | ||
831 | base + j * PCI_MSIX_ENTRY_SIZE + | 878 | base + j * PCI_MSIX_ENTRY_SIZE + |
832 | PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); | 879 | PCI_MSIX_ENTRY_LOWER_ADDR_OFFSET); |
833 | writel(address.hi_address, | 880 | writel(address_hi, |
834 | base + j * PCI_MSIX_ENTRY_SIZE + | 881 | base + j * PCI_MSIX_ENTRY_SIZE + |
835 | PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); | 882 | PCI_MSIX_ENTRY_UPPER_ADDR_OFFSET); |
836 | writel(*(u32*)&data, | 883 | writel(data, |
837 | base + j * PCI_MSIX_ENTRY_SIZE + | 884 | base + j * PCI_MSIX_ENTRY_SIZE + |
838 | PCI_MSIX_ENTRY_DATA_OFFSET); | 885 | PCI_MSIX_ENTRY_DATA_OFFSET); |
839 | attach_msi_entry(entry, vector); | 886 | attach_msi_entry(entry, vector); |
@@ -901,9 +948,10 @@ int pci_enable_msi(struct pci_dev* dev) | |||
901 | vector_irq[dev->irq] = -1; | 948 | vector_irq[dev->irq] = -1; |
902 | nr_released_vectors--; | 949 | nr_released_vectors--; |
903 | spin_unlock_irqrestore(&msi_lock, flags); | 950 | spin_unlock_irqrestore(&msi_lock, flags); |
904 | msi_register_init(dev, msi_desc[dev->irq]); | 951 | status = msi_register_init(dev, msi_desc[dev->irq]); |
905 | enable_msi_mode(dev, pos, PCI_CAP_ID_MSI); | 952 | if (status == 0) |
906 | return 0; | 953 | enable_msi_mode(dev, pos, PCI_CAP_ID_MSI); |
954 | return status; | ||
907 | } | 955 | } |
908 | spin_unlock_irqrestore(&msi_lock, flags); | 956 | spin_unlock_irqrestore(&msi_lock, flags); |
909 | dev->irq = temp; | 957 | dev->irq = temp; |
@@ -980,6 +1028,8 @@ static int msi_free_vector(struct pci_dev* dev, int vector, int reassign) | |||
980 | void __iomem *base; | 1028 | void __iomem *base; |
981 | unsigned long flags; | 1029 | unsigned long flags; |
982 | 1030 | ||
1031 | msi_ops->teardown(vector); | ||
1032 | |||
983 | spin_lock_irqsave(&msi_lock, flags); | 1033 | spin_lock_irqsave(&msi_lock, flags); |
984 | entry = msi_desc[vector]; | 1034 | entry = msi_desc[vector]; |
985 | if (!entry || entry->dev != dev) { | 1035 | if (!entry || entry->dev != dev) { |
diff --git a/drivers/pci/msi.h b/drivers/pci/msi.h index 4ac52d441e47..56951c39d3a3 100644 --- a/drivers/pci/msi.h +++ b/drivers/pci/msi.h | |||
@@ -6,6 +6,68 @@ | |||
6 | #ifndef MSI_H | 6 | #ifndef MSI_H |
7 | #define MSI_H | 7 | #define MSI_H |
8 | 8 | ||
9 | /* | ||
10 | * MSI operation vector. Used by the msi core code (drivers/pci/msi.c) | ||
11 | * to abstract platform-specific tasks relating to MSI address generation | ||
12 | * and resource management. | ||
13 | */ | ||
14 | struct msi_ops { | ||
15 | /** | ||
16 | * setup - generate an MSI bus address and data for a given vector | ||
17 | * @pdev: PCI device context (in) | ||
18 | * @vector: vector allocated by the msi core (in) | ||
19 | * @addr_hi: upper 32 bits of PCI bus MSI address (out) | ||
20 | * @addr_lo: lower 32 bits of PCI bus MSI address (out) | ||
21 | * @data: MSI data payload (out) | ||
22 | * | ||
23 | * Description: The setup op is used to generate a PCI bus addres and | ||
24 | * data which the msi core will program into the card MSI capability | ||
25 | * registers. The setup routine is responsible for picking an initial | ||
26 | * cpu to target the MSI at. The setup routine is responsible for | ||
27 | * examining pdev to determine the MSI capabilities of the card and | ||
28 | * generating a suitable address/data. The setup routine is | ||
29 | * responsible for allocating and tracking any system resources it | ||
30 | * needs to route the MSI to the cpu it picks, and for associating | ||
31 | * those resources with the passed in vector. | ||
32 | * | ||
33 | * Returns 0 if the MSI address/data was successfully setup. | ||
34 | **/ | ||
35 | |||
36 | int (*setup) (struct pci_dev *pdev, unsigned int vector, | ||
37 | u32 *addr_hi, u32 *addr_lo, u32 *data); | ||
38 | |||
39 | /** | ||
40 | * teardown - release resources allocated by setup | ||
41 | * @vector: vector context for resources (in) | ||
42 | * | ||
43 | * Description: The teardown op is used to release any resources | ||
44 | * that were allocated in the setup routine associated with the passed | ||
45 | * in vector. | ||
46 | **/ | ||
47 | |||
48 | void (*teardown) (unsigned int vector); | ||
49 | |||
50 | /** | ||
51 | * target - retarget an MSI at a different cpu | ||
52 | * @vector: vector context for resources (in) | ||
53 | * @cpu: new cpu to direct vector at (in) | ||
54 | * @addr_hi: new value of PCI bus upper 32 bits (in/out) | ||
55 | * @addr_lo: new value of PCI bus lower 32 bits (in/out) | ||
56 | * | ||
57 | * Description: The target op is used to redirect an MSI vector | ||
58 | * at a different cpu. addr_hi/addr_lo coming in are the existing | ||
59 | * values that the MSI core has programmed into the card. The | ||
60 | * target code is responsible for freeing any resources (if any) | ||
61 | * associated with the old address, and generating a new PCI bus | ||
62 | * addr_hi/addr_lo that will redirect the vector at the indicated cpu. | ||
63 | **/ | ||
64 | |||
65 | void (*target) (unsigned int vector, unsigned int cpu, | ||
66 | u32 *addr_hi, u32 *addr_lo); | ||
67 | }; | ||
68 | |||
69 | extern int msi_register(struct msi_ops *ops); | ||
70 | |||
9 | #include <asm/msi.h> | 71 | #include <asm/msi.h> |
10 | 72 | ||
11 | /* | 73 | /* |
@@ -63,67 +125,6 @@ extern int pci_vector_resources(int last, int nr_released); | |||
63 | #define msix_mask(address) (address | PCI_MSIX_FLAGS_BITMASK) | 125 | #define msix_mask(address) (address | PCI_MSIX_FLAGS_BITMASK) |
64 | #define msix_is_pending(address) (address & PCI_MSIX_FLAGS_PENDMASK) | 126 | #define msix_is_pending(address) (address & PCI_MSIX_FLAGS_PENDMASK) |
65 | 127 | ||
66 | /* | ||
67 | * MSI Defined Data Structures | ||
68 | */ | ||
69 | #define MSI_ADDRESS_HEADER 0xfee | ||
70 | #define MSI_ADDRESS_HEADER_SHIFT 12 | ||
71 | #define MSI_ADDRESS_HEADER_MASK 0xfff000 | ||
72 | #define MSI_ADDRESS_DEST_ID_MASK 0xfff0000f | ||
73 | #define MSI_TARGET_CPU_MASK 0xff | ||
74 | #define MSI_DELIVERY_MODE 0 | ||
75 | #define MSI_LEVEL_MODE 1 /* Edge always assert */ | ||
76 | #define MSI_TRIGGER_MODE 0 /* MSI is edge sensitive */ | ||
77 | #define MSI_PHYSICAL_MODE 0 | ||
78 | #define MSI_LOGICAL_MODE 1 | ||
79 | #define MSI_REDIRECTION_HINT_MODE 0 | ||
80 | |||
81 | struct msg_data { | ||
82 | #if defined(__LITTLE_ENDIAN_BITFIELD) | ||
83 | __u32 vector : 8; | ||
84 | __u32 delivery_mode : 3; /* 000b: FIXED | 001b: lowest prior */ | ||
85 | __u32 reserved_1 : 3; | ||
86 | __u32 level : 1; /* 0: deassert | 1: assert */ | ||
87 | __u32 trigger : 1; /* 0: edge | 1: level */ | ||
88 | __u32 reserved_2 : 16; | ||
89 | #elif defined(__BIG_ENDIAN_BITFIELD) | ||
90 | __u32 reserved_2 : 16; | ||
91 | __u32 trigger : 1; /* 0: edge | 1: level */ | ||
92 | __u32 level : 1; /* 0: deassert | 1: assert */ | ||
93 | __u32 reserved_1 : 3; | ||
94 | __u32 delivery_mode : 3; /* 000b: FIXED | 001b: lowest prior */ | ||
95 | __u32 vector : 8; | ||
96 | #else | ||
97 | #error "Bitfield endianness not defined! Check your byteorder.h" | ||
98 | #endif | ||
99 | } __attribute__ ((packed)); | ||
100 | |||
101 | struct msg_address { | ||
102 | union { | ||
103 | struct { | ||
104 | #if defined(__LITTLE_ENDIAN_BITFIELD) | ||
105 | __u32 reserved_1 : 2; | ||
106 | __u32 dest_mode : 1; /*0:physic | 1:logic */ | ||
107 | __u32 redirection_hint: 1; /*0: dedicated CPU | ||
108 | 1: lowest priority */ | ||
109 | __u32 reserved_2 : 4; | ||
110 | __u32 dest_id : 24; /* Destination ID */ | ||
111 | #elif defined(__BIG_ENDIAN_BITFIELD) | ||
112 | __u32 dest_id : 24; /* Destination ID */ | ||
113 | __u32 reserved_2 : 4; | ||
114 | __u32 redirection_hint: 1; /*0: dedicated CPU | ||
115 | 1: lowest priority */ | ||
116 | __u32 dest_mode : 1; /*0:physic | 1:logic */ | ||
117 | __u32 reserved_1 : 2; | ||
118 | #else | ||
119 | #error "Bitfield endianness not defined! Check your byteorder.h" | ||
120 | #endif | ||
121 | }u; | ||
122 | __u32 value; | ||
123 | }lo_address; | ||
124 | __u32 hi_address; | ||
125 | } __attribute__ ((packed)); | ||
126 | |||
127 | struct msi_desc { | 128 | struct msi_desc { |
128 | struct { | 129 | struct { |
129 | __u8 type : 5; /* {0: unused, 5h:MSI, 11h:MSI-X} */ | 130 | __u8 type : 5; /* {0: unused, 5h:MSI, 11h:MSI-X} */ |
@@ -132,7 +133,7 @@ struct msi_desc { | |||
132 | __u8 reserved: 1; /* reserved */ | 133 | __u8 reserved: 1; /* reserved */ |
133 | __u8 entry_nr; /* specific enabled entry */ | 134 | __u8 entry_nr; /* specific enabled entry */ |
134 | __u8 default_vector; /* default pre-assigned vector */ | 135 | __u8 default_vector; /* default pre-assigned vector */ |
135 | __u8 current_cpu; /* current destination cpu */ | 136 | __u8 unused; /* formerly unused destination cpu*/ |
136 | }msi_attrib; | 137 | }msi_attrib; |
137 | 138 | ||
138 | struct { | 139 | struct { |
@@ -142,6 +143,14 @@ struct msi_desc { | |||
142 | 143 | ||
143 | void __iomem *mask_base; | 144 | void __iomem *mask_base; |
144 | struct pci_dev *dev; | 145 | struct pci_dev *dev; |
146 | |||
147 | #ifdef CONFIG_PM | ||
148 | /* PM save area for MSIX address/data */ | ||
149 | |||
150 | u32 address_hi_save; | ||
151 | u32 address_lo_save; | ||
152 | u32 data_save; | ||
153 | #endif | ||
145 | }; | 154 | }; |
146 | 155 | ||
147 | #endif /* MSI_H */ | 156 | #endif /* MSI_H */ |