c08d9cec97355080cd3cf604c73d27fd7e20a683
[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 an arbitrary capability. This should move to pci.c? */
61 int pci_cap(struct pci_device *p, int cap)
62 {
63         int i, c, off;
64
65         /* status register bit 4 has capabilities */
66         if((pcidev_read16(p, PciPSR) & 1<<4) == 0)
67                 return -1;
68         switch(pcidev_read8(p, PciHDT) & 0x7f){
69         default:
70                 return -1;
71         case 0:                         /* etc */
72         case 1:                         /* pci to pci bridge */
73                 off = 0x34;
74                 break;
75         case 2:                         /* cardbus bridge */
76                 off = 0x14;
77                 break;
78         }
79         for(i = 48; i--;){
80                 off = pcidev_read8(p, off);
81                 if(off < 0x40 || (off & 3))
82                         break;
83                 off &= ~3;
84                 c = pcidev_read8(p, off);
85                 if(c == 0xff)
86                         break;
87                 if(c == cap)
88                         return off;
89                 off++;
90         }
91         return -1;
92 }
93
94 /* Find the offset in config space of this function of the msi capability.
95  * It is defined in 6.8.1 and is variable-sized.
96  */
97 static int
98 msicap(struct pci_device *p)
99 {
100         int c;
101
102         c = pci_cap(p, PciCapMSI);
103         if(c == -1)
104                 return 0;
105         return c;
106 }
107
108 /* Find the offset in config space of this function of the msi-x capability.
109  * It is defined in 6.8.1 and is variable-sized.
110  */
111 static int
112 msixcap(struct pci_device *p)
113 {
114         int c;
115
116         c = pci_cap(p, PciCapMSIX);
117         if(c == -1)
118                 return 0;
119         return c;
120 }
121
122 static int
123 blacklist(struct pci_device *p)
124 {
125         switch(p->ven_id<<16 | p->dev_id){
126         case 0x11ab<<16 | 0x6485:
127         case 0x8086<<16 | 0x100f:
128                 return -1;
129         }
130         return 0;
131 }
132
133 /* see section 6.8.1 of the pci spec. */
134 /* Set up a single function on a single device.
135  * We need to take the vec, bust it up into bits,
136  * and put parts of it in the msi address and parts
137  * in the msi data.
138  */
139 int pci_msi_enable(struct pci_device *p, uint64_t vec)
140 {
141         char *s;
142         unsigned int c, f, d, datao, lopri, dmode, logical;
143
144         /* Get the offset of the MSI capability
145          * in the function's config space.
146          */
147
148         c = msicap(p);
149         if(c == 0)
150                 return -1;
151
152         /* read it, clear out the Mmesgmsk bits. 
153          * This means that there will be no multiple
154          * messages enabled.
155          */
156         f = pcidev_read16(p, c + 2) & ~Mmesgmsk;
157
158         /* See if it's a broken device, currently
159          * there's only Marvell there.
160          */
161         if(blacklist(p) != 0)
162                 return -1;
163
164         /* Data begins at 8 bytes in. */
165         datao = 8;
166
167         /* The data we write is 16 bits, scarfed
168          * in the upper 16 bits of d.
169          */
170         d = vec>>48;
171
172         /* Hard to see it being anything but lopri but ... */
173         lopri = (vec & 0x700) == MTlp;
174
175         logical = (vec & Lm) != 0;
176
177         /* OK, Msiabase is fee00000, and we offset with the
178          * dest from above, lowpri, and logical.
179          */
180         printd("Write to %d %08lx \n",c + 4, Msiabase | Msiaedest * d
181                 | Msialowpri * lopri | Msialogical * logical);
182         pcidev_write32(p, c + 4, Msiabase | Msiaedest * d
183                 | Msialowpri * lopri | Msialogical * logical);
184
185         /* And even if it's 64-bit capable, we do nothing with
186          * the high order bits. If it is 64-bit we need to offset
187          * datao (data offset) by 4 (i.e. another 32 bits)
188          */
189         if(f & Cap64){
190                 datao += 4;
191                 pcidev_write32(p, c + 8, 0);
192         }
193
194         /* pick up the delivery mode from the vector */
195         dmode = (vec >> 8) & 7;
196
197         /* the data we write to that location is a combination
198          * of things. It's not yet clear if this is a plan 9 chosen
199          * thing or a PCI spec chosen thing.
200          */
201         printd("Write data %d %04x\n", c + datao, Msidassert | Msidlogical * logical
202                        | Msidmode * dmode | ((unsigned int)vec & 0xff));
203         pcidev_write16(p, c + datao, Msidassert | Msidlogical * logical
204                        | Msidmode * dmode | ((unsigned int)vec & 0xff));
205
206         /* If we have the option of masking the vectors,
207          * blow all the masks to 0. It's a 32-bit mask.
208          */
209         if(f & Vmask)
210                 pcidev_write32(p, c + datao + 4, 0);
211
212         /* Now write the control bits back, with the
213          * Mmesg mask (which is a power of 2) set to 0
214          * (meaning one message only).
215          */
216         printd("write @ %d %04lx\n",c + 2, f);
217         pcidev_write16(p, c + 2, f);
218         return 0;
219 }
220
221 /* see section 6.8.1 of the pci spec. */
222 /* Set up a single function on a single device.
223  * We need to take the vec, bust it up into bits,
224  * and put parts of it in the msi address and parts
225  * in the msi data.
226  */
227 static int pci_msix_init(struct pci_device *p)
228 {
229         char *s;
230         unsigned int c, d, datao, lopri, dmode, logical;
231         uint16_t f;
232         int bars[2], found;
233
234         /* Get the offset of the MSI capability
235          * in the function's config space.
236          */
237         c = msixcap(p);
238         if(c == 0)
239                 return -1;
240
241         /* for this to work, we need at least one free BAR. */
242         found = pci_find_unused_bars(p, bars, 1);
243
244         /* we'll use one for now. */
245         if (found < 1)
246                 return -1;
247
248         f = pcidev_read16(p, c + 2);
249         printd("msix control %04x\n", f);
250         if (!(f & Msixenable)){
251                 printk("msix not enabled, f is 0x%x; done\n", f);
252                 return -1;
253         }
254
255         /* See if it's a broken device.
256          */
257         if(blacklist(p) != 0)
258                 return -1;
259
260         /* alloc 8 physically contiguous pages. */
261         p->msix = get_cont_pages(8, KMALLOC_WAIT);
262         if (! p->msix)
263                 return -1;
264         /* init them. */
265         memset(p->msix, 0, 8*PAGE_SIZE);
266         /* point the bar you found to them. */
267         p->msixbar = found;
268         p->msixsize = f & Msixsize;
269         /* what do we do for 64-bit bars? Who knows? */
270         /* there's an issue here. Does it need to be 8k aligned? Hmm. */
271         pcidev_write32(p, 0x10 + p->msixbar, paddr_low32(p->msix));
272         /* set the caps to point to the bar. */
273         /* Format is offset from the msibar | which bar it is. */
274         /* table is at offset 0. */
275         pcidev_write32(p, c + 4, found);
276         /* PBA is at offset 4096 */
277         pcidev_write32(p, c + 8, found | 4*PAGE_SIZE);
278         /* that seems to be it for the config space. */
279         return 0;
280 }
281
282 struct msixentry {
283         uint32_t addrlo, addrhi, data, vector;
284 };
285
286 int pci_msix_enable(struct pci_device *p, uint64_t vec)
287 {
288         int i;
289         struct msixentry *entry;
290         unsigned int c, d, datao, lopri, dmode, logical;
291         /* we don't call this much, so we don't mind
292          * retrying it.
293          */
294         if (! p->msixready) {
295                 if (pci_msix_init(p) < 0)
296                         return -1;
297                 p->msixready = 1;
298         }
299
300         /* find an unused slot. */
301         for(i = 0, entry = p->msix; i < p->msixsize; i++, entry++)
302                 if (! entry->vector)
303                         break;
304         if (i == p->msixsize)
305                 return -1;
306
307         /* The data we write is 16 bits, scarfed
308          * in the upper 16 bits of d.
309          */
310         /* The data we write is 16 bits, scarfed
311          * in the upper 16 bits of d.
312          */
313         d = vec>>48;
314
315         entry->data = d;
316
317         /* Hard to see it being anything but lopri but ... */
318         lopri = (vec & 0x700) == MTlp;
319
320         logical = (vec & Lm) != 0;
321
322         /* OK, Msiabase is fee00000, and we offset with the
323          * dest from above, lowpri, and logical.
324          */
325         printd("Write to %d %08lx \n",c + 4, Msiabase | Msiaedest * d
326                 | Msialowpri * lopri | Msialogical * logical);
327         entry->addrlo = Msiabase | Msiaedest * d
328                 | Msialowpri * lopri | Msialogical * logical;
329         
330         /* And even if it's 64-bit capable, we do nothing with
331          * the high order bits. If it is 64-bit we need to offset
332          * datao (data offset) by 4 (i.e. another 32 bits)
333          */
334         entry->addrhi = 0;
335
336         /* pick up the delivery mode from the vector */
337         dmode = (vec >> 8) & 7;
338
339         /* the data we write to that location is a combination
340          * of things. It's not yet clear if this is a plan 9 chosen
341          * thing or a PCI spec chosen thing.
342          */
343         printd("Write data @%p %04x\n", &entry->data, Msidassert | Msidlogical * logical
344                        | Msidmode * dmode | ((unsigned int)vec & 0xff));
345         entry->data = Msidassert | Msidlogical * logical
346                 | Msidmode * dmode | ((unsigned int)vec & 0xff);
347         return 0;
348 }
349 /* Mask the msi function. Since 'masking' means disable it,
350  * but the parameter has a 1 for disabling it, well, it's a
351  * bit clear operation.
352  */
353 int
354 pcimsimask(struct pci_device *p, int mask)
355 {
356         unsigned int c, f;
357
358         c = msicap(p);
359         if(c == 0)
360                 return -1;
361         f = pcidev_read16(p, c + 2);
362         if(mask){
363                 pcidev_write16(p, c + 2, f & ~Msienable);
364         }else{
365                 pcidev_write16(p, c + 2, f | Msienable);
366         }
367         return 0;
368 }
369
370 void msi_mask_irq(struct irq_handler *irq_h, int apic_vector)
371 {
372         struct pci_device *p = (struct pci_device*)irq_h->dev_private;
373         unsigned int c, f;
374         c = msicap(p);
375         if (!c)
376                 return;
377
378         f = pcidev_read16(p, c + 2);
379         pcidev_write16(p, c + 2, f & ~Msienable);
380 }
381
382 void msi_unmask_irq(struct irq_handler *irq_h, int apic_vector)
383 {
384         struct pci_device *p = (struct pci_device*)irq_h->dev_private;
385         unsigned int c, f;
386         c = msicap(p);
387         assert(c);
388
389         f = pcidev_read16(p, c + 2);
390         pcidev_write16(p, c + 2, f | Msienable);
391 }
392
393 int msi_route_irq(struct irq_handler *irq_h, int apic_vector, int dest)
394 {
395         struct pci_device *p = (struct pci_device*)irq_h->dev_private;
396         unsigned int c, f;
397         c = msicap(p);
398         assert(c);
399
400         /* TODO */
401         printk("Not routing MSI yet, fix me!\n");
402         return -1;
403 }