new 64b kernel memory map (not userspace yet)
[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 #include <arch/pci_defs.h>
14
15 /* Which pci devices hang off of which irqs */
16 /* TODO: make this an array of SLISTs (pain from ioapic.c, etc...) */
17 struct pci_device *irq_pci_map[NUM_IRQS] = {0};
18
19 /* List of all discovered devices */
20 struct pcidev_stailq pci_devices = STAILQ_HEAD_INITIALIZER(pci_devices);
21
22 /* Scans the PCI bus.  Won't actually work for anything other than bus 0, til we
23  * sort out how to handle bridge devices. */
24 void pci_init(void) {
25         uint32_t result = 0;
26         uint16_t dev_id, ven_id;
27         struct pci_device *pcidev;
28         for (int i = 0; i < PCI_MAX_BUS - 1; i++)       /* phantoms at 0xff */
29                 for (int j = 0; j < PCI_MAX_DEV; j++)
30                         for (int k = 0; k < PCI_MAX_FUNC; k++) {
31                                 result = pci_read32(i, j, k, PCI_DEV_VEND_REG);
32                                 dev_id = result >> PCI_DEVICE_OFFSET;
33                                 ven_id = result & PCI_VENDOR_MASK;
34                                 /* Skip invalid IDs (not a device) */
35                                 if (ven_id == INVALID_VENDOR_ID) 
36                                         continue;
37                                 pcidev = kmalloc(sizeof(struct pci_device), 0);
38                                 pcidev->bus = i;
39                                 pcidev->dev = j;
40                                 pcidev->func = k;
41                                 pcidev->dev_id = dev_id;
42                                 pcidev->ven_id = ven_id;
43                                 /* Get the Class/subclass */
44                                 result = pcidev_read32(pcidev, PCI_CLASS_REG);
45                                 pcidev->class = result >> 24;
46                                 pcidev->subclass = (result >> 16) & 0xff;
47                                 pcidev->progif = (result >> 8) & 0xff;
48                                 /* All device types (0, 1, 2) have the IRQ in the same place */
49                                 result = pcidev_read32(pcidev, PCI_IRQ_STD);
50                                 /* This is the PIC IRQ the device is wired to */
51                                 pcidev->irqline = result & PCI_IRQLINE_MASK;
52                                 /* This is the interrupt pin the device uses (INTA# - INTD#) */
53                                 pcidev->irqpin = (result & PCI_IRQPIN_MASK) >> PCI_IRQPIN_SHFT;
54                                 #ifdef __CONFIG_PCI_VERBOSE__
55                                 pcidev_print_info(pcidev, 4);
56                                 #else
57                                 pcidev_print_info(pcidev, 0);
58                                 #endif /* __CONFIG_PCI_VERBOSE__ */
59                                 if (pcidev->irqpin != PCI_NOINT) {
60                                         /* TODO: use a list (check for collisions for now) (massive
61                                          * collisions on a desktop with bridge IRQs. */
62                                         //assert(!irq_pci_map[pcidev->irqline]);
63                                         irq_pci_map[pcidev->irqline] = pcidev;
64                                 }
65                                 /* Loop over the BARs Right now we don't do anything useful with
66                                  * this data.  This is legacy code in which I pulled data from
67                                  * the BARS during NIC development At some point we will have to
68                                  * use this, so the code is still here. */
69                                 
70                                 // Note: These magic numbers are from the PCI spec (according to OSDev).
71                                 #if 0
72                                 #ifdef CHECK_BARS
73                                 for (int k = 0; k <= 5; k++) {
74                                         reg = 4 + k;
75                                         address = MK_CONFIG_ADDR(bus, dev, func, reg << 2);     
76                                 outl(PCI_CONFIG_ADDR, address);
77                                 result = inl(PCI_CONFIG_DATA);
78                                         
79                                         if (result == 0) // (0 denotes no valid data)
80                                                 continue;
81
82                                         // Read the bottom bit of the BAR. 
83                                         if (result & PCI_BAR_IO_MASK) {
84                                                 result = result & PCI_IO_MASK;
85                                                 pci_debug("-->BAR%u: %s --> %x\n", k, "IO", result);
86                                         } else {
87                                                 result = result & PCI_MEM_MASK;
88                                                 pci_debug("-->BAR%u: %s --> %x\n", k, "MEM", result);
89                                         }                                       
90                                 }
91                                 #endif
92                                 #endif
93                                 
94                                 STAILQ_INSERT_TAIL(&pci_devices, pcidev, all_dev);
95                         }
96 }
97
98 /* Helper to read 32 bits from the config space of B:D:F.  'Offset' is how far
99  * into the config space we offset before reading, aka: where we are reading. */
100 uint32_t pci_read32(unsigned short bus, unsigned short dev, unsigned short func,
101                     unsigned short offset)
102 {
103         /* Send type 1 requests for everything beyond bus 0.  Note this does nothing
104          * until we configure the PCI bridges (which we don't do yet). */
105         if (bus !=  0)
106                 offset |= 0x1;
107         outl(PCI_CONFIG_ADDR, MK_CONFIG_ADDR(bus, dev, func, offset));
108         return inl(PCI_CONFIG_DATA);
109 }
110
111 /* Same, but writes (doing 32bit at a time).  Never actually tested (not sure if
112  * PCI lets you write back). */
113 void pci_write32(unsigned short bus, unsigned short dev, unsigned short func,
114                     unsigned short offset, uint32_t value)
115 {
116         outl(PCI_CONFIG_ADDR, MK_CONFIG_ADDR(bus, dev, func, offset));
117         outl(PCI_CONFIG_DATA, value);
118 }
119
120 /* Helper to read from a specific device's config space. */
121 uint32_t pcidev_read32(struct pci_device *pcidev, unsigned short offset)
122 {
123         return pci_read32(pcidev->bus, pcidev->dev, pcidev->func, offset);
124 }
125
126 /* Helper to write to a specific device */
127 void pcidev_write32(struct pci_device *pcidev, unsigned short offset,
128                     uint32_t value)
129 {
130         pci_write32(pcidev->bus, pcidev->dev, pcidev->func, offset, value);
131 }
132
133 /* Gets any old raw bar. */
134 uint32_t pci_getbar(struct pci_device *pcidev, unsigned int bar)
135 {
136         uint32_t value, type;
137         if (bar > 5)
138                 panic("Nonexistant bar requested!");
139         value = pcidev_read32(pcidev, PCI_HEADER_REG);
140         type = (value >> 16) & 0xff;
141         /* Only types 0 and 1 have BARS */
142         if ((type != 0x00) && (type != 0x01))
143                 return 0;
144         /* Only type 0 has BAR2 - BAR5 */
145         if ((bar > 1) && (type != 0x00))
146                 return 0;
147         return pcidev_read32(pcidev, PCI_BAR0_STD + bar * PCI_BAR_OFF);
148 }
149
150 /* Determines if a given bar is IO (o/w, it's mem) */
151 bool pci_is_iobar(uint32_t bar)
152 {
153         return bar & PCI_BAR_IO;
154 }
155
156 /* Helper to get the address from a membar.  Check the type beforehand */
157 uint32_t pci_getmembar32(uint32_t bar)
158 {
159         uint8_t type = bar & PCI_MEMBAR_TYPE;
160         if (type != PCI_MEMBAR_32BIT) {
161                 warn("Unhandled PCI membar type: %02p\n", type >> 1);
162                 return 0;
163         }
164         return bar & 0xfffffff0;
165 }
166
167 /* Helper to get the address from an IObar.  Check the type beforehand */
168 uint32_t pci_getiobar32(uint32_t bar)
169 {
170         return bar & 0xfffffffc;
171 }
172
173 /* Helper to get the class description strings.  Adapted from
174  * http://www.pcidatabase.com/reports.php?type=c-header */
175 static void pcidev_get_cldesc(struct pci_device *pcidev, char **class,
176                               char **subclass, char **progif)
177 {
178         int     i ;
179         *class = *subclass = *progif = "";
180
181         for (i = 0; i < PCI_CLASSCODETABLE_LEN; i++) {
182                 if (PciClassCodeTable[i].BaseClass == pcidev->class) {
183                         if (!(**class))
184                                 *class = PciClassCodeTable[i].BaseDesc;
185                         if (PciClassCodeTable[i].SubClass == pcidev->subclass) {
186                                 if (!(**subclass))
187                                         *subclass = PciClassCodeTable[i].SubDesc;
188                                 if (PciClassCodeTable[i].ProgIf == pcidev->progif) {
189                                         *progif = PciClassCodeTable[i].ProgDesc;
190                                         break ;
191                                 }
192                         }
193                 }
194         }
195 }
196
197 /* Helper to get the vendor and device description strings */
198 static void pcidev_get_devdesc(struct pci_device *pcidev, char **vend_short,
199                                char **vend_full, char **chip, char **chip_desc)
200 {
201         int     i ;
202         *vend_short = *vend_full = *chip = *chip_desc = "";
203
204         for (i = 0; i < PCI_VENTABLE_LEN; i++) {
205                 if (PciVenTable[i].VenId == pcidev->ven_id) {
206                         *vend_short = PciVenTable[i].VenShort;
207                         *vend_full = PciVenTable[i].VenFull;
208                         break ;
209                 }
210         }
211         for (i = 0; i < PCI_DEVTABLE_LEN; i++) {
212                 if ((PciDevTable[i].VenId == pcidev->ven_id) &&
213                    (PciDevTable[i].DevId == pcidev->dev_id)) {
214                         *chip = PciDevTable[i].Chip;
215                         *chip_desc = PciDevTable[i].ChipDesc;
216                         break ;
217                 }
218         }
219 }
220
221 /* Prints info (like lspci) for a device */
222 void pcidev_print_info(struct pci_device *pcidev, int verbosity)
223 {
224         char *ven_sht, *ven_fl, *chip, *chip_txt, *class, *subcl, *progif;
225         pcidev_get_cldesc(pcidev, &class, &subcl, &progif);
226         pcidev_get_devdesc(pcidev, &ven_sht, &ven_fl, &chip, &chip_txt);
227
228         printk("%02x:%02x.%x %s: %s %s %s\n",
229                pcidev->bus,
230                pcidev->dev,
231                pcidev->func,
232                subcl,
233                ven_sht,
234                chip,
235                chip_txt);
236         if (verbosity > 1)
237                 printk("        IRQ: %02d IRQ pin: %02p\n",
238                        pcidev->irqline,
239                        pcidev->irqpin);
240         if (verbosity > 2)
241                 printk("        Vendor Id: %04p Device Id: %04p\n",
242                        pcidev->ven_id,
243                        pcidev->dev_id);
244         if (verbosity > 3)
245                 printk("        %s %s %s\n",
246                        class,
247                        progif,
248                        ven_fl);
249 }