PCI: Fixes capability detection
[akaros.git] / kern / arch / x86 / msi.c
1 /* 
2  * This file is part of the UCB release of Plan 9. It is subject to the license
3  * terms in the LICENSE file found in the top-level directory of this
4  * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
5  * part of the UCB release of Plan 9, including this file, may be copied,
6  * modified, propagated, or distributed except according to the terms contained
7  * in the LICENSE file.
8  */
9
10 #include <vfs.h>
11 #include <kfs.h>
12 #include <slab.h>
13 #include <kmalloc.h>
14 #include <kref.h>
15 #include <string.h>
16 #include <stdio.h>
17 #include <assert.h>
18 #include <error.h>
19 #include <cpio.h>
20 #include <pmap.h>
21 #include <smp.h>
22 #include <ip.h>
23
24 enum {
25         Dpcicap         = 1<<0,
26         Dmsicap         = 1<<1,
27         Dvec            = 1<<2,
28         Debug           = 0,
29 };
30
31 enum {
32         /* address */
33         Msiabase                = 0xfee00000u,
34         Msiadest                = 1<<12,                /* same as 63:56 of apic vector */
35         Msiaedest       = 1<<4,         /* same as 55:48 of apic vector */
36         Msialowpri      = 1<<3,         /* redirection hint */
37         Msialogical     = 1<<2,
38
39         /* data */
40         Msidlevel       = 1<<15,
41         Msidassert      = 1<<14,
42         Msidlogical     = 1<<11,
43         Msidmode        = 1<<8,         /* 3 bits; delivery mode */
44         Msidvector      = 0xff<<0,
45 };
46
47 enum{
48         /* msi capabilities */
49         Vmask           = 1<<8, /* Vectors can be masked. Optional. */
50         Cap64           = 1<<7, /* 64-bit addresses. Optional. */
51         Mmesgmsk        = 7<<4, /* Mask for # of messages allowed. See 6.8.1.3 */
52         Mmcap           = 7<<1, /* # of messages the function can support. */
53         Msienable       = 1<<0, /* Enable. */
54         /* msix capabilities */
55         Msixenable      = 1<<15,
56         Msixmask        = 1<<15,
57         Msixsize        = 0x3ff,
58 };
59
60 /* Find the offset in config space of this function of the msi capability.
61  * It is defined in 6.8.1 and is variable-sized.  Returns 0 on failure. */
62 static int
63 msicap(struct pci_device *p)
64 {
65         return p->caps[PCI_CAP_ID_MSI];
66 }
67
68 /* Find the offset in config space of this function of the msi-x capability.
69  * It is defined in 6.8.1 and is variable-sized.
70  */
71 static int
72 msixcap(struct pci_device *p)
73 {
74         return p->caps[PCI_CAP_ID_MSIX];
75 }
76
77 static int
78 blacklist(struct pci_device *p)
79 {
80         switch(p->ven_id<<16 | p->dev_id){
81         case 0x11ab<<16 | 0x6485:
82         case 0x8086<<16 | 0x100f:
83                 return -1;
84         }
85         return 0;
86 }
87
88 /* see section 6.8.1 of the pci spec. */
89 /* Set up a single function on a single device.
90  * We need to take the vec, bust it up into bits,
91  * and put parts of it in the msi address and parts
92  * in the msi data.
93  */
94 int pci_msi_enable(struct pci_device *p, uint64_t vec)
95 {
96         char *s;
97         unsigned int c, f, d, datao, lopri, dmode, logical;
98
99         /* Get the offset of the MSI capability
100          * in the function's config space.
101          */
102
103         c = msicap(p);
104         if(c == 0)
105                 return -1;
106
107         /* read it, clear out the Mmesgmsk bits. 
108          * This means that there will be no multiple
109          * messages enabled.
110          */
111         f = pcidev_read16(p, c + 2) & ~Mmesgmsk;
112
113         /* See if it's a broken device, currently
114          * there's only Marvell there.
115          */
116         if(blacklist(p) != 0)
117                 return -1;
118
119         /* Data begins at 8 bytes in. */
120         datao = 8;
121
122         /* The data we write is 16 bits, scarfed
123          * in the upper 16 bits of d.
124          */
125         d = vec>>48;
126
127         /* Hard to see it being anything but lopri but ... */
128         lopri = (vec & 0x700) == MTlp;
129
130         logical = (vec & Lm) != 0;
131
132         /* OK, Msiabase is fee00000, and we offset with the
133          * dest from above, lowpri, and logical.
134          */
135         printd("Write to %d %08lx \n",c + 4, Msiabase | Msiaedest * d
136                 | Msialowpri * lopri | Msialogical * logical);
137         pcidev_write32(p, c + 4, Msiabase | Msiaedest * d
138                 | Msialowpri * lopri | Msialogical * logical);
139
140         /* And even if it's 64-bit capable, we do nothing with
141          * the high order bits. If it is 64-bit we need to offset
142          * datao (data offset) by 4 (i.e. another 32 bits)
143          */
144         if(f & Cap64){
145                 datao += 4;
146                 pcidev_write32(p, c + 8, 0);
147         }
148
149         /* pick up the delivery mode from the vector */
150         dmode = (vec >> 8) & 7;
151
152         /* the data we write to that location is a combination
153          * of things. It's not yet clear if this is a plan 9 chosen
154          * thing or a PCI spec chosen thing.
155          */
156         printd("Write data %d %04x\n", c + datao, Msidassert | Msidlogical * logical
157                        | Msidmode * dmode | ((unsigned int)vec & 0xff));
158         pcidev_write16(p, c + datao, Msidassert | Msidlogical * logical
159                        | Msidmode * dmode | ((unsigned int)vec & 0xff));
160
161         /* If we have the option of masking the vectors,
162          * blow all the masks to 0. It's a 32-bit mask.
163          */
164         if(f & Vmask)
165                 pcidev_write32(p, c + datao + 4, 0);
166
167         /* Now write the control bits back, with the
168          * Mmesg mask (which is a power of 2) set to 0
169          * (meaning one message only).
170          */
171         printd("write @ %d %04lx\n",c + 2, f);
172         pcidev_write16(p, c + 2, f);
173         return 0;
174 }
175
176 /* see section 6.8.1 of the pci spec. */
177 /* Set up a single function on a single device.
178  * We need to take the vec, bust it up into bits,
179  * and put parts of it in the msi address and parts
180  * in the msi data.
181  */
182 static int pci_msix_init(struct pci_device *p)
183 {
184         char *s;
185         unsigned int c, d, datao, lopri, dmode, logical;
186         uint16_t f;
187         int bars[2], found;
188
189         /* Get the offset of the MSI capability
190          * in the function's config space.
191          */
192         c = msixcap(p);
193         if(c == 0)
194                 return -1;
195
196         /* for this to work, we need at least one free BAR. */
197         found = pci_find_unused_bars(p, bars, 1);
198
199         /* we'll use one for now. */
200         if (found < 1)
201                 return -1;
202
203         f = pcidev_read16(p, c + 2);
204         printd("msix control %04x\n", f);
205         if (!(f & Msixenable)){
206                 printk("msix not enabled, f is 0x%x; done\n", f);
207                 return -1;
208         }
209
210         /* See if it's a broken device.
211          */
212         if(blacklist(p) != 0)
213                 return -1;
214
215         /* alloc 8 physically contiguous pages. */
216         p->msix = get_cont_pages(8, KMALLOC_WAIT);
217         if (! p->msix)
218                 return -1;
219         /* init them. */
220         memset(p->msix, 0, 8*PAGE_SIZE);
221         /* point the bar you found to them. */
222         p->msixbar = found;
223         p->msixsize = f & Msixsize;
224         /* what do we do for 64-bit bars? Who knows? */
225         /* there's an issue here. Does it need to be 8k aligned? Hmm. */
226         pcidev_write32(p, 0x10 + p->msixbar, paddr_low32(p->msix));
227         /* set the caps to point to the bar. */
228         /* Format is offset from the msibar | which bar it is. */
229         /* table is at offset 0. */
230         pcidev_write32(p, c + 4, found);
231         /* PBA is at offset 4096 */
232         pcidev_write32(p, c + 8, found | 4*PAGE_SIZE);
233         /* that seems to be it for the config space. */
234         return 0;
235 }
236
237 struct msixentry {
238         uint32_t addrlo, addrhi, data, vector;
239 };
240
241 int pci_msix_enable(struct pci_device *p, uint64_t vec)
242 {
243         int i;
244         struct msixentry *entry;
245         unsigned int c, d, datao, lopri, dmode, logical;
246         /* we don't call this much, so we don't mind
247          * retrying it.
248          */
249         if (! p->msixready) {
250                 if (pci_msix_init(p) < 0)
251                         return -1;
252                 p->msixready = 1;
253         }
254
255         /* find an unused slot. */
256         for(i = 0, entry = p->msix; i < p->msixsize; i++, entry++)
257                 if (! entry->vector)
258                         break;
259         if (i == p->msixsize)
260                 return -1;
261
262         /* The data we write is 16 bits, scarfed
263          * in the upper 16 bits of d.
264          */
265         /* The data we write is 16 bits, scarfed
266          * in the upper 16 bits of d.
267          */
268         d = vec>>48;
269
270         entry->data = d;
271
272         /* Hard to see it being anything but lopri but ... */
273         lopri = (vec & 0x700) == MTlp;
274
275         logical = (vec & Lm) != 0;
276
277         /* OK, Msiabase is fee00000, and we offset with the
278          * dest from above, lowpri, and logical.
279          */
280         printd("Write to %d %08lx \n",c + 4, Msiabase | Msiaedest * d
281                 | Msialowpri * lopri | Msialogical * logical);
282         entry->addrlo = Msiabase | Msiaedest * d
283                 | Msialowpri * lopri | Msialogical * logical;
284         
285         /* And even if it's 64-bit capable, we do nothing with
286          * the high order bits. If it is 64-bit we need to offset
287          * datao (data offset) by 4 (i.e. another 32 bits)
288          */
289         entry->addrhi = 0;
290
291         /* pick up the delivery mode from the vector */
292         dmode = (vec >> 8) & 7;
293
294         /* the data we write to that location is a combination
295          * of things. It's not yet clear if this is a plan 9 chosen
296          * thing or a PCI spec chosen thing.
297          */
298         printd("Write data @%p %04x\n", &entry->data, Msidassert | Msidlogical * logical
299                        | Msidmode * dmode | ((unsigned int)vec & 0xff));
300         entry->data = Msidassert | Msidlogical * logical
301                 | Msidmode * dmode | ((unsigned int)vec & 0xff);
302         return 0;
303 }
304 /* Mask the msi function. Since 'masking' means disable it,
305  * but the parameter has a 1 for disabling it, well, it's a
306  * bit clear operation.
307  */
308 int
309 pcimsimask(struct pci_device *p, int mask)
310 {
311         unsigned int c, f;
312
313         c = msicap(p);
314         if(c == 0)
315                 return -1;
316         f = pcidev_read16(p, c + 2);
317         if(mask){
318                 pcidev_write16(p, c + 2, f & ~Msienable);
319         }else{
320                 pcidev_write16(p, c + 2, f | Msienable);
321         }
322         return 0;
323 }
324
325 void msi_mask_irq(struct irq_handler *irq_h, int apic_vector)
326 {
327         struct pci_device *p = (struct pci_device*)irq_h->dev_private;
328         unsigned int c, f;
329         c = msicap(p);
330         if (!c)
331                 return;
332
333         f = pcidev_read16(p, c + 2);
334         pcidev_write16(p, c + 2, f & ~Msienable);
335 }
336
337 void msi_unmask_irq(struct irq_handler *irq_h, int apic_vector)
338 {
339         struct pci_device *p = (struct pci_device*)irq_h->dev_private;
340         unsigned int c, f;
341         c = msicap(p);
342         assert(c);
343
344         f = pcidev_read16(p, c + 2);
345         pcidev_write16(p, c + 2, f | Msienable);
346 }
347
348 int msi_route_irq(struct irq_handler *irq_h, int apic_vector, int dest)
349 {
350         struct pci_device *p = (struct pci_device*)irq_h->dev_private;
351         unsigned int c, f;
352         c = msicap(p);
353         assert(c);
354
355         /* TODO */
356         printk("Not routing MSI yet, fix me!\n");
357         return -1;
358 }