grml...
[seabios.git] / src / pci.c
1 // PCI config space access functions.
2 //
3 // Copyright (C) 2008  Kevin O'Connor <kevin@koconnor.net>
4 // Copyright (C) 2002  MandrakeSoft S.A.
5 //
6 // This file may be distributed under the terms of the GNU LGPLv3 license.
7
8 #include "pci.h" // pci_config_writel
9 #include "ioport.h" // outl
10 #include "util.h" // dprintf
11 #include "paravirt.h" // romfile_loadint
12 #include "farptr.h" // MAKE_FLATPTR
13 #include "pci_regs.h" // PCI_VENDOR_ID
14 #include "pci_ids.h" // PCI_CLASS_DISPLAY_VGA
15
16 void pci_config_writel(u16 bdf, u32 addr, u32 val)
17 {
18     outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
19     outl(val, PORT_PCI_DATA);
20 }
21
22 void pci_config_writew(u16 bdf, u32 addr, u16 val)
23 {
24     outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
25     outw(val, PORT_PCI_DATA + (addr & 2));
26 }
27
28 void pci_config_writeb(u16 bdf, u32 addr, u8 val)
29 {
30     outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
31     outb(val, PORT_PCI_DATA + (addr & 3));
32 }
33
34 u32 pci_config_readl(u16 bdf, u32 addr)
35 {
36     outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
37     return inl(PORT_PCI_DATA);
38 }
39
40 u16 pci_config_readw(u16 bdf, u32 addr)
41 {
42     outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
43     return inw(PORT_PCI_DATA + (addr & 2));
44 }
45
46 u8 pci_config_readb(u16 bdf, u32 addr)
47 {
48     outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
49     return inb(PORT_PCI_DATA + (addr & 3));
50 }
51
52 void
53 pci_config_maskw(u16 bdf, u32 addr, u16 off, u16 on)
54 {
55     u16 val = pci_config_readw(bdf, addr);
56     val = (val & ~off) | on;
57     pci_config_writew(bdf, addr, val);
58 }
59
60 // Helper function for foreachbdf() macro - return next device
61 int
62 pci_next(int bdf, int bus)
63 {
64     if (pci_bdf_to_fn(bdf) == 0
65         && (pci_config_readb(bdf, PCI_HEADER_TYPE) & 0x80) == 0)
66         // Last found device wasn't a multi-function device - skip to
67         // the next device.
68         bdf += 8;
69     else
70         bdf += 1;
71
72     for (;;) {
73         if (pci_bdf_to_bus(bdf) != bus)
74             return -1;
75
76         u16 v = pci_config_readw(bdf, PCI_VENDOR_ID);
77         if (v != 0x0000 && v != 0xffff)
78             // Device is present.
79             return bdf;
80
81         if (pci_bdf_to_fn(bdf) == 0)
82             bdf += 8;
83         else
84             bdf += 1;
85     }
86 }
87
88 struct pci_device *PCIDevices;
89 int MaxPCIBus VAR16VISIBLE;
90
91 // Check if PCI is available at all
92 int
93 pci_probe_host(void)
94 {
95     outl(0x80000000, PORT_PCI_CMD);
96     if (inl(PORT_PCI_CMD) != 0x80000000) {
97         dprintf(1, "Detected non-PCI system\n");
98         return -1;
99     }
100     return 0;
101 }
102
103 // Find all PCI devices and populate PCIDevices linked list.
104 void
105 pci_probe_devices(void)
106 {
107     dprintf(3, "PCI probe\n");
108     struct pci_device *busdevs[256];
109     memset(busdevs, 0, sizeof(busdevs));
110     struct pci_device **pprev = &PCIDevices;
111     int extraroots = romfile_loadint("etc/extra-pci-roots", 0);
112     int bus = -1, lastbus = 0, rootbuses = 0, count=0;
113     while (bus < 0xff && (bus < MaxPCIBus || rootbuses < extraroots)) {
114         bus++;
115         int bdf;
116         foreachbdf(bdf, bus) {
117             // Create new pci_device struct and add to list.
118             struct pci_device *dev = malloc_tmp(sizeof(*dev));
119             if (!dev) {
120                 warn_noalloc();
121                 return;
122             }
123             memset(dev, 0, sizeof(*dev));
124             *pprev = dev;
125             pprev = &dev->next;
126             count++;
127
128             // Find parent device.
129             int rootbus;
130             struct pci_device *parent = busdevs[bus];
131             if (!parent) {
132                 if (bus != lastbus)
133                     rootbuses++;
134                 lastbus = bus;
135                 rootbus = rootbuses;
136                 if (bus > MaxPCIBus)
137                     MaxPCIBus = bus;
138             } else {
139                 rootbus = parent->rootbus;
140             }
141
142             // Populate pci_device info.
143             dev->bdf = bdf;
144             dev->parent = parent;
145             dev->rootbus = rootbus;
146             u32 vendev = pci_config_readl(bdf, PCI_VENDOR_ID);
147             dev->vendor = vendev & 0xffff;
148             dev->device = vendev >> 16;
149             u32 classrev = pci_config_readl(bdf, PCI_CLASS_REVISION);
150             dev->class = classrev >> 16;
151             dev->prog_if = classrev >> 8;
152             dev->revision = classrev & 0xff;
153             dev->header_type = pci_config_readb(bdf, PCI_HEADER_TYPE);
154             u8 v = dev->header_type & 0x7f;
155             if (v == PCI_HEADER_TYPE_BRIDGE || v == PCI_HEADER_TYPE_CARDBUS) {
156                 u8 secbus = pci_config_readb(bdf, PCI_SECONDARY_BUS);
157                 dev->secondary_bus = secbus;
158                 if (secbus > bus && !busdevs[secbus])
159                     busdevs[secbus] = dev;
160                 if (secbus > MaxPCIBus)
161                     MaxPCIBus = secbus;
162             }
163             dprintf(4, "PCI device %02x:%02x.%x (vd=%04x:%04x c=%04x)\n"
164                     , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)
165                     , pci_bdf_to_fn(bdf)
166                     , dev->vendor, dev->device, dev->class);
167         }
168     }
169     dprintf(1, "Found %d PCI devices (max PCI bus is %02x)\n", count, MaxPCIBus);
170 }
171
172 // Search for a device with the specified vendor and device ids.
173 struct pci_device *
174 pci_find_device(u16 vendid, u16 devid)
175 {
176     struct pci_device *pci;
177     foreachpci(pci) {
178         if (pci->vendor == vendid && pci->device == devid)
179             return pci;
180     }
181     return NULL;
182 }
183
184 // Search for a device with the specified class id.
185 struct pci_device *
186 pci_find_class(u16 classid)
187 {
188     struct pci_device *pci;
189     foreachpci(pci) {
190         if (pci->class == classid)
191             return pci;
192     }
193     return NULL;
194 }
195
196 int pci_init_device(const struct pci_device_id *ids
197                     , struct pci_device *pci, void *arg)
198 {
199     while (ids->vendid || ids->class_mask) {
200         if ((ids->vendid == PCI_ANY_ID || ids->vendid == pci->vendor) &&
201             (ids->devid == PCI_ANY_ID || ids->devid == pci->device) &&
202             !((ids->class ^ pci->class) & ids->class_mask)) {
203             if (ids->func)
204                 ids->func(pci, arg);
205             return 0;
206         }
207         ids++;
208     }
209     return -1;
210 }
211
212 struct pci_device *
213 pci_find_init_device(const struct pci_device_id *ids, void *arg)
214 {
215     struct pci_device *pci;
216     foreachpci(pci) {
217         if (pci_init_device(ids, pci, arg) == 0)
218             return pci;
219     }
220     return NULL;
221 }
222
223 void
224 pci_reboot(void)
225 {
226     u8 v = inb(PORT_PCI_REBOOT) & ~6;
227     outb(v|2, PORT_PCI_REBOOT); /* Request hard reset */
228     udelay(50);
229     outb(v|6, PORT_PCI_REBOOT); /* Actually do the reset */
230     udelay(50);
231 }
232
233 // helper functions to access pci mmio bars from real mode
234
235 u32 VISIBLE32FLAT
236 pci_readl_32(u32 addr)
237 {
238     dprintf(3, "32: pci read : %x\n", addr);
239     return readl((void*)addr);
240 }
241
242 u32 pci_readl(u32 addr)
243 {
244     if (MODESEGMENT) {
245         dprintf(3, "16: pci read : %x\n", addr);
246         extern void _cfunc32flat_pci_readl_32(u32 addr);
247         return call32(_cfunc32flat_pci_readl_32, addr, -1);
248     } else {
249         return pci_readl_32(addr);
250     }
251 }
252
253 struct reg32 {
254     u32 addr;
255     u32 data;
256 };
257
258 void VISIBLE32FLAT
259 pci_writel_32(struct reg32 *reg32)
260 {
261     dprintf(3, "32: pci write: %x, %x (%p)\n", reg32->addr, reg32->data, reg32);
262     writel((void*)(reg32->addr), reg32->data);
263 }
264
265 void pci_writel(u32 addr, u32 val)
266 {
267     struct reg32 reg32 = { .addr = addr, .data = val };
268     if (MODESEGMENT) {
269         dprintf(3, "16: pci write: %x, %x (%x:%p)\n",
270                 reg32.addr, reg32.data, GET_SEG(SS), &reg32);
271         void *flatptr = MAKE_FLATPTR(GET_SEG(SS), &reg32);
272         extern void _cfunc32flat_pci_writel_32(struct reg32 *reg32);
273         call32(_cfunc32flat_pci_writel_32, (u32)flatptr, -1);
274     } else {
275         pci_writel_32(&reg32);
276     }
277 }