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