blob: dc62c5c231296cc8732829ec9399dd36053d68e3 [file] [log] [blame]
Kevin O'Connor061d1372008-06-11 22:39:46 -04001// PCI config space access functions.
2//
3// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
4// Copyright (C) 2002 MandrakeSoft S.A.
5//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05006// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connor061d1372008-06-11 22:39:46 -04007
Kevin O'Connor59d6ca52012-05-31 00:20:55 -04008#include "config.h" // CONFIG_*
Kevin O'Connore6338322008-11-29 20:31:49 -05009#include "pci.h" // pci_config_writel
Kevin O'Connora0dc2962008-03-16 14:29:32 -040010#include "ioport.h" // outl
Kevin O'Connordb03d5d2008-06-21 11:55:29 -040011#include "util.h" // dprintf
Kevin O'Connorb044e772011-07-05 20:40:11 -040012#include "farptr.h" // MAKE_FLATPTR
Kevin O'Connor2ed2f582008-11-08 15:53:36 -050013#include "pci_regs.h" // PCI_VENDOR_ID
Kevin O'Connor04eece22009-07-19 19:05:30 -040014#include "pci_ids.h" // PCI_CLASS_DISPLAY_VGA
Kevin O'Connora0dc2962008-03-16 14:29:32 -040015
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050016void pci_config_writel(u16 bdf, u32 addr, u32 val)
Kevin O'Connora0dc2962008-03-16 14:29:32 -040017{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050018 outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
Kevin O'Connor0f803e42008-05-24 23:07:16 -040019 outl(val, PORT_PCI_DATA);
Kevin O'Connora0dc2962008-03-16 14:29:32 -040020}
21
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050022void pci_config_writew(u16 bdf, u32 addr, u16 val)
Kevin O'Connora0dc2962008-03-16 14:29:32 -040023{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050024 outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
Kevin O'Connor0f803e42008-05-24 23:07:16 -040025 outw(val, PORT_PCI_DATA + (addr & 2));
Kevin O'Connora0dc2962008-03-16 14:29:32 -040026}
27
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050028void pci_config_writeb(u16 bdf, u32 addr, u8 val)
Kevin O'Connora0dc2962008-03-16 14:29:32 -040029{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050030 outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
Kevin O'Connor0f803e42008-05-24 23:07:16 -040031 outb(val, PORT_PCI_DATA + (addr & 3));
Kevin O'Connora0dc2962008-03-16 14:29:32 -040032}
33
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050034u32 pci_config_readl(u16 bdf, u32 addr)
Kevin O'Connora0dc2962008-03-16 14:29:32 -040035{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050036 outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
Kevin O'Connor0f803e42008-05-24 23:07:16 -040037 return inl(PORT_PCI_DATA);
Kevin O'Connora0dc2962008-03-16 14:29:32 -040038}
39
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050040u16 pci_config_readw(u16 bdf, u32 addr)
Kevin O'Connora0dc2962008-03-16 14:29:32 -040041{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050042 outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
Kevin O'Connor0f803e42008-05-24 23:07:16 -040043 return inw(PORT_PCI_DATA + (addr & 2));
Kevin O'Connora0dc2962008-03-16 14:29:32 -040044}
45
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050046u8 pci_config_readb(u16 bdf, u32 addr)
Kevin O'Connora0dc2962008-03-16 14:29:32 -040047{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050048 outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
Kevin O'Connor0f803e42008-05-24 23:07:16 -040049 return inb(PORT_PCI_DATA + (addr & 3));
50}
51
Kevin O'Connor59f02832009-10-12 10:09:15 -040052void
53pci_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
Kevin O'Connorbaac6b62011-06-19 10:46:28 -040060// Helper function for foreachbdf() macro - return next device
Kevin O'Connore6338322008-11-29 20:31:49 -050061int
Kevin O'Connor2b333e42011-07-02 14:49:41 -040062pci_next(int bdf, int bus)
Kevin O'Connor0f803e42008-05-24 23:07:16 -040063{
Kevin O'Connor2b333e42011-07-02 14:49:41 -040064 if (pci_bdf_to_fn(bdf) == 0
65 && (pci_config_readb(bdf, PCI_HEADER_TYPE) & 0x80) == 0)
Kevin O'Connore6338322008-11-29 20:31:49 -050066 // Last found device wasn't a multi-function device - skip to
67 // the next device.
Kevin O'Connor2b333e42011-07-02 14:49:41 -040068 bdf += 8;
69 else
70 bdf += 1;
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050071
Kevin O'Connore6338322008-11-29 20:31:49 -050072 for (;;) {
Kevin O'Connor2b333e42011-07-02 14:49:41 -040073 if (pci_bdf_to_bus(bdf) != bus)
74 return -1;
Kevin O'Connore6338322008-11-29 20:31:49 -050075
76 u16 v = pci_config_readw(bdf, PCI_VENDOR_ID);
77 if (v != 0x0000 && v != 0xffff)
78 // Device is present.
Kevin O'Connor2b333e42011-07-02 14:49:41 -040079 return bdf;
Kevin O'Connore6338322008-11-29 20:31:49 -050080
81 if (pci_bdf_to_fn(bdf) == 0)
82 bdf += 8;
83 else
84 bdf += 1;
85 }
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050086}
87
Kevin O'Connorb98a4b12013-06-08 21:53:36 -040088struct hlist_head PCIDevices VARVERIFY32INIT;
Kevin O'Connor89a2f962013-02-18 23:36:03 -050089int MaxPCIBus VARFSEG;
Kevin O'Connor096a9b12011-06-19 14:09:20 -040090
Jan Kiszka58e6b3f2011-09-21 08:16:21 +020091// Check if PCI is available at all
92int
93pci_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
Kevin O'Connor0f654a92011-07-02 14:32:11 -0400103// Find all PCI devices and populate PCIDevices linked list.
Kevin O'Connor096a9b12011-06-19 14:09:20 -0400104void
Jan Kiszka58e6b3f2011-09-21 08:16:21 +0200105pci_probe_devices(void)
Kevin O'Connor096a9b12011-06-19 14:09:20 -0400106{
Kevin O'Connor0f654a92011-07-02 14:32:11 -0400107 dprintf(3, "PCI probe\n");
Kevin O'Connor096a9b12011-06-19 14:09:20 -0400108 struct pci_device *busdevs[256];
109 memset(busdevs, 0, sizeof(busdevs));
Kevin O'Connorb98a4b12013-06-08 21:53:36 -0400110 struct hlist_node **pprev = &PCIDevices.first;
Kevin O'Connorb044e772011-07-05 20:40:11 -0400111 int extraroots = romfile_loadint("etc/extra-pci-roots", 0);
Kevin O'Connor0f654a92011-07-02 14:32:11 -0400112 int bus = -1, lastbus = 0, rootbuses = 0, count=0;
Kevin O'Connorb044e772011-07-05 20:40:11 -0400113 while (bus < 0xff && (bus < MaxPCIBus || rootbuses < extraroots)) {
Kevin O'Connor0f654a92011-07-02 14:32:11 -0400114 bus++;
Kevin O'Connor2b333e42011-07-02 14:49:41 -0400115 int bdf;
116 foreachbdf(bdf, bus) {
Kevin O'Connor0f654a92011-07-02 14:32:11 -0400117 // 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));
Kevin O'Connorb98a4b12013-06-08 21:53:36 -0400124 hlist_add(&dev->node, pprev);
Kevin O'Connor2a9aeab2013-07-14 13:55:52 -0400125 pprev = &dev->node.next;
Kevin O'Connor0f654a92011-07-02 14:32:11 -0400126 count++;
Kevin O'Connor096a9b12011-06-19 14:09:20 -0400127
Kevin O'Connor0f654a92011-07-02 14:32:11 -0400128 // 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;
Kevin O'Connor3076cfb2011-07-02 18:39:03 -0400136 if (bus > MaxPCIBus)
137 MaxPCIBus = bus;
Kevin O'Connor0f654a92011-07-02 14:32:11 -0400138 } else {
139 rootbus = parent->rootbus;
140 }
Kevin O'Connor096a9b12011-06-19 14:09:20 -0400141
Kevin O'Connor0f654a92011-07-02 14:32:11 -0400142 // 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);
Kevin O'Connor096a9b12011-06-19 14:09:20 -0400167 }
168 }
Kevin O'Connor0f654a92011-07-02 14:32:11 -0400169 dprintf(1, "Found %d PCI devices (max PCI bus is %02x)\n", count, MaxPCIBus);
Kevin O'Connor096a9b12011-06-19 14:09:20 -0400170}
171
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -0500172// Search for a device with the specified vendor and device ids.
Kevin O'Connor0cd70052011-07-02 14:04:19 -0400173struct pci_device *
Kevin O'Connor4132e022008-12-04 19:39:10 -0500174pci_find_device(u16 vendid, u16 devid)
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -0500175{
Kevin O'Connor0cd70052011-07-02 14:04:19 -0400176 struct pci_device *pci;
177 foreachpci(pci) {
178 if (pci->vendor == vendid && pci->device == devid)
179 return pci;
Kevin O'Connor0f803e42008-05-24 23:07:16 -0400180 }
Kevin O'Connor0cd70052011-07-02 14:04:19 -0400181 return NULL;
Kevin O'Connor0f803e42008-05-24 23:07:16 -0400182}
183
Kevin O'Connor5fdaa032008-08-31 11:06:27 -0400184// Search for a device with the specified class id.
Kevin O'Connor0cd70052011-07-02 14:04:19 -0400185struct pci_device *
Kevin O'Connor4132e022008-12-04 19:39:10 -0500186pci_find_class(u16 classid)
Kevin O'Connor5fdaa032008-08-31 11:06:27 -0400187{
Kevin O'Connor0cd70052011-07-02 14:04:19 -0400188 struct pci_device *pci;
189 foreachpci(pci) {
190 if (pci->class == classid)
191 return pci;
Kevin O'Connor0f803e42008-05-24 23:07:16 -0400192 }
Kevin O'Connor0cd70052011-07-02 14:04:19 -0400193 return NULL;
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400194}
Isaku Yamahata968d3a82010-07-07 12:14:01 +0900195
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400196int pci_init_device(const struct pci_device_id *ids
197 , struct pci_device *pci, void *arg)
Isaku Yamahata968d3a82010-07-07 12:14:01 +0900198{
Isaku Yamahata968d3a82010-07-07 12:14:01 +0900199 while (ids->vendid || ids->class_mask) {
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400200 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);
Isaku Yamahata968d3a82010-07-07 12:14:01 +0900205 return 0;
206 }
207 ids++;
208 }
209 return -1;
210}
Isaku Yamahata23173ac2010-07-20 16:37:15 +0900211
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400212struct pci_device *
213pci_find_init_device(const struct pci_device_id *ids, void *arg)
Isaku Yamahata23173ac2010-07-20 16:37:15 +0900214{
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400215 struct pci_device *pci;
216 foreachpci(pci) {
217 if (pci_init_device(ids, pci, arg) == 0)
218 return pci;
Isaku Yamahata23173ac2010-07-20 16:37:15 +0900219 }
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400220 return NULL;
Isaku Yamahata23173ac2010-07-20 16:37:15 +0900221}
Kevin O'Connoradaf3732010-09-13 20:22:07 -0400222
223void
224pci_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}
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100232
233// helper functions to access pci mmio bars from real mode
234
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100235u32 VISIBLE32FLAT
236pci_readl_32(u32 addr)
237{
Kevin O'Connorbf079e12012-03-11 11:19:52 -0400238 dprintf(9, "32: pci read : %x\n", addr);
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100239 return readl((void*)addr);
240}
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100241
242u32 pci_readl(u32 addr)
243{
244 if (MODESEGMENT) {
Kevin O'Connorbf079e12012-03-11 11:19:52 -0400245 dprintf(9, "16: pci read : %x\n", addr);
Kevin O'Connorf3fe3aa2010-12-05 12:38:33 -0500246 extern void _cfunc32flat_pci_readl_32(u32 addr);
247 return call32(_cfunc32flat_pci_readl_32, addr, -1);
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100248 } else {
249 return pci_readl_32(addr);
250 }
251}
252
253struct reg32 {
254 u32 addr;
255 u32 data;
256};
257
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100258void VISIBLE32FLAT
259pci_writel_32(struct reg32 *reg32)
260{
Kevin O'Connorbf079e12012-03-11 11:19:52 -0400261 dprintf(9, "32: pci write: %x, %x (%p)\n", reg32->addr, reg32->data, reg32);
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100262 writel((void*)(reg32->addr), reg32->data);
263}
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100264
265void pci_writel(u32 addr, u32 val)
266{
267 struct reg32 reg32 = { .addr = addr, .data = val };
268 if (MODESEGMENT) {
Kevin O'Connorbf079e12012-03-11 11:19:52 -0400269 dprintf(9, "16: pci write: %x, %x (%x:%p)\n",
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100270 reg32.addr, reg32.data, GET_SEG(SS), &reg32);
271 void *flatptr = MAKE_FLATPTR(GET_SEG(SS), &reg32);
Kevin O'Connorf3fe3aa2010-12-05 12:38:33 -0500272 extern void _cfunc32flat_pci_writel_32(struct reg32 *reg32);
273 call32(_cfunc32flat_pci_writel_32, (u32)flatptr, -1);
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100274 } else {
275 pci_writel_32(&reg32);
276 }
277}