Convert pci_find_device/class to use 'struct pci_device'.
[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 struct pci_device *
168 pci_find_device(u16 vendid, u16 devid)
169 {
170     struct pci_device *pci;
171     foreachpci(pci) {
172         if (pci->vendor == vendid && pci->device == devid)
173             return pci;
174     }
175     return NULL;
176 }
177
178 // Search for a device with the specified class id.
179 struct pci_device *
180 pci_find_class(u16 classid)
181 {
182     struct pci_device *pci;
183     foreachpci(pci) {
184         if (pci->class == classid)
185             return pci;
186     }
187     return NULL;
188 }
189
190 int pci_init_device(const struct pci_device_id *ids
191                     , struct pci_device *pci, void *arg)
192 {
193     while (ids->vendid || ids->class_mask) {
194         if ((ids->vendid == PCI_ANY_ID || ids->vendid == pci->vendor) &&
195             (ids->devid == PCI_ANY_ID || ids->devid == pci->device) &&
196             !((ids->class ^ pci->class) & ids->class_mask)) {
197             if (ids->func)
198                 ids->func(pci, arg);
199             return 0;
200         }
201         ids++;
202     }
203     return -1;
204 }
205
206 struct pci_device *
207 pci_find_init_device(const struct pci_device_id *ids, void *arg)
208 {
209     struct pci_device *pci;
210     foreachpci(pci) {
211         if (pci_init_device(ids, pci, arg) == 0)
212             return pci;
213     }
214     return NULL;
215 }
216
217 void
218 pci_reboot(void)
219 {
220     u8 v = inb(PORT_PCI_REBOOT) & ~6;
221     outb(v|2, PORT_PCI_REBOOT); /* Request hard reset */
222     udelay(50);
223     outb(v|6, PORT_PCI_REBOOT); /* Actually do the reset */
224     udelay(50);
225 }
226
227 // helper functions to access pci mmio bars from real mode
228
229 u32 VISIBLE32FLAT
230 pci_readl_32(u32 addr)
231 {
232     dprintf(3, "32: pci read : %x\n", addr);
233     return readl((void*)addr);
234 }
235
236 u32 pci_readl(u32 addr)
237 {
238     if (MODESEGMENT) {
239         dprintf(3, "16: pci read : %x\n", addr);
240         extern void _cfunc32flat_pci_readl_32(u32 addr);
241         return call32(_cfunc32flat_pci_readl_32, addr, -1);
242     } else {
243         return pci_readl_32(addr);
244     }
245 }
246
247 struct reg32 {
248     u32 addr;
249     u32 data;
250 };
251
252 void VISIBLE32FLAT
253 pci_writel_32(struct reg32 *reg32)
254 {
255     dprintf(3, "32: pci write: %x, %x (%p)\n", reg32->addr, reg32->data, reg32);
256     writel((void*)(reg32->addr), reg32->data);
257 }
258
259 void pci_writel(u32 addr, u32 val)
260 {
261     struct reg32 reg32 = { .addr = addr, .data = val };
262     if (MODESEGMENT) {
263         dprintf(3, "16: pci write: %x, %x (%x:%p)\n",
264                 reg32.addr, reg32.data, GET_SEG(SS), &reg32);
265         void *flatptr = MAKE_FLATPTR(GET_SEG(SS), &reg32);
266         extern void _cfunc32flat_pci_writel_32(struct reg32 *reg32);
267         call32(_cfunc32flat_pci_writel_32, (u32)flatptr, -1);
268     } else {
269         pci_writel_32(&reg32);
270     }
271 }