igbe: abort MSI for a shitty NIC
[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 static int
105 blacklist(struct pci_device *p)
106 {
107         switch(p->ven_id<<16 | p->dev_id){
108         case 0x11ab<<16 | 0x6485:
109         case 0x8086<<16 | 0x100f:
110                 return -1;
111         }
112         return 0;
113 }
114
115 /* see section 6.8.1 of the pci spec. */
116 /* Set up a single function on a single device.
117  * We need to take the vec, bust it up into bits,
118  * and put parts of it in the msi address and parts
119  * in the msi data.
120  */
121 int pci_msi_enable(struct pci_device *p, uint64_t vec)
122 {
123         char *s;
124         unsigned int c, f, d, datao, lopri, dmode, logical;
125
126         /* Get the offset of the MSI capability
127          * in the function's config space.
128          */
129         c = msicap(p);
130         if(c == 0)
131                 return -1;
132
133         /* read it, clear out the Mmesgmsk bits. 
134          * This means that there will be no multiple
135          * messages enabled.
136          */
137         f = pcidev_read16(p, c + 2) & ~Mmesgmsk;
138
139         /* See if it's a broken device, currently
140          * there's only Marvell there.
141          */
142         if(blacklist(p) != 0)
143                 return -1;
144
145         /* Data begins at 8 bytes in. */
146         datao = 8;
147
148         /* The data we write is 16 bits, scarfed
149          * in the upper 16 bits of d.
150          */
151         d = vec>>48;
152
153         /* Hard to see it being anything but lopri but ... */
154         lopri = (vec & 0x700) == MTlp;
155
156         logical = (vec & Lm) != 0;
157
158         /* OK, Msiabase is fee00000, and we offset with the
159          * dest from above, lowpri, and logical.
160          */
161         printd("Write to %d %08lx \n",c + 4, Msiabase | Msiaedest * d
162                 | Msialowpri * lopri | Msialogical * logical);
163         pcidev_write32(p, c + 4, Msiabase | Msiaedest * d
164                 | Msialowpri * lopri | Msialogical * logical);
165
166         /* And even if it's 64-bit capable, we do nothing with
167          * the high order bits. If it is 64-bit we need to offset
168          * datao (data offset) by 4 (i.e. another 32 bits)
169          */
170         if(f & Cap64){
171                 datao += 4;
172                 pcidev_write32(p, c + 8, 0);
173         }
174
175         /* pick up the delivery mode from the vector */
176         dmode = (vec >> 8) & 7;
177
178         /* the data we write to that location is a combination
179          * of things. It's not yet clear if this is a plan 9 chosen
180          * thing or a PCI spec chosen thing.
181          */
182         printd("Write data %d %04x\n", c + datao, Msidassert | Msidlogical * logical
183                        | Msidmode * dmode | ((unsigned int)vec & 0xff));
184         pcidev_write16(p, c + datao, Msidassert | Msidlogical * logical
185                        | Msidmode * dmode | ((unsigned int)vec & 0xff));
186
187         /* If we have the option of masking the vectors,
188          * blow all the masks to 0. It's a 32-bit mask.
189          */
190         if(f & Vmask)
191                 pcidev_write32(p, c + datao + 4, 0);
192
193         /* Now write the control bits back, with the
194          * Mmesg mask (which is a power of 2) set to 0
195          * (meaning one message only).
196          */
197         printd("write @ %d %04lx\n",c + 2, f);
198         pcidev_write16(p, c + 2, f);
199         return 0;
200 }
201
202 /* Mask the msi function. Since 'masking' means disable it,
203  * but the parameter has a 1 for disabling it, well, it's a
204  * bit clear operation.
205  */
206 int
207 pcimsimask(struct pci_device *p, int mask)
208 {
209         unsigned int c, f;
210
211         c = msicap(p);
212         if(c == 0)
213                 return -1;
214         f = pcidev_read16(p, c + 2);
215         if(mask){
216                 pcidev_write16(p, c + 2, f & ~Msienable);
217         }else{
218                 pcidev_write16(p, c + 2, f | Msienable);
219         }
220         return 0;
221 }
222
223 void msi_mask_irq(struct irq_handler *irq_h, int apic_vector)
224 {
225         struct pci_device *p = (struct pci_device*)irq_h->dev_private;
226         unsigned int c, f;
227         c = msicap(p);
228         if (!c)
229                 return;
230
231         f = pcidev_read16(p, c + 2);
232         pcidev_write16(p, c + 2, f & ~Msienable);
233 }
234
235 void msi_unmask_irq(struct irq_handler *irq_h, int apic_vector)
236 {
237         struct pci_device *p = (struct pci_device*)irq_h->dev_private;
238         unsigned int c, f;
239         c = msicap(p);
240         assert(c);
241
242         f = pcidev_read16(p, c + 2);
243         pcidev_write16(p, c + 2, f | Msienable);
244 }
245
246 int msi_route_irq(struct irq_handler *irq_h, int apic_vector, int dest)
247 {
248         struct pci_device *p = (struct pci_device*)irq_h->dev_private;
249         unsigned int c, f;
250         c = msicap(p);
251         assert(c);
252
253         /* TODO */
254         printk("Not routing MSI yet, fix me!\n");
255         return -1;
256 }