[PATCH] Initial generic hypertransport interrupt support
[powerpc.git] / drivers / pci / htirq.c
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  */
23 static DEFINE_SPINLOCK(ht_irq_lock);
24
25 struct ht_irq_cfg {
26         struct pci_dev *dev;
27         unsigned pos;
28         unsigned idx;
29 };
30
31 void 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
41 void 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
51 u32 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
63 u32 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
75 void 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
91 void 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  */
117 int 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  */
176 void 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
188 EXPORT_SYMBOL(ht_create_irq);
189 EXPORT_SYMBOL(ht_destroy_irq);