9a696b9052394a943ac62a486f8b94041bce89df
[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 "config.h" // CONFIG_*
12 #include "farptr.h" // CONFIG_*
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 *pmax)
63 {
64     if (pci_bdf_to_fn(bdf) == 1
65         && (pci_config_readb(bdf-1, PCI_HEADER_TYPE) & 0x80) == 0)
66         // Last found device wasn't a multi-function device - skip to
67         // the next device.
68         bdf += 7;
69
70     int max = *pmax;
71     for (;;) {
72         if (bdf >= max) {
73             if (CONFIG_PCI_ROOT1 && bdf <= (CONFIG_PCI_ROOT1 << 8))
74                 bdf = CONFIG_PCI_ROOT1 << 8;
75             else if (CONFIG_PCI_ROOT2 && bdf <= (CONFIG_PCI_ROOT2 << 8))
76                 bdf = CONFIG_PCI_ROOT2 << 8;
77             else
78                 return -1;
79             *pmax = max = bdf + 0x0100;
80         }
81
82         u16 v = pci_config_readw(bdf, PCI_VENDOR_ID);
83         if (v != 0x0000 && v != 0xffff)
84             // Device is present.
85             break;
86
87         if (pci_bdf_to_fn(bdf) == 0)
88             bdf += 8;
89         else
90             bdf += 1;
91     }
92
93     // Check if found device is a bridge.
94     u32 v = pci_config_readb(bdf, PCI_HEADER_TYPE);
95     v &= 0x7f;
96     if (v == PCI_HEADER_TYPE_BRIDGE || v == PCI_HEADER_TYPE_CARDBUS) {
97         v = pci_config_readl(bdf, PCI_PRIMARY_BUS);
98         int newmax = (v & 0xff00) + 0x0100;
99         if (newmax > max)
100             *pmax = newmax;
101     }
102
103     return bdf;
104 }
105
106 struct pci_device *PCIDevices;
107 int MaxPCIBus;
108
109 void
110 pci_probe(void)
111 {
112     int rootbuses = 0;
113     struct pci_device *busdevs[256];
114     memset(busdevs, 0, sizeof(busdevs));
115
116     struct pci_device **pprev = &PCIDevices;
117     u8 lastbus = 0;
118     int bdf, max;
119     foreachbdf(bdf, max) {
120         // Create new pci_device struct and add to list.
121         struct pci_device *dev = malloc_tmp(sizeof(*dev));
122         if (!dev) {
123             warn_noalloc();
124             return;
125         }
126         memset(dev, 0, sizeof(*dev));
127         *pprev = dev;
128         pprev = &dev->next;
129
130         // Find parent device.
131         u8 bus = pci_bdf_to_bus(bdf), rootbus;
132         struct pci_device *parent = busdevs[bus];
133         if (!parent) {
134             if (bus != lastbus)
135                 rootbuses++;
136             lastbus = bus;
137             rootbus = rootbuses;
138         } else {
139             rootbus = parent->rootbus;
140         }
141         if (bus > MaxPCIBus)
142             MaxPCIBus = bus;
143
144         // Populate pci_device info.
145         dev->bdf = bdf;
146         dev->parent = parent;
147         dev->rootbus = rootbus;
148         u32 vendev = pci_config_readl(bdf, PCI_VENDOR_ID);
149         dev->vendor = vendev & 0xffff;
150         dev->device = vendev >> 16;
151         u32 classrev = pci_config_readl(bdf, PCI_CLASS_REVISION);
152         dev->class = classrev >> 16;
153         dev->prog_if = classrev >> 8;
154         dev->revision = classrev & 0xff;
155         dev->header_type = pci_config_readb(bdf, PCI_HEADER_TYPE);
156         u8 v = dev->header_type & 0x7f;
157         if (v == PCI_HEADER_TYPE_BRIDGE || v == PCI_HEADER_TYPE_CARDBUS) {
158             u8 secbus = pci_config_readb(bdf, PCI_SECONDARY_BUS);
159             dev->secondary_bus = secbus;
160             if (secbus > bus && !busdevs[secbus])
161                 busdevs[secbus] = dev;
162         }
163     }
164 }
165
166 // Search for a device with the specified vendor and device ids.
167 int
168 pci_find_device(u16 vendid, u16 devid)
169 {
170     u32 id = (devid << 16) | vendid;
171     int bdf, max;
172     foreachbdf(bdf, max) {
173         u32 v = pci_config_readl(bdf, PCI_VENDOR_ID);
174         if (v == id)
175             return bdf;
176     }
177     return -1;
178 }
179
180 // Search for a device with the specified class id.
181 int
182 pci_find_class(u16 classid)
183 {
184     int bdf, max;
185     foreachbdf(bdf, max) {
186         u16 v = pci_config_readw(bdf, PCI_CLASS_DEVICE);
187         if (v == classid)
188             return bdf;
189     }
190     return -1;
191 }
192
193 int *PCIpaths;
194
195 // Build the PCI path designations.
196 void
197 pci_path_setup(void)
198 {
199     PCIpaths = malloc_tmp(sizeof(*PCIpaths) * 256);
200     if (!PCIpaths)
201         return;
202     memset(PCIpaths, 0, sizeof(*PCIpaths) * 256);
203
204     int roots = 0;
205     int bdf, max;
206     foreachbdf(bdf, max) {
207         int bus = pci_bdf_to_bus(bdf);
208         if (! PCIpaths[bus])
209             PCIpaths[bus] = (roots++) | PP_ROOT;
210
211         // Check if found device is a bridge.
212         u32 v = pci_config_readb(bdf, PCI_HEADER_TYPE);
213         v &= 0x7f;
214         if (v == PCI_HEADER_TYPE_BRIDGE || v == PCI_HEADER_TYPE_CARDBUS) {
215             v = pci_config_readl(bdf, PCI_PRIMARY_BUS);
216             int childbus = (v >> 8) & 0xff;
217             if (childbus > bus)
218                 PCIpaths[childbus] = bdf | PP_PCIBRIDGE;
219         }
220     }
221 }
222
223 int pci_init_device(const struct pci_device_id *ids, u16 bdf, void *arg)
224 {
225     u16 vendor_id = pci_config_readw(bdf, PCI_VENDOR_ID);
226     u16 device_id = pci_config_readw(bdf, PCI_DEVICE_ID);
227     u16 class = pci_config_readw(bdf, PCI_CLASS_DEVICE);
228
229     while (ids->vendid || ids->class_mask) {
230         if ((ids->vendid == PCI_ANY_ID || ids->vendid == vendor_id) &&
231             (ids->devid == PCI_ANY_ID || ids->devid == device_id) &&
232             !((ids->class ^ class) & ids->class_mask)) {
233             if (ids->func) {
234                 ids->func(bdf, arg);
235             }
236             return 0;
237         }
238         ids++;
239     }
240     return -1;
241 }
242
243 int pci_find_init_device(const struct pci_device_id *ids, void *arg)
244 {
245     int bdf, max;
246
247     foreachbdf(bdf, max) {
248         if (pci_init_device(ids, bdf, arg) == 0) {
249             return bdf;
250         }
251     }
252     return -1;
253 }
254
255 void
256 pci_reboot(void)
257 {
258     u8 v = inb(PORT_PCI_REBOOT) & ~6;
259     outb(v|2, PORT_PCI_REBOOT); /* Request hard reset */
260     udelay(50);
261     outb(v|6, PORT_PCI_REBOOT); /* Actually do the reset */
262     udelay(50);
263 }
264
265 // helper functions to access pci mmio bars from real mode
266
267 u32 VISIBLE32FLAT
268 pci_readl_32(u32 addr)
269 {
270     dprintf(3, "32: pci read : %x\n", addr);
271     return readl((void*)addr);
272 }
273
274 u32 pci_readl(u32 addr)
275 {
276     if (MODESEGMENT) {
277         dprintf(3, "16: pci read : %x\n", addr);
278         extern void _cfunc32flat_pci_readl_32(u32 addr);
279         return call32(_cfunc32flat_pci_readl_32, addr, -1);
280     } else {
281         return pci_readl_32(addr);
282     }
283 }
284
285 struct reg32 {
286     u32 addr;
287     u32 data;
288 };
289
290 void VISIBLE32FLAT
291 pci_writel_32(struct reg32 *reg32)
292 {
293     dprintf(3, "32: pci write: %x, %x (%p)\n", reg32->addr, reg32->data, reg32);
294     writel((void*)(reg32->addr), reg32->data);
295 }
296
297 void pci_writel(u32 addr, u32 val)
298 {
299     struct reg32 reg32 = { .addr = addr, .data = val };
300     if (MODESEGMENT) {
301         dprintf(3, "16: pci write: %x, %x (%x:%p)\n",
302                 reg32.addr, reg32.data, GET_SEG(SS), &reg32);
303         void *flatptr = MAKE_FLATPTR(GET_SEG(SS), &reg32);
304         extern void _cfunc32flat_pci_writel_32(struct reg32 *reg32);
305         call32(_cfunc32flat_pci_writel_32, (u32)flatptr, -1);
306     } else {
307         pci_writel_32(&reg32);
308     }
309 }