898c58f922a18b6403ac678e270766ff66fe9227
[akaros.git] / kern / arch / x86 / pci.c
1 /* Copyright (c) 2009, 2010 The Regents of the University of California
2  * See LICENSE for details.
3  *
4  * Barret Rhoden <brho@cs.berkeley.edu>
5  * Original by Paul Pearce <pearce@eecs.berkeley.edu> */
6
7 #include <arch/x86.h>
8 #include <arch/pci.h>
9 #include <trap.h>
10 #include <stdio.h>
11 #include <string.h>
12 #include <assert.h>
13 #include <kmalloc.h>
14 #include <arch/pci_defs.h>
15
16 /* List of all discovered devices */
17 struct pcidev_stailq pci_devices = STAILQ_HEAD_INITIALIZER(pci_devices);
18
19 static char STD_PCI_DEV[] = "Standard PCI Device";
20 static char PCI2PCI[] = "PCI-to-PCI Bridge";
21 static char PCI2CARDBUS[] = "PCI-Cardbus Bridge";
22
23 /* memory bars have a little dance you go through to detect what the size of the
24  * memory region is.  for 64 bit bars, i'm assuming you only need to do this to
25  * the lower part (no device will need > 4GB, right?). */
26 uint32_t pci_membar_get_sz(struct pci_device *pcidev, int bar)
27 {
28         /* save the old value, write all 1s, invert, add 1, restore.
29          * http://wiki.osdev.org/PCI for details. */
30         uint8_t bar_off = PCI_BAR0_STD + bar * PCI_BAR_OFF;
31         uint32_t old_val = pcidev_read32(pcidev, bar_off);
32         uint32_t retval;
33         pcidev_write32(pcidev, bar_off, 0xffffffff);
34         /* Don't forget to mask the lower 3 bits! */
35         retval = pcidev_read32(pcidev, bar_off) & PCI_BAR_MEM_MASK;
36         retval = ~retval + 1;
37         pcidev_write32(pcidev, bar_off, old_val);
38         return retval;
39 }
40
41 /* process the bars.  these will tell us what address space (PIO or memory) and
42  * where the base is.  fills results into pcidev.  i don't know if you can have
43  * multiple bars with conflicting/different regions (like two separate PIO
44  * ranges).  I'm assuming you don't, and will warn if we see one. */
45 static void pci_handle_bars(struct pci_device *pcidev)
46 {
47         /* only handling standards for now */
48         uint32_t bar_val;
49         int max_bars = pcidev->header_type == STD_PCI_DEV ? MAX_PCI_BAR : 0;
50         for (int i = 0; i < max_bars; i++) {
51                 bar_val = pci_getbar(pcidev, i);
52                 pcidev->bar[i].raw_bar = bar_val;
53                 if (!bar_val)   /* (0 denotes no valid data) */
54                         continue;
55                 if (pci_is_iobar(bar_val)) {
56                         pcidev->bar[i].pio_base = pci_getiobar32(bar_val);
57                 } else {
58                         if (pci_is_membar32(bar_val)) {
59                                 pcidev->bar[i].mmio_base32 = bar_val & PCI_BAR_MEM_MASK;
60                                 pcidev->bar[i].mmio_sz = pci_membar_get_sz(pcidev, i);
61                         } else if (pci_is_membar64(bar_val)) {
62                                 /* 64 bit, the lower 32 are in this bar, the upper
63                                  * are in the next bar */
64                                 pcidev->bar[i].mmio_base64 = bar_val & PCI_BAR_MEM_MASK;
65                                 assert(i < max_bars - 1);
66                                 bar_val = pci_getbar(pcidev, i + 1);    /* read next bar */
67                                 /* note we don't check for IO or memsize.  the entire next bar
68                                  * is supposed to be for the upper 32 bits. */
69                                 pcidev->bar[i].mmio_base64 |= (uint64_t)bar_val << 32;
70                                 pcidev->bar[i].mmio_sz = pci_membar_get_sz(pcidev, i);
71                                 i++;
72                         }
73                 }
74                 /* this will track the maximum bar we've had.  it'll include the 64 bit
75                  * uppers, as well as devices that have only higher numbered bars. */
76                 pcidev->nr_bars = i + 1;
77         }
78 }
79
80 /* Scans the PCI bus.  Won't actually work for anything other than bus 0, til we
81  * sort out how to handle bridge devices. */
82 void pci_init(void) {
83         uint32_t result = 0;
84         uint16_t dev_id, ven_id;
85         struct pci_device *pcidev;
86         for (int i = 0; i < PCI_MAX_BUS - 1; i++)       /* phantoms at 0xff */
87                 for (int j = 0; j < PCI_MAX_DEV; j++)
88                         for (int k = 0; k < PCI_MAX_FUNC; k++) {
89                                 result = pci_read32(i, j, k, PCI_DEV_VEND_REG);
90                                 dev_id = result >> PCI_DEVICE_OFFSET;
91                                 ven_id = result & PCI_VENDOR_MASK;
92                                 /* Skip invalid IDs (not a device) */
93                                 if (ven_id == INVALID_VENDOR_ID) 
94                                         continue;
95                                 pcidev = kzmalloc(sizeof(struct pci_device), 0);
96                                 pcidev->bus = i;
97                                 pcidev->dev = j;
98                                 pcidev->func = k;
99                                 pcidev->dev_id = dev_id;
100                                 pcidev->ven_id = ven_id;
101                                 /* Get the Class/subclass */
102                                 result = pcidev_read32(pcidev, PCI_CLASS_REG);
103                                 pcidev->class = result >> 24;
104                                 pcidev->subclass = (result >> 16) & 0xff;
105                                 pcidev->progif = (result >> 8) & 0xff;
106                                 /* All device types (0, 1, 2) have the IRQ in the same place */
107                                 result = pcidev_read32(pcidev, PCI_IRQ_STD);
108                                 /* This is the PIC IRQ the device is wired to */
109                                 pcidev->irqline = result & PCI_IRQLINE_MASK;
110                                 /* This is the interrupt pin the device uses (INTA# - INTD#) */
111                                 pcidev->irqpin = (result & PCI_IRQPIN_MASK) >> PCI_IRQPIN_SHFT;
112                                 if (pcidev->irqpin != PCI_NOINT) {
113                                         /* TODO: use a list (check for collisions for now) (massive
114                                          * collisions on a desktop with bridge IRQs. */
115                                         //assert(!irq_pci_map[pcidev->irqline]);
116                                         irq_pci_map[pcidev->irqline] = pcidev;
117                                 }
118                                 switch ((pcidev_read32(pcidev, PCI_HEADER_REG) >> 16) & 0xff) {
119                                         case 0x00:
120                                                 pcidev->header_type = STD_PCI_DEV;
121                                                 break;
122                                         case 0x01:
123                                                 pcidev->header_type = PCI2PCI;
124                                                 break;
125                                         case 0x02:
126                                                 pcidev->header_type = PCI2CARDBUS;
127                                                 break;
128                                         default:
129                                                 pcidev->header_type = "Unknown Header Type";
130                                 }
131                                 pci_handle_bars(pcidev);
132                                 STAILQ_INSERT_TAIL(&pci_devices, pcidev, all_dev);
133                                 #ifdef CONFIG_PCI_VERBOSE
134                                 pcidev_print_info(pcidev, 4);
135                                 #else
136                                 pcidev_print_info(pcidev, 0);
137                                 #endif /* CONFIG_PCI_VERBOSE */
138                         }
139 }
140
141 /* Helper to read 32 bits from the config space of B:D:F.  'Offset' is how far
142  * into the config space we offset before reading, aka: where we are reading. */
143 uint32_t pci_read32(unsigned short bus, unsigned short dev, unsigned short func,
144                     unsigned short offset)
145 {
146         /* Send type 1 requests for everything beyond bus 0.  Note this does nothing
147          * until we configure the PCI bridges (which we don't do yet). */
148         if (bus !=  0)
149                 offset |= 0x1;
150         outl(PCI_CONFIG_ADDR, MK_CONFIG_ADDR(bus, dev, func, offset));
151         return inl(PCI_CONFIG_DATA);
152 }
153
154 /* Same, but writes (doing 32bit at a time).  Never actually tested (not sure if
155  * PCI lets you write back). */
156 void pci_write32(unsigned short bus, unsigned short dev, unsigned short func,
157                     unsigned short offset, uint32_t value)
158 {
159         outl(PCI_CONFIG_ADDR, MK_CONFIG_ADDR(bus, dev, func, offset));
160         outl(PCI_CONFIG_DATA, value);
161 }
162
163 /* Helper to read from a specific device's config space. */
164 uint32_t pcidev_read32(struct pci_device *pcidev, unsigned short offset)
165 {
166         return pci_read32(pcidev->bus, pcidev->dev, pcidev->func, offset);
167 }
168
169 /* Helper to write to a specific device */
170 void pcidev_write32(struct pci_device *pcidev, unsigned short offset,
171                     uint32_t value)
172 {
173         pci_write32(pcidev->bus, pcidev->dev, pcidev->func, offset, value);
174 }
175
176 /* Gets any old raw bar, with some catches based on type. */
177 uint32_t pci_getbar(struct pci_device *pcidev, unsigned int bar)
178 {
179         uint32_t value, type;
180         if (bar >= MAX_PCI_BAR)
181                 panic("Nonexistant bar requested!");
182         value = pcidev_read32(pcidev, PCI_HEADER_REG);
183         type = (value >> 16) & 0xff;
184         /* Only types 0 and 1 have BARS */
185         if ((type != 0x00) && (type != 0x01))
186                 return 0;
187         /* Only type 0 has BAR2 - BAR5 */
188         if ((bar > 1) && (type != 0x00))
189                 return 0;
190         return pcidev_read32(pcidev, PCI_BAR0_STD + bar * PCI_BAR_OFF);
191 }
192
193 /* Determines if a given bar is IO (o/w, it's mem) */
194 bool pci_is_iobar(uint32_t bar)
195 {
196         return bar & PCI_BAR_IO;
197 }
198
199 bool pci_is_membar32(uint32_t bar)
200 {
201         if (pci_is_iobar(bar))
202                 return FALSE;
203         return (bar & PCI_MEMBAR_TYPE) == PCI_MEMBAR_32BIT;
204 }
205
206 bool pci_is_membar64(uint32_t bar)
207 {
208         if (pci_is_iobar(bar))
209                 return FALSE;
210         return (bar & PCI_MEMBAR_TYPE) == PCI_MEMBAR_64BIT;
211 }
212
213 /* Helper to get the address from a membar.  Check the type beforehand */
214 uint32_t pci_getmembar32(uint32_t bar)
215 {
216         uint8_t type = bar & PCI_MEMBAR_TYPE;
217         if (type != PCI_MEMBAR_32BIT) {
218                 warn("Unhandled PCI membar type: %02p\n", type >> 1);
219                 return 0;
220         }
221         return bar & 0xfffffff0;
222 }
223
224 /* Helper to get the address from an IObar.  Check the type beforehand */
225 uint32_t pci_getiobar32(uint32_t bar)
226 {
227         return bar & 0xfffffffc;
228 }
229
230 /* Helper to get the class description strings.  Adapted from
231  * http://www.pcidatabase.com/reports.php?type=c-header */
232 static void pcidev_get_cldesc(struct pci_device *pcidev, char **class,
233                               char **subclass, char **progif)
234 {
235         int     i ;
236         *class = *subclass = *progif = "";
237
238         for (i = 0; i < PCI_CLASSCODETABLE_LEN; i++) {
239                 if (PciClassCodeTable[i].BaseClass == pcidev->class) {
240                         if (!(**class))
241                                 *class = PciClassCodeTable[i].BaseDesc;
242                         if (PciClassCodeTable[i].SubClass == pcidev->subclass) {
243                                 if (!(**subclass))
244                                         *subclass = PciClassCodeTable[i].SubDesc;
245                                 if (PciClassCodeTable[i].ProgIf == pcidev->progif) {
246                                         *progif = PciClassCodeTable[i].ProgDesc;
247                                         break ;
248                                 }
249                         }
250                 }
251         }
252 }
253
254 /* Helper to get the vendor and device description strings */
255 static void pcidev_get_devdesc(struct pci_device *pcidev, char **vend_short,
256                                char **vend_full, char **chip, char **chip_desc)
257 {
258         int     i ;
259         *vend_short = *vend_full = *chip = *chip_desc = "";
260
261         for (i = 0; i < PCI_VENTABLE_LEN; i++) {
262                 if (PciVenTable[i].VenId == pcidev->ven_id) {
263                         *vend_short = PciVenTable[i].VenShort;
264                         *vend_full = PciVenTable[i].VenFull;
265                         break ;
266                 }
267         }
268         for (i = 0; i < PCI_DEVTABLE_LEN; i++) {
269                 if ((PciDevTable[i].VenId == pcidev->ven_id) &&
270                    (PciDevTable[i].DevId == pcidev->dev_id)) {
271                         *chip = PciDevTable[i].Chip;
272                         *chip_desc = PciDevTable[i].ChipDesc;
273                         break ;
274                 }
275         }
276 }
277
278 /* Prints info (like lspci) for a device */
279 void pcidev_print_info(struct pci_device *pcidev, int verbosity)
280 {
281         char *ven_sht, *ven_fl, *chip, *chip_txt, *class, *subcl, *progif;
282         pcidev_get_cldesc(pcidev, &class, &subcl, &progif);
283         pcidev_get_devdesc(pcidev, &ven_sht, &ven_fl, &chip, &chip_txt);
284
285         printk("%02x:%02x.%x %s: %s %s %s: %s\n",
286                pcidev->bus,
287                pcidev->dev,
288                pcidev->func,
289                subcl,
290                ven_sht,
291                chip,
292                chip_txt,
293                    pcidev->header_type);
294         if (verbosity < 1)      /* whatever */
295                 return;
296         printk("\tIRQ: %02d IRQ pin: 0x%02x\n",
297                pcidev->irqline,
298                pcidev->irqpin);
299         printk("\tVendor Id: 0x%04x Device Id: 0x%04x\n",
300                pcidev->ven_id,
301                pcidev->dev_id);
302         printk("\t%s %s %s\n",
303                class,
304                progif,
305                ven_fl);
306         for (int i = 0; i < pcidev->nr_bars; i++) {
307                 if (pcidev->bar[i].raw_bar == 0)
308                         continue;
309                 printk("\tBAR %d: ", i);
310                 if (pci_is_iobar(pcidev->bar[i].raw_bar)) {
311                         assert(pcidev->bar[i].pio_base);
312                         printk("IO port 0x%04x\n", pcidev->bar[i].pio_base);
313                 } else {
314                         bool bar_is_64 = pci_is_membar64(pcidev->bar[i].raw_bar);
315                         printk("MMIO Base %p, MMIO Size %p\n",
316                                bar_is_64 ? pcidev->bar[i].mmio_base64 :
317                                            pcidev->bar[i].mmio_base32,
318                                pcidev->bar[i].mmio_sz);
319                         /* Takes up two bars */
320                         if (bar_is_64) {
321                                 assert(!pcidev->bar[i].mmio_base32);    /* double-check */
322                                 i++;
323                         }
324                 }
325         }
326 }
327
328 void pci_set_bus_master(struct pci_device *pcidev)
329 {
330         pcidev_write32(pcidev, PCI_STAT_CMD_REG,
331                        pcidev_read32(pcidev, PCI_STAT_CMD_REG) |
332                        PCI_CMD_BUS_MAS);
333 }