Refactors MSI code slightly
[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         /* MSI address format
33          *
34          * +31----------------------20+19----------12+11--------4+--3--+--2--+1---0+
35          * |       0xfee              | Dest APIC ID |  Reserved | RH  | DM  |  XX |
36          * +--------------------------+--------------+-----------+-----+-----+-----+
37          *
38          * RH: Redirection Hint
39          * DM: Destinatio Mode
40          * XX: Probably reserved, set to 0
41          */
42         Msiabase                = 0xfee00000u,
43         Msiadest                = 1<<12,                /* same as 63:56 of apic vector */
44         Msiaedest       = 1<<4,         /* same as 55:48 of apic vector */
45         Msialowpri      = 1<<3,         /* redirection hint */
46         Msialogical     = 1<<2,
47
48         /* MSI data format
49          * +63-------------------------------------------------------------------32+
50          * |                          Reserved                                     |
51          * +-------------------------------+-15-+-14-+--------+10----8+7----------0+
52          * |          Reserved             | TM | Lv | Reserv | Dmode |   Vector   |
53          * +-------------------------------+----+----+--------+-------+------------+
54          *
55          * Dmode: delivery mode (like APIC/LVT messages).  Usually 000 (Fixed).
56          * TM: Trigger mode (0 Edge, 1 Level)
57          * Lv: Level assert (0 Deassert, 1 Assert)
58          *
59          *
60          * for more info, check intel's SDMv3 (grep message signal) */
61         Msidlevel       = 1<<15,
62         Msidassert      = 1<<14,
63         Msidmode        = 1<<8,         /* 3 bits; delivery mode */
64         Msidvector      = 0xff<<0,
65 };
66
67 enum{
68         /* msi capabilities */
69         Vmask           = 1<<8, /* Vectors can be masked. Optional. */
70         Cap64           = 1<<7, /* 64-bit addresses. Optional. */
71         Mmesgmsk        = 7<<4, /* Mask for # of messages allowed. See 6.8.1.3 */
72         Mmcap           = 7<<1, /* # of messages the function can support. */
73         Msienable       = 1<<0, /* Enable. */
74         /* msix capabilities */
75         Msixenable      = 1<<15,
76         Msixmask        = 1<<14,
77         Msixtblsize     = 0x7ff,
78 };
79
80 /* Find the offset in config space of this function of the msi capability.
81  * It is defined in 6.8.1 and is variable-sized.  Returns 0 on failure. */
82 static int msicap(struct pci_device *p)
83 {
84         return p->caps[PCI_CAP_ID_MSI];
85 }
86
87 /* Find the offset in config space of this function of the msi-x capability.
88  * It is defined in 6.8.1 and is variable-sized.
89  */
90 static int msixcap(struct pci_device *p)
91 {
92         return p->caps[PCI_CAP_ID_MSIX];
93 }
94
95 static int msi_blacklist(struct pci_device *p)
96 {
97         switch (p->ven_id << 16 | p->dev_id) {
98                 case 0x11ab << 16 | 0x6485:
99                 case 0x8086 << 16 | 0x100f:
100                         return -1;
101         }
102         return 0;
103 }
104
105 static int msix_blacklist(struct pci_device *p)
106 {
107         switch (p->ven_id << 16 | p->dev_id) {
108 //              case 0x11ab << 16 | 0x6485:     /* placeholder */
109                         return -1;
110         }
111         return 0;
112 }
113
114 static uint32_t msi_make_addr_lo(uint64_t vec)
115 {
116         unsigned int dest, lopri, logical;
117         /* The destination is the traditional 8-bit APIC id is in 63:56 of the
118          * vector.  Later we may need to deal with extra destination bits
119          * (Msiaedest, in this code).  I haven't seen anything in the Intel SDM
120          * about using Msiaedest (the bits are reserved) */
121         dest = vec >> 56;
122         /* lopri is rarely set, and intel doesn't recommend using it.  with msi, the
123          * lopri field is actually a redirection hint, and also must be set when
124          * sending logical messages. */
125         lopri = (vec & 0x700) == MTlp;
126         logical = (vec & Lm) != 0;
127         if (logical)
128                 lopri = 1;
129         return Msiabase | Msiadest * dest | Msialowpri * lopri |
130                Msialogical * logical;
131 }
132
133 static uint32_t msi_make_data(uint64_t vec)
134 {
135         unsigned int deliv_mode;
136         deliv_mode = (vec >> 8) & 7;
137         /* We can only specify the lower 16 bits of the MSI message, the rest gets
138          * forced to 0 by the device.  MSI-X can use the full 32 bits.  We're
139          * assuming edge triggered here. */
140         return Msidmode * deliv_mode | ((unsigned int)vec & 0xff);
141 }
142
143 /* see section 6.8.1 of the pci spec. */
144 /* Set up a single function on a single device.
145  * We need to take the vec, bust it up into bits,
146  * and put parts of it in the msi address and parts
147  * in the msi data.
148  */
149 int pci_msi_enable(struct pci_device *p, uint64_t vec)
150 {
151         unsigned int c, f, datao;
152
153         if (p->msix_ready) {
154                 printk("MSI: MSI-X is already enabled, aborting\n");
155                 return -1;
156         }
157         if (p->msi_ready) {
158                 /* only allowing one enable of MSI per device (not supporting multiple
159                  * vectors) */
160                 printk("MSI: MSI is already enabled, aborting\n");
161                 return -1;
162         }
163         p->msi_ready = TRUE;
164
165         /* Get the offset of the MSI capability
166          * in the function's config space.
167          */
168         c = msicap(p);
169         if(c == 0)
170                 return -1;
171
172         /* read it, clear out the Mmesgmsk bits. 
173          * This means that there will be no multiple
174          * messages enabled.
175          */
176         f = pcidev_read16(p, c + 2) & ~Mmesgmsk;
177
178         if (msi_blacklist(p) != 0)
179                 return -1;
180
181         /* Data begins at 8 bytes in. */
182         datao = 8;
183         p->msi_msg_addr_lo = msi_make_addr_lo(vec);
184         printd("Write to %d %08lx \n",c + 4, p->msi_msg_addr_lo);
185         pcidev_write32(p, c + 4, p->msi_msg_addr_lo);
186
187         /* And even if it's 64-bit capable, we do nothing with
188          * the high order bits. If it is 64-bit we need to offset
189          * datao (data offset) by 4 (i.e. another 32 bits)
190          */
191         if(f & Cap64){
192                 datao += 4;
193                 pcidev_write32(p, c + 8, 0);
194         }
195         p->msi_msg_addr_hi = 0;
196
197         p->msi_msg_data = msi_make_data(vec);
198         printd("Write data %d %04x\n", c + datao, p->msi_msg_data);
199         pcidev_write16(p, c + datao, p->msi_msg_data);
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 Mmesg mask (which is a power of
208          * 2) set to 0 (meaning one vector only).  Note we still haven't enabled
209          * MSI.  Will do that when we unmask.  According to the spec, we're not
210          * supposed to use the Msienable bit to mask the IRQ, though I don't see how
211          * we can mask on non-Vmask-supported HW. */
212         printd("write @ %d %04lx\n",c + 2, f);
213         pcidev_write16(p, c + 2, f);
214         return 0;
215 }
216
217 static void msix_mask_entry(struct msix_entry *entry)
218 {
219         uintptr_t reg = (uintptr_t)&entry->vector;
220         write_mmreg32(reg, read_mmreg32(reg) | 0x1);
221 }
222
223 static void msix_unmask_entry(struct msix_entry *entry)
224 {
225         uintptr_t reg = (uintptr_t)&entry->vector;
226         write_mmreg32(reg, read_mmreg32(reg) & ~0x1);
227 }
228
229 static uintptr_t msix_get_capbar_paddr(struct pci_device *p, int offset)
230 {
231         uint32_t bir, capbar_off;
232         uintptr_t membar;
233         
234         bir = pcidev_read32(p, offset);
235         capbar_off = bir & ~0x7;
236         bir &= 0x7;
237         membar = pci_get_membar(p, bir);
238
239         if (!membar) {
240                 printk("MSI-X: no cap membar, bir %d\n", bir);
241                 return 0;
242         }
243         membar += capbar_off;
244         if (PGOFF(membar)) {
245                 printk("MSI-X: unaligned cap membar %p\n", membar);
246                 return 0;
247         }
248         return membar;
249 }
250
251 /* One time initialization of MSI-X for a PCI device.  -1 on error.  Otherwise,
252  * the device will be ready to assign/route MSI-X entries/vectors.  All vectors
253  * are masked, but the overall MSI-X function is unmasked. */
254 static int pci_msix_init(struct pci_device *p)
255 {
256         unsigned int c;
257         uint16_t f;
258         int tbl_bir, tbl_off, pba_bir, pba_off;
259         struct msix_entry *entry;
260
261         if (p->msi_ready) {
262                 printk("MSI-X: MSI is already on, aborting\n");
263                 return -1;
264         }
265         if (msix_blacklist(p) != 0)
266                 return -1;
267         /* Get the offset of the MSI capability in the function's config space. */
268         c = msixcap(p);
269         if (c == 0)
270                 return -1;
271         f = pcidev_read16(p, c + 2);
272         /* enable and mask the entire function/all vectors */
273         f |= Msixenable | Msixmask;
274         pcidev_write16(p, c + 2, f);
275
276         p->msix_tbl_paddr = msix_get_capbar_paddr(p, c + 4);
277         p->msix_pba_paddr = msix_get_capbar_paddr(p, c + 8);
278         if (!p->msix_tbl_paddr || !p->msix_pba_paddr)
279                 return -1;
280         p->msix_nr_vec = (f & Msixtblsize) + 1;
281         p->msix_tbl_vaddr = vmap_pmem_nocache(p->msix_tbl_paddr, p->msix_nr_vec *
282                                               sizeof(struct msix_entry));
283         if (!p->msix_tbl_vaddr) {
284                 printk("MSI-X: unable to vmap the Table!\n");
285                 return -1;
286         }
287         p->msix_pba_vaddr = vmap_pmem_nocache(p->msix_pba_paddr,
288                                               ROUNDUP(p->msix_nr_vec, 8) / 8);
289         if (!p->msix_pba_vaddr) {
290                 printk("MSI-X: unable to vmap the PBA!\n");
291                 vunmap_vmem(p->msix_tbl_paddr,
292                         p->msix_nr_vec * sizeof(struct msix_entry));
293                 return -1;
294         }
295         /* they should all be masked already, but just in case */
296         entry = (struct msix_entry*)p->msix_tbl_vaddr;
297         for (int i = 0; i < p->msix_nr_vec; i++, entry++) {
298                 msix_mask_entry(entry);
299         }
300         /* unmask the device, now that all the vectors are masked */
301         f &= ~Msixmask;
302         pcidev_write16(p, c + 2, f);
303         return 0;
304 }
305
306 /* Enables an MSI-X vector for a PCI device.  vec is formatted like an ioapic
307  * route.  This should be able to handle multiple vectors for a device.  Returns
308  * a msix_irq_vector linkage struct on success (the connection btw an irq_h and
309  * the specific {pcidev, entry}), and 0 on failure. */
310 struct msix_irq_vector *pci_msix_enable(struct pci_device *p, uint64_t vec)
311 {
312         int i;
313         struct msix_entry *entry;
314         struct msix_irq_vector *linkage;
315         unsigned int c, datao;
316
317         /* TODO: sync protection */
318         if (!p->msix_ready) {
319                 if (pci_msix_init(p) < 0)
320                         return 0;
321                 p->msix_ready = TRUE;
322         }
323         /* find an unused slot (no apic_vector assigned).  later, we might want to
324          * point back to the irq_hs for each entry.  not a big deal now. */
325         entry = (struct msix_entry*)p->msix_tbl_vaddr;
326         for (i = 0; i < p->msix_nr_vec; i++, entry++)
327                 if (!(read_mmreg32((uintptr_t)&entry->data) & 0xff))
328                         break;
329         if (i == p->msix_nr_vec)
330                 return 0;
331         linkage = kmalloc(sizeof(struct msix_irq_vector), KMALLOC_WAIT);
332         linkage->pcidev = p;
333         linkage->entry = entry;
334         linkage->addr_lo = msi_make_addr_lo(vec);
335         linkage->addr_hi = 0;
336         linkage->data = msi_make_data(vec);
337         write_mmreg32((uintptr_t)&entry->data, linkage->data);
338         write_mmreg32((uintptr_t)&entry->addr_lo, linkage->addr_lo);
339         write_mmreg32((uintptr_t)&entry->addr_hi, linkage->addr_hi);
340         return linkage;
341 }
342
343 /* TODO: should lock in all of these PCI/MSI functions */
344 void pci_msi_mask(struct pci_device *p)
345 {
346         unsigned int c, f;
347         c = msicap(p);
348         assert(c);
349
350         f = pcidev_read16(p, c + 2);
351         pcidev_write16(p, c + 2, f & ~Msienable);
352 }
353
354 void pci_msi_unmask(struct pci_device *p)
355 {
356         unsigned int c, f;
357         c = msicap(p);
358         assert(c);
359
360         f = pcidev_read16(p, c + 2);
361         pcidev_write16(p, c + 2, f | Msienable);
362 }
363
364 int pci_msi_route(struct pci_device *p, int dest)
365 {
366         unsigned int c, f;
367         c = msicap(p);
368         assert(c);
369
370         /* mask out the old destination, replace with new */
371         p->msi_msg_addr_lo &= ~(((1 << 8) - 1) << 12);
372         p->msi_msg_addr_lo |= (dest & 0xff) << 12;
373         pcidev_write32(p, c + 4, p->msi_msg_addr_lo);
374         return 0;
375 }
376
377 void pci_msix_mask_vector(struct msix_irq_vector *linkage)
378 {
379         msix_mask_entry(linkage->entry);
380 }
381
382 void pci_msix_unmask_vector(struct msix_irq_vector *linkage)
383 {
384         msix_unmask_entry(linkage->entry);
385 }
386
387 void pci_msix_route_vector(struct msix_irq_vector *linkage, int dest)
388 {
389         /* mask out the old destination, replace with new */
390         linkage->addr_lo &= ~(((1 << 8) - 1) << 12);
391         linkage->addr_lo |= (dest & 0xff) << 12;
392         write_mmreg32((uintptr_t)&linkage->entry->addr_lo, linkage->addr_lo);
393 }