766cf3231e63598de78c54165a97b5892a5f07fc
[akaros.git] / kern / arch / i686 / 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 <stdio.h>
10 #include <string.h>
11 #include <assert.h>
12 #include <kmalloc.h>
13
14 /* Which pci devices hang off of which irqs */
15 /* TODO: make this an array of SLISTs (pain from ioapic.c, etc...) */
16 struct pci_device *irq_pci_map[NUM_IRQS] = {0};
17
18 /* List of all discovered devices */
19 struct pcidev_stailq pci_devices = STAILQ_HEAD_INITIALIZER(pci_devices);
20
21 /* Scans the PCI bus.  Won't actually work for anything other than bus 0, til we
22  * sort out how to handle bridge devices. */
23 void pci_init(void) {
24         uint32_t result = 0;
25         uint16_t dev_id, ven_id;
26         struct pci_device *pcidev;
27         for (int i = 0; i < PCI_MAX_BUS; i++)
28                 for (int j = 0; j < PCI_MAX_DEV; j++)
29                         for (int k = 0; k < PCI_MAX_FUNC; k++) {
30                                 result = pci_read32(i, j, k, PCI_DEV_VEND_REG);
31                                 dev_id = result >> PCI_DEVICE_OFFSET;
32                                 ven_id = result & PCI_VENDOR_MASK;
33                                 /* Skip invalid IDs (not a device) */
34                                 if (ven_id == INVALID_VENDOR_ID) 
35                                         continue;
36                                 pcidev = kmalloc(sizeof(struct pci_device), 0);
37                                 pcidev->bus = i;
38                                 pcidev->dev = j;
39                                 pcidev->func = k;
40                                 pcidev->dev_id = dev_id;
41                                 pcidev->ven_id = ven_id;
42                                 /* Get the Class/subclass */
43                                 result = pcidev_read32(pcidev, PCI_CLASS_REG);
44                                 pcidev->class = result >> 24;
45                                 pcidev->subclass = (result >> 16) & 0xff;
46                                 /* All device types (0, 1, 2) have the IRQ in the same place */
47                                 result = pcidev_read32(pcidev, PCI_IRQ_STD);
48                                 /* This is the PIC IRQ the device is wired to */
49                                 pcidev->irqline = result & PCI_IRQLINE_MASK;
50                                 /* This is the interrupt pin the device uses (INTA# - INTD#) */
51                                 pcidev->irqpin = (result & PCI_IRQPIN_MASK) >> PCI_IRQPIN_SHFT;
52                                 printk("PCI: %d:%d:%d Vend/Dev: %04p/%04p Class/Sub: %02p/%02p "
53                                        "IRQ: %d Pin: %p\n", pcidev->bus, pcidev->dev,
54                                        pcidev->func, pcidev->ven_id, pcidev->dev_id,
55                                        pcidev->class, pcidev->subclass, pcidev->irqline,
56                                        pcidev->irqpin);
57                                 if (pcidev->irqpin != PCI_NOINT) {
58                                         /* TODO: use a list (check for collisions for now) */
59                                         assert(!irq_pci_map[pcidev->irqline]);
60                                         irq_pci_map[pcidev->irqline] = pcidev;
61                                 }
62                                 /* Loop over the BARs Right now we don't do anything useful with
63                                  * this data.  This is legacy code in which I pulled data from
64                                  * the BARS during NIC development At some point we will have to
65                                  * use this, so the code is still here. */
66                                 
67                                 // Note: These magic numbers are from the PCI spec (according to OSDev).
68                                 #if 0
69                                 #ifdef CHECK_BARS
70                                 for (int k = 0; k <= 5; k++) {
71                                         reg = 4 + k;
72                                         address = MK_CONFIG_ADDR(bus, dev, func, reg << 2);     
73                                 outl(PCI_CONFIG_ADDR, address);
74                                 result = inl(PCI_CONFIG_DATA);
75                                         
76                                         if (result == 0) // (0 denotes no valid data)
77                                                 continue;
78
79                                         // Read the bottom bit of the BAR. 
80                                         if (result & PCI_BAR_IO_MASK) {
81                                                 result = result & PCI_IO_MASK;
82                                                 pci_debug("-->BAR%u: %s --> %x\n", k, "IO", result);
83                                         } else {
84                                                 result = result & PCI_MEM_MASK;
85                                                 pci_debug("-->BAR%u: %s --> %x\n", k, "MEM", result);
86                                         }                                       
87                                 }
88                                 #endif
89                                 #endif
90                                 
91                                 STAILQ_INSERT_TAIL(&pci_devices, pcidev, all_dev);
92                         }
93 }
94
95 /* Helper to read 32 bits from the config space of B:D:F.  'Offset' is how far
96  * into the config space we offset before reading, aka: where we are reading. */
97 uint32_t pci_read32(unsigned short bus, unsigned short dev, unsigned short func,
98                     unsigned short offset)
99 {
100         /* Send type 1 requests for everything beyond bus 0.  Note this does nothing
101          * until we configure the PCI bridges (which we don't do yet). */
102         if (bus !=  0)
103                 offset |= 0x1;
104         outl(PCI_CONFIG_ADDR, MK_CONFIG_ADDR(bus, dev, func, offset));
105         return inl(PCI_CONFIG_DATA);
106 }
107
108 /* Same, but writes (doing 32bit at a time).  Never actually tested (not sure if
109  * PCI lets you write back). */
110 void pci_write32(unsigned short bus, unsigned short dev, unsigned short func,
111                     unsigned short offset, uint32_t value)
112 {
113         outl(PCI_CONFIG_ADDR, MK_CONFIG_ADDR(bus, dev, func, offset));
114         outl(PCI_CONFIG_DATA, value);
115 }
116
117 /* Helper to read from a specific device's config space. */
118 uint32_t pcidev_read32(struct pci_device *pcidev, unsigned short offset)
119 {
120         return pci_read32(pcidev->bus, pcidev->dev, pcidev->func, offset);
121 }
122
123 /* Helper to write to a specific device */
124 void pcidev_write32(struct pci_device *pcidev, unsigned short offset,
125                     uint32_t value)
126 {
127         pci_write32(pcidev->bus, pcidev->dev, pcidev->func, offset, value);
128 }
129
130 /* Gets any old raw bar. */
131 uint32_t pci_getbar(struct pci_device *pcidev, unsigned int bar)
132 {
133         uint32_t value, type;
134         if (bar > 5)
135                 panic("Nonexistant bar requested!");
136         value = pcidev_read32(pcidev, PCI_HEADER_REG);
137         type = (value >> 16) & 0xff;
138         /* Only types 0 and 1 have BARS */
139         if ((type != 0x00) && (type != 0x01))
140                 return 0;
141         /* Only type 0 has BAR2 - BAR5 */
142         if ((bar > 1) && (type != 0x00))
143                 return 0;
144         return pcidev_read32(pcidev, PCI_BAR0_STD + bar * PCI_BAR_OFF);
145 }
146
147 /* Determines if a given bar is IO (o/w, it's mem) */
148 bool pci_is_iobar(uint32_t bar)
149 {
150         return bar & PCI_BAR_IO;
151 }
152
153 /* Helper to get the address from a membar.  Check the type beforehand */
154 uint32_t pci_getmembar32(uint32_t bar)
155 {
156         uint8_t type = bar & PCI_MEMBAR_TYPE;
157         if (type != PCI_MEMBAR_32BIT) {
158                 warn("Unhandled PCI membar type: %02p\n", type >> 1);
159                 return 0;
160         }
161         return bar & 0xfffffff0;
162 }
163
164 /* Helper to get the address from an IObar.  Check the type beforehand */
165 uint32_t pci_getiobar32(uint32_t bar)
166 {
167         return bar & 0xfffffffc;
168 }
169