Add comments to msi code; add new pci_clr_bus_master function
[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
58 pcicap(struct pci_device *p, int cap)
59 {
60         int i, c, off;
61
62         /* status register bit 4 has capabilities */
63         if((pcidev_read16(p, PciPSR) & 1<<4) == 0)
64                 return -1;
65         switch(pcidev_read8(p, PciHDT) & 0x7f){
66         default:
67                 return -1;
68         case 0:                         /* etc */
69         case 1:                         /* pci to pci bridge */
70                 off = 0x34;
71                 break;
72         case 2:                         /* cardbus bridge */
73                 off = 0x14;
74                 break;
75         }
76         for(i = 48; i--;){
77                 off = pcidev_read8(p, off);
78                 if(off < 0x40 || (off & 3))
79                         break;
80                 off &= ~3;
81                 c = pcidev_read8(p, off);
82                 if(c == 0xff)
83                         break;
84                 if(c == cap)
85                         return off;
86                 off++;
87         }
88         return -1;
89 }
90
91 /* Find the offset in config space of this function of the msi capability.
92  * It is defined in 6.8.1 and is variable-sized.
93  */
94 static int
95 msicap(struct pci_device *p)
96 {
97         int c;
98
99         c = pcicap(p, PciCapMSI);
100         if(c == -1)
101                 return 0;
102         return c;
103 }
104
105 static int
106 blacklist(struct pci_device *p)
107 {
108         switch(p->ven_id<<16 | p->dev_id){
109         case 0x11ab<<16 | 0x6485:
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
122 pcimsienable(struct pci_device *p, uint64_t vec)
123 {
124         char *s;
125         unsigned int c, f, d, datao, lopri, dmode, logical;
126
127         /* Get the offset of the MSI capability
128          * in the function's config space.
129          */
130         c = msicap(p);
131         if(c == 0)
132                 return -1;
133
134         /* read it, clear out the Mmesgmsk bits. 
135          * This means that there will be no multiple
136          * messages enabled.
137          */
138         f = pcidev_read16(p, c + 2) & ~Mmesgmsk;
139
140         /* See if it's a broken device, currently
141          * there's only Marvell there.
142          */
143         if(blacklist(p) != 0)
144                 return -1;
145
146         /* Data begins at 8 bytes in. */
147         datao = 8;
148
149         /* The data we write is 16 bits, scarfed
150          * in the upper 16 bits of d.
151          */
152         d = vec>>48;
153
154         /* Hard to see it being anything but lopri but ... */
155         lopri = (vec & 0x700) == MTlp;
156
157         logical = (vec & Lm) != 0;
158
159         /* OK, Msiabase is fee00000, and we offset with the
160          * dest from above, lowpri, and logical.
161          */
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         pcidev_write16(p, c + datao, Msidassert | Msidlogical * logical
182                        | Msidmode * dmode | ((unsigned int)vec & 0xff));
183
184         /* If we have the option of masking the vectors,
185          * blow all the masks to 0. It's a 32-bit mask.
186          */
187         if(f & Vmask)
188                 pcidev_write32(p, c + datao + 4, 0);
189
190         /* Now write the control bits back, with the
191          * Mmesg mask (which is a power of 2) set to 0
192          * (meaning one message only).
193          */
194         pcidev_write16(p, c + 2, f);
195         return 0;
196 }
197
198 /* Mask the msi function. Since 'masking' means disable it,
199  * but the parameter has a 1 for disabling it, well, it's a
200  * bit clear operation.
201  */
202 int
203 pcimsimask(struct pci_device *p, int mask)
204 {
205         unsigned int c, f;
206
207         c = msicap(p);
208         if(c == 0)
209                 return -1;
210         f = pcidev_read16(p, c + 2);
211         if(mask){
212                 pcidev_write16(p, c + 2, f & ~Msienable);
213                 pci_clr_bus_master(p);
214         }else{
215                 pci_set_bus_master(p);
216                 pcidev_write16(p, c + 2, f | Msienable);
217         }
218         return 0;
219 }