-Added IRQ to PCI
[pintos-anon] / src / devices / pci.c
1 #include <debug.h>
2 #include <list.h>
3 #include <stdbool.h>
4 #include <stdio.h>
5 #include "devices/pci.h"
6 #include "threads/io.h"
7 #include "threads/malloc.h"
8
9 struct list pci_dev_list;
10
11 static void scan_bus(uint8_t bus);
12
13 static uint32_t
14 pci_read_config (unsigned bus, unsigned dev, unsigned func, unsigned reg) 
15 {
16   ASSERT (bus < 256);
17   ASSERT (dev < 32);
18   ASSERT (func < 8);
19   ASSERT (reg < 64);
20
21   outl (0xcf8,
22         0x80000000 | (bus << 16) | (dev << 11) | (func << 8) | (reg << 2));
23   return inl (0xcfc);
24 }
25
26 static void
27 pci_write_config (unsigned bus, unsigned dev, unsigned func, unsigned reg, 
28                   unsigned data)
29 {
30   ASSERT (bus < 256);
31   ASSERT (dev < 32);
32   ASSERT (func < 8);
33   ASSERT (reg < 64);
34
35   outl (0xcf8,
36         0x80000000 | (bus << 16) | (dev << 11) | (func << 8) | (reg << 2));
37   outl (0xcfc, data);
38 }
39
40 static void
41 pci_dump_dev (struct pci_dev *dev) 
42 {
43   int bar;
44
45   printf ("PCI: Device %04x:%02x:%02x.%x %04x:%04x\n", 
46           0, dev->bus_id, dev->devfn >> 4, dev->devfn & 0xf, 
47           dev->ven_id, dev->dev_id);
48   printf ("PCI: Class %02x:%02x:%02x\n", 
49           dev->base_class, dev->sub_class, dev->interface);
50
51   for (bar = 0; bar < PCI_NUM_BARS; bar++)
52     {
53       if (dev->resources[bar].start != 0 ||
54           dev->resources[bar].end != 0)
55         {
56           printf ("PCI: %s ", 
57                   dev->resources[bar].type == PCI_BAR_TYPE_MEM 
58                   ? "Memory" : "IO");
59           printf ("range %d: %08x-%08x\n", 
60                   bar, dev->resources[bar].start, dev->resources[bar].end);
61         }
62     }
63   if (dev->irq) {
64     printf ("PCI: Interrupt: %u\n", dev->irq);
65   }
66 }
67
68 static void
69 pci_setup_irq (struct pci_dev *dev)
70 {
71   uint32_t pinline;
72   pinline = pci_read_config(dev->bus_id, 
73                             dev->devfn >> 4, 
74                             dev->devfn & 0xf, 
75                             PCI_REGNUM_INTERRUPT);
76
77   /* Devices without a non-zero pin get IRQ 0. I guess this is acceptable? */
78   if (pinline & PCI_INTERRUPT_MASK_PIN) {
79     dev->irq = pinline & PCI_INTERRUPT_MASK_LINE;
80   }
81 }
82
83 static void
84 pci_setup_bases (struct pci_dev *dev) 
85 {
86   int bar;
87   unsigned bar_reg;
88   uint32_t base, size;
89   struct resource *res;
90
91   for (bar = 0; bar < PCI_NUM_BARS; bar++)
92     {
93       bar_reg = PCI_REGNUM_BASE_ADDRESS + bar;
94
95       res = &dev->resources[bar];
96       res->start = 0;
97       res->end = 0;
98       
99       /* Get the base address out of the BAR */
100       base = pci_read_config(dev->bus_id,
101                              dev->devfn >> 4, 
102                              dev->devfn & 0xf, 
103                              bar_reg);
104
105       /* Write 1's and get the size from the BAR */
106       pci_write_config(dev->bus_id, 
107                        dev->devfn >> 4, 
108                        dev->devfn & 0xf, 
109                        bar_reg,
110                        ~0);
111       size = pci_read_config(dev->bus_id, 
112                              dev->devfn >> 4, 
113                              dev->devfn & 0xf, 
114                              bar_reg);
115
116       /* Put the base address back in the BAR */
117       pci_write_config(dev->bus_id, 
118                        dev->devfn >> 4, 
119                        dev->devfn & 0xf, 
120                        bar_reg,
121                        base);
122
123       /* If the size is unreasonable, skip this BAR.
124          Is this redundant for Pintos? */
125       if (!size || size == 0xffffffff) {
126         continue;
127       }
128
129       /* Broken hardware that requests a base at the end? 
130          Maybe redundant for Pintos? */
131       if (base == 0xffffffff) {
132         base = 0;
133       }
134
135       /* Make a new resource, tag it for I/O vs. memory */
136       res->type = base & PCI_BAR_TYPE_MASK;
137       if (res->type == PCI_BAR_TYPE_MEM) 
138         {
139           res->start = base & PCI_BAR_MASK_MEM;
140           size = size & PCI_BAR_MASK_MEM;
141         }
142       else
143         {
144           res->start = base & PCI_BAR_MASK_IO;
145           size = size & PCI_BAR_MASK_IO;
146         }
147       res->end = res->start + size;
148     }
149 }
150
151 static bool
152 scan_device (uint8_t bus, uint8_t dev, uint8_t func) 
153 {
154   uint32_t cfg[16];
155   uint8_t *byte_cfg;
156   int dev_id, vendor_id;
157   int line;
158   int reg;
159   struct pci_dev *new_pci_dev;
160
161   byte_cfg = (uint8_t *) cfg;
162
163   /* Read configuration space header */
164   for (reg = 0; reg < 16; reg++)
165     cfg[reg] = pci_read_config (bus, dev, func, reg);
166   
167   /* Get vendor and device ID */
168   dev_id = cfg[0] >> 16;
169   vendor_id = cfg[0] & 0xffff;
170
171   if (dev_id == 0 || dev_id == PCI_BAD_DEVICE || 
172       vendor_id == 0 || vendor_id == PCI_BAD_DEVICE)
173     return 0;
174
175   /* We have a valid PCI device, set it up */
176   new_pci_dev = malloc (sizeof *new_pci_dev);
177   if (!new_pci_dev)
178     PANIC ("couldn't allocate memory for PCI device");
179
180   new_pci_dev->bus_id = bus;
181   new_pci_dev->devfn = (dev << 4) | func;
182   new_pci_dev->ven_id = vendor_id;
183   new_pci_dev->dev_id = dev_id;
184   new_pci_dev->base_class = byte_cfg[PCI_REG_CLASS_BASE];
185   new_pci_dev->sub_class = byte_cfg[PCI_REG_CLASS_SUB];
186   new_pci_dev->interface = byte_cfg[PCI_REG_CLASS_INTERFACE];
187   list_push_front (&pci_dev_list, &new_pci_dev->elem);
188
189   pci_setup_irq (new_pci_dev);
190     
191   /* If device is PCI-to-PCI bridge, scan the bus behind it */
192   if (new_pci_dev->base_class == PCI_BRIDGE_BASE_CLASS &&
193       new_pci_dev->sub_class == PCI_BRIDGE_SUB_CLASS &&
194       (byte_cfg[PCI_REG_HEADER_TYPE] & 0x3f) == PCI_BRIDGE_HEADER_TYPE)
195     {
196       scan_bus(byte_cfg[PCI_BRIDGE_REG_SBUS]);
197     }
198
199   /* For normal devices, set up the BARs */
200   else 
201     {
202       pci_setup_bases (new_pci_dev);
203     }
204
205   /* Debugging output */
206   pci_dump_dev(new_pci_dev);
207   for (line = 0; line < 16; line++) 
208     {
209       int byte;
210       
211       printf ("PCI: %02x:", line * 4);
212       for (byte = 3; byte >= 0; byte--)
213         printf (" %02x", byte_cfg[line * 4 + byte]);
214       printf ("\n");
215     }
216   printf ("\n");
217
218   
219   /* Return if we're a multifunction device */
220   return byte_cfg[PCI_REG_HEADER_TYPE] & 0x80;
221 }
222
223 static void
224 scan_bus(uint8_t bus)
225 {
226   uint8_t dev;
227
228   printf ("PCI: Scanning bus (%u)\n", bus);
229   printf ("\n");
230
231   for (dev = 0; dev < 32; dev++) 
232     {
233       /* Returns true if device is multi-function */
234       if (scan_device (bus, dev, 0)) 
235         {
236           int func;
237
238           for (func = 1; func < 8; func++)
239             scan_device (bus, dev, func);
240         }
241     }
242   printf ("PCI: Done (%u)\n", bus);
243   printf ("\n");
244 }
245
246 void
247 pci_init (void) 
248 {
249   printf ("\n");
250   printf ("PCI: Initializating\n");
251   list_init (&pci_dev_list);
252   scan_bus(0);  
253   printf ("PCI: Initialization done\n");
254 }
255
256 void
257 pci_dump (void)
258 {
259   struct list_elem *e;
260
261   for (e = list_begin (&pci_dev_list); e != list_end (&pci_dev_list);
262        e = list_next (e))
263     {
264       struct pci_dev *dev = list_entry (e, struct pci_dev, elem);
265       pci_dump_dev(dev);
266     }
267 }