aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/pci/Kconfig9
-rw-r--r--drivers/pci/Makefile1
-rw-r--r--drivers/pci/htirq.c189
3 files changed, 199 insertions, 0 deletions
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index c27e782e6df9..0af6d7288415 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -52,3 +52,12 @@ config PCI_DEBUG
52 52
53 When in doubt, say N. 53 When in doubt, say N.
54 54
55config HT_IRQ
56 bool "Interrupts on hypertransport devices"
57 default y
58 depends on PCI_MSI
59 depends on X86_LOCAL_APIC && X86_IO_APIC
60 help
61 This allows native hypertransport devices to use interrupts.
62
63 If unsure say Y.
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile
index 983d0f86aa33..2752c57ecf01 100644
--- a/drivers/pci/Makefile
+++ b/drivers/pci/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_PPC32) += setup-irq.o
26obj-$(CONFIG_PPC64) += setup-bus.o 26obj-$(CONFIG_PPC64) += setup-bus.o
27obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o 27obj-$(CONFIG_MIPS) += setup-bus.o setup-irq.o
28obj-$(CONFIG_X86_VISWS) += setup-irq.o 28obj-$(CONFIG_X86_VISWS) += setup-irq.o
29obj-$(CONFIG_HT_IRQ) += htirq.o
29 30
30msiobj-y := msi.o 31msiobj-y := msi.o
31msiobj-$(CONFIG_IA64) += msi-apic.o 32msiobj-$(CONFIG_IA64) += msi-apic.o
diff --git a/drivers/pci/htirq.c b/drivers/pci/htirq.c
new file mode 100644
index 000000000000..4ba46359d367
--- /dev/null
+++ b/drivers/pci/htirq.c
@@ -0,0 +1,189 @@
1/*
2 * File: htirq.c
3 * Purpose: Hypertransport Interrupt Capability
4 *
5 * Copyright (C) 2006 Linux Networx
6 * Copyright (C) Eric Biederman <ebiederman@lnxi.com>
7 */
8
9#include <linux/irq.h>
10#include <linux/pci.h>
11#include <linux/spinlock.h>
12#include <linux/slab.h>
13#include <linux/gfp.h>
14
15/* Global ht irq lock.
16 *
17 * This is needed to serialize access to the data port in hypertransport
18 * irq capability.
19 *
20 * With multiple simultaneous hypertransport irq devices it might pay
21 * to make this more fine grained. But start with simple, stupid, and correct.
22 */
23static DEFINE_SPINLOCK(ht_irq_lock);
24
25struct ht_irq_cfg {
26 struct pci_dev *dev;
27 unsigned pos;
28 unsigned idx;
29};
30
31void write_ht_irq_low(unsigned int irq, u32 data)
32{
33 struct ht_irq_cfg *cfg = get_irq_data(irq);
34 unsigned long flags;
35 spin_lock_irqsave(&ht_irq_lock, flags);
36 pci_write_config_byte(cfg->dev, cfg->pos + 2, cfg->idx);
37 pci_write_config_dword(cfg->dev, cfg->pos + 4, data);
38 spin_unlock_irqrestore(&ht_irq_lock, flags);
39}
40
41void write_ht_irq_high(unsigned int irq, u32 data)
42{
43 struct ht_irq_cfg *cfg = get_irq_data(irq);
44 unsigned long flags;
45 spin_lock_irqsave(&ht_irq_lock, flags);
46 pci_write_config_byte(cfg->dev, cfg->pos + 2, cfg->idx + 1);
47 pci_write_config_dword(cfg->dev, cfg->pos + 4, data);
48 spin_unlock_irqrestore(&ht_irq_lock, flags);
49}
50
51u32 read_ht_irq_low(unsigned int irq)
52{
53 struct ht_irq_cfg *cfg = get_irq_data(irq);
54 unsigned long flags;
55 u32 data;
56 spin_lock_irqsave(&ht_irq_lock, flags);
57 pci_write_config_byte(cfg->dev, cfg->pos + 2, cfg->idx);
58 pci_read_config_dword(cfg->dev, cfg->pos + 4, &data);
59 spin_unlock_irqrestore(&ht_irq_lock, flags);
60 return data;
61}
62
63u32 read_ht_irq_high(unsigned int irq)
64{
65 struct ht_irq_cfg *cfg = get_irq_data(irq);
66 unsigned long flags;
67 u32 data;
68 spin_lock_irqsave(&ht_irq_lock, flags);
69 pci_write_config_byte(cfg->dev, cfg->pos + 2, cfg->idx + 1);
70 pci_read_config_dword(cfg->dev, cfg->pos + 4, &data);
71 spin_unlock_irqrestore(&ht_irq_lock, flags);
72 return data;
73}
74
75void mask_ht_irq(unsigned int irq)
76{
77 struct ht_irq_cfg *cfg;
78 unsigned long flags;
79 u32 data;
80
81 cfg = get_irq_data(irq);
82
83 spin_lock_irqsave(&ht_irq_lock, flags);
84 pci_write_config_byte(cfg->dev, cfg->pos + 2, cfg->idx);
85 pci_read_config_dword(cfg->dev, cfg->pos + 4, &data);
86 data |= 1;
87 pci_write_config_dword(cfg->dev, cfg->pos + 4, data);
88 spin_unlock_irqrestore(&ht_irq_lock, flags);
89}
90
91void unmask_ht_irq(unsigned int irq)
92{
93 struct ht_irq_cfg *cfg;
94 unsigned long flags;
95 u32 data;
96
97 cfg = get_irq_data(irq);
98
99 spin_lock_irqsave(&ht_irq_lock, flags);
100 pci_write_config_byte(cfg->dev, cfg->pos + 2, cfg->idx);
101 pci_read_config_dword(cfg->dev, cfg->pos + 4, &data);
102 data &= ~1;
103 pci_write_config_dword(cfg->dev, cfg->pos + 4, data);
104 spin_unlock_irqrestore(&ht_irq_lock, flags);
105}
106
107/**
108 * ht_create_irq - create an irq and attach it to a device.
109 * @dev: The hypertransport device to find the irq capability on.
110 * @idx: Which of the possible irqs to attach to.
111 *
112 * ht_create_irq is needs to be called for all hypertransport devices
113 * that generate irqs.
114 *
115 * The irq number of the new irq or a negative error value is returned.
116 */
117int ht_create_irq(struct pci_dev *dev, int idx)
118{
119 struct ht_irq_cfg *cfg;
120 unsigned long flags;
121 u32 data;
122 int max_irq;
123 int pos;
124 int irq;
125
126 pos = pci_find_capability(dev, PCI_CAP_ID_HT);
127 while (pos) {
128 u8 subtype;
129 pci_read_config_byte(dev, pos + 3, &subtype);
130 if (subtype == HT_CAPTYPE_IRQ)
131 break;
132 pos = pci_find_next_capability(dev, pos, PCI_CAP_ID_HT);
133 }
134 if (!pos)
135 return -EINVAL;
136
137 /* Verify the idx I want to use is in range */
138 spin_lock_irqsave(&ht_irq_lock, flags);
139 pci_write_config_byte(dev, pos + 2, 1);
140 pci_read_config_dword(dev, pos + 4, &data);
141 spin_unlock_irqrestore(&ht_irq_lock, flags);
142
143 max_irq = (data >> 16) & 0xff;
144 if ( idx > max_irq)
145 return -EINVAL;
146
147 cfg = kmalloc(sizeof(*cfg), GFP_KERNEL);
148 if (!cfg)
149 return -ENOMEM;
150
151 cfg->dev = dev;
152 cfg->pos = pos;
153 cfg->idx = 0x10 + (idx * 2);
154
155 irq = create_irq();
156 if (irq < 0) {
157 kfree(cfg);
158 return -EBUSY;
159 }
160 set_irq_data(irq, cfg);
161
162 if (arch_setup_ht_irq(irq, dev) < 0) {
163 ht_destroy_irq(irq);
164 return -EBUSY;
165 }
166
167 return irq;
168}
169
170/**
171 * ht_destroy_irq - destroy an irq created with ht_create_irq
172 *
173 * This reverses ht_create_irq removing the specified irq from
174 * existence. The irq should be free before this happens.
175 */
176void ht_destroy_irq(unsigned int irq)
177{
178 struct ht_irq_cfg *cfg;
179
180 cfg = get_irq_data(irq);
181 set_irq_chip(irq, NULL);
182 set_irq_data(irq, NULL);
183 destroy_irq(irq);
184
185 kfree(cfg);
186}
187
188EXPORT_SYMBOL(ht_create_irq);
189EXPORT_SYMBOL(ht_destroy_irq);