e628a45fa7a0867a71146521f9500164f491fe5a
[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,
50         Cap64           = 1<<7,
51         Mmesgmsk        = 7<<4,
52         Mmcap           = 7<<1,
53         Msienable       = 1<<0,
54 };
55
56 int
57 pcicap(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 static int
91 msicap(struct pci_device *p)
92 {
93         int c;
94
95         c = pcicap(p, PciCapMSI);
96         if(c == -1)
97                 return 0;
98         return c;
99 }
100
101 static int
102 blacklist(struct pci_device *p)
103 {
104         switch(p->ven_id<<16 | p->dev_id){
105         case 0x11ab<<16 | 0x6485:
106                 return -1;
107         }
108         return 0;
109 }
110
111 int
112 pcimsienable(struct pci_device *p, uint64_t vec)
113 {
114         char *s;
115         unsigned int c, f, d, datao, lopri, dmode, logical;
116
117         c = msicap(p);
118         if(c == 0)
119                 return -1;
120
121         f = pcidev_read16(p, c + 2) & ~Mmesgmsk;
122
123         if(blacklist(p) != 0)
124                 return -1;
125         datao = 8;
126         d = vec>>48;
127         lopri = (vec & 0x700) == MTlp;
128         logical = (vec & Lm) != 0;
129         pcidev_write32(p, c + 4, Msiabase | Msiaedest * d
130                 | Msialowpri * lopri | Msialogical * logical);
131         if(f & Cap64){
132                 datao += 4;
133                 pcidev_write32(p, c + 8, 0);
134         }
135         dmode = (vec >> 8) & 7;
136         pcidev_write16(p, c + datao, Msidassert | Msidlogical * logical
137                        | Msidmode * dmode | ((unsigned int)vec & 0xff));
138         if(f & Vmask)
139                 pcidev_write32(p, c + datao + 4, 0);
140
141         pcidev_write16(p, c + 2, f);
142         return 0;
143 }
144
145 int
146 pcimsimask(struct pci_device *p, int mask)
147 {
148         unsigned int c, f;
149
150         c = msicap(p);
151         if(c == 0)
152                 return -1;
153         f = pcidev_read16(p, c + 2) & ~Msienable;
154         if(mask){
155                 pcidev_write16(p, c + 2, f & ~Msienable);
156 //              pciclrbme(p);           cheeze
157         }else{
158                 pci_set_bus_master(p);
159                 pcidev_write16(p, c + 2, f | Msienable);
160         }
161         return 0;
162 }