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