pci: add helper functions for mmio bar access from real mode.
[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 foreachpci() 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 // Find a vga device with legacy address decoding enabled.
107 int
108 pci_find_vga(void)
109 {
110     int bdf = 0x0000, max = 0x0100;
111     for (;;) {
112         if (bdf >= max) {
113             if (CONFIG_PCI_ROOT1 && bdf <= (CONFIG_PCI_ROOT1 << 8))
114                 bdf = CONFIG_PCI_ROOT1 << 8;
115             else if (CONFIG_PCI_ROOT2 && bdf <= (CONFIG_PCI_ROOT2 << 8))
116                 bdf = CONFIG_PCI_ROOT2 << 8;
117             else
118                 return -1;
119             max = bdf + 0x0100;
120         }
121
122         u16 cls = pci_config_readw(bdf, PCI_CLASS_DEVICE);
123         if (cls == 0x0000 || cls == 0xffff) {
124             // Device not present.
125             if (pci_bdf_to_fn(bdf) == 0)
126                 bdf += 8;
127             else
128                 bdf += 1;
129             continue;
130         }
131         if (cls == PCI_CLASS_DISPLAY_VGA) {
132             u16 cmd = pci_config_readw(bdf, PCI_COMMAND);
133             if (cmd & PCI_COMMAND_IO && cmd & PCI_COMMAND_MEMORY)
134                 // Found active vga card
135                 return bdf;
136         }
137
138         // Check if device is a bridge.
139         u8 hdr = pci_config_readb(bdf, PCI_HEADER_TYPE);
140         u8 ht = hdr & 0x7f;
141         if (ht == PCI_HEADER_TYPE_BRIDGE || ht == PCI_HEADER_TYPE_CARDBUS) {
142             u32 ctrl = pci_config_readb(bdf, PCI_BRIDGE_CONTROL);
143             if (ctrl & PCI_BRIDGE_CTL_VGA) {
144                 // Found a VGA enabled bridge.
145                 u32 pbus = pci_config_readl(bdf, PCI_PRIMARY_BUS);
146                 bdf = (pbus & 0xff00);
147                 max = bdf + 0x100;
148                 continue;
149             }
150         }
151
152         if (pci_bdf_to_fn(bdf) == 0 && (hdr & 0x80) == 0)
153             // Last found device wasn't a multi-function device - skip to
154             // the next device.
155             bdf += 8;
156         else
157             bdf += 1;
158     }
159 }
160
161 // Search for a device with the specified vendor and device ids.
162 int
163 pci_find_device(u16 vendid, u16 devid)
164 {
165     u32 id = (devid << 16) | vendid;
166     int bdf, max;
167     foreachpci(bdf, max) {
168         u32 v = pci_config_readl(bdf, PCI_VENDOR_ID);
169         if (v == id)
170             return bdf;
171     }
172     return -1;
173 }
174
175 // Search for a device with the specified class id.
176 int
177 pci_find_class(u16 classid)
178 {
179     int bdf, max;
180     foreachpci(bdf, max) {
181         u16 v = pci_config_readw(bdf, PCI_CLASS_DEVICE);
182         if (v == classid)
183             return bdf;
184     }
185     return -1;
186 }
187
188 int pci_init_device(const struct pci_device_id *ids, u16 bdf, void *arg)
189 {
190     u16 vendor_id = pci_config_readw(bdf, PCI_VENDOR_ID);
191     u16 device_id = pci_config_readw(bdf, PCI_DEVICE_ID);
192     u16 class = pci_config_readw(bdf, PCI_CLASS_DEVICE);
193
194     while (ids->vendid || ids->class_mask) {
195         if ((ids->vendid == PCI_ANY_ID || ids->vendid == vendor_id) &&
196             (ids->devid == PCI_ANY_ID || ids->devid == device_id) &&
197             !((ids->class ^ class) & ids->class_mask)) {
198             if (ids->func) {
199                 ids->func(bdf, arg);
200             }
201             return 0;
202         }
203         ids++;
204     }
205     return -1;
206 }
207
208 int pci_find_init_device(const struct pci_device_id *ids, void *arg)
209 {
210     int bdf, max;
211
212     foreachpci(bdf, max) {
213         if (pci_init_device(ids, bdf, arg) == 0) {
214             return bdf;
215         }
216     }
217     return -1;
218 }
219
220 void
221 pci_reboot(void)
222 {
223     u8 v = inb(PORT_PCI_REBOOT) & ~6;
224     outb(v|2, PORT_PCI_REBOOT); /* Request hard reset */
225     udelay(50);
226     outb(v|6, PORT_PCI_REBOOT); /* Actually do the reset */
227     udelay(50);
228 }
229
230 // helper functions to access pci mmio bars from real mode
231
232 extern u32 pci_readl_32(u32 addr);
233 #if MODESEGMENT == 0
234 u32 VISIBLE32FLAT
235 pci_readl_32(u32 addr)
236 {
237     dprintf(3, "32: pci read : %x\n", addr);
238     return readl((void*)addr);
239 }
240 #endif
241
242 u32 pci_readl(u32 addr)
243 {
244     if (MODESEGMENT) {
245         dprintf(3, "16: pci read : %x\n", addr);
246         return call32(pci_readl_32, addr, -1);
247     } else {
248         return pci_readl_32(addr);
249     }
250 }
251
252 struct reg32 {
253     u32 addr;
254     u32 data;
255 };
256
257 extern void pci_writel_32(struct reg32 *reg32);
258 #if MODESEGMENT == 0
259 void VISIBLE32FLAT
260 pci_writel_32(struct reg32 *reg32)
261 {
262     dprintf(3, "32: pci write: %x, %x (%p)\n", reg32->addr, reg32->data, reg32);
263     writel((void*)(reg32->addr), reg32->data);
264 }
265 #endif
266
267 void pci_writel(u32 addr, u32 val)
268 {
269     struct reg32 reg32 = { .addr = addr, .data = val };
270     if (MODESEGMENT) {
271         dprintf(3, "16: pci write: %x, %x (%x:%p)\n",
272                 reg32.addr, reg32.data, GET_SEG(SS), &reg32);
273         void *flatptr = MAKE_FLATPTR(GET_SEG(SS), &reg32);
274         call32(pci_writel_32, (u32)flatptr, -1);
275     } else {
276         pci_writel_32(&reg32);
277     }
278 }