blob: f171d10741e33ce2391193cf3afc704559ab865c [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'Connor41639f82013-09-14 19:37:36 -040015#include "romfile.h" // romfile_loadint
Kevin O'Connor3df600b2013-09-14 19:28:55 -040016#include "stacks.h" // call32
Kevin O'Connorfa9c66a2013-09-14 19:10:40 -040017#include "string.h" // memset
Kevin O'Connorb9c6a962013-09-14 13:01:30 -040018#include "x86.h" // readl
Kevin O'Connora0dc2962008-03-16 14:29:32 -040019
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050020void pci_config_writel(u16 bdf, u32 addr, u32 val)
Kevin O'Connora0dc2962008-03-16 14:29:32 -040021{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050022 outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
Kevin O'Connor0f803e42008-05-24 23:07:16 -040023 outl(val, PORT_PCI_DATA);
Kevin O'Connora0dc2962008-03-16 14:29:32 -040024}
25
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050026void pci_config_writew(u16 bdf, u32 addr, u16 val)
Kevin O'Connora0dc2962008-03-16 14:29:32 -040027{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050028 outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
Kevin O'Connor0f803e42008-05-24 23:07:16 -040029 outw(val, PORT_PCI_DATA + (addr & 2));
Kevin O'Connora0dc2962008-03-16 14:29:32 -040030}
31
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050032void pci_config_writeb(u16 bdf, u32 addr, u8 val)
Kevin O'Connora0dc2962008-03-16 14:29:32 -040033{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050034 outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
Kevin O'Connor0f803e42008-05-24 23:07:16 -040035 outb(val, PORT_PCI_DATA + (addr & 3));
Kevin O'Connora0dc2962008-03-16 14:29:32 -040036}
37
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050038u32 pci_config_readl(u16 bdf, u32 addr)
Kevin O'Connora0dc2962008-03-16 14:29:32 -040039{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050040 outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
Kevin O'Connor0f803e42008-05-24 23:07:16 -040041 return inl(PORT_PCI_DATA);
Kevin O'Connora0dc2962008-03-16 14:29:32 -040042}
43
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050044u16 pci_config_readw(u16 bdf, u32 addr)
Kevin O'Connora0dc2962008-03-16 14:29:32 -040045{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050046 outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
Kevin O'Connor0f803e42008-05-24 23:07:16 -040047 return inw(PORT_PCI_DATA + (addr & 2));
Kevin O'Connora0dc2962008-03-16 14:29:32 -040048}
49
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050050u8 pci_config_readb(u16 bdf, u32 addr)
Kevin O'Connora0dc2962008-03-16 14:29:32 -040051{
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050052 outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
Kevin O'Connor0f803e42008-05-24 23:07:16 -040053 return inb(PORT_PCI_DATA + (addr & 3));
54}
55
Kevin O'Connor59f02832009-10-12 10:09:15 -040056void
57pci_config_maskw(u16 bdf, u32 addr, u16 off, u16 on)
58{
59 u16 val = pci_config_readw(bdf, addr);
60 val = (val & ~off) | on;
61 pci_config_writew(bdf, addr, val);
62}
63
Kevin O'Connorbaac6b62011-06-19 10:46:28 -040064// Helper function for foreachbdf() macro - return next device
Kevin O'Connore6338322008-11-29 20:31:49 -050065int
Kevin O'Connor2b333e42011-07-02 14:49:41 -040066pci_next(int bdf, int bus)
Kevin O'Connor0f803e42008-05-24 23:07:16 -040067{
Kevin O'Connor2b333e42011-07-02 14:49:41 -040068 if (pci_bdf_to_fn(bdf) == 0
69 && (pci_config_readb(bdf, PCI_HEADER_TYPE) & 0x80) == 0)
Kevin O'Connore6338322008-11-29 20:31:49 -050070 // Last found device wasn't a multi-function device - skip to
71 // the next device.
Kevin O'Connor2b333e42011-07-02 14:49:41 -040072 bdf += 8;
73 else
74 bdf += 1;
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050075
Kevin O'Connore6338322008-11-29 20:31:49 -050076 for (;;) {
Kevin O'Connor2b333e42011-07-02 14:49:41 -040077 if (pci_bdf_to_bus(bdf) != bus)
78 return -1;
Kevin O'Connore6338322008-11-29 20:31:49 -050079
80 u16 v = pci_config_readw(bdf, PCI_VENDOR_ID);
81 if (v != 0x0000 && v != 0xffff)
82 // Device is present.
Kevin O'Connor2b333e42011-07-02 14:49:41 -040083 return bdf;
Kevin O'Connore6338322008-11-29 20:31:49 -050084
85 if (pci_bdf_to_fn(bdf) == 0)
86 bdf += 8;
87 else
88 bdf += 1;
89 }
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050090}
91
Kevin O'Connorb98a4b12013-06-08 21:53:36 -040092struct hlist_head PCIDevices VARVERIFY32INIT;
Kevin O'Connor89a2f962013-02-18 23:36:03 -050093int MaxPCIBus VARFSEG;
Kevin O'Connor096a9b12011-06-19 14:09:20 -040094
Jan Kiszka58e6b3f2011-09-21 08:16:21 +020095// Check if PCI is available at all
96int
97pci_probe_host(void)
98{
99 outl(0x80000000, PORT_PCI_CMD);
100 if (inl(PORT_PCI_CMD) != 0x80000000) {
101 dprintf(1, "Detected non-PCI system\n");
102 return -1;
103 }
104 return 0;
105}
106
Kevin O'Connor0f654a92011-07-02 14:32:11 -0400107// Find all PCI devices and populate PCIDevices linked list.
Kevin O'Connor096a9b12011-06-19 14:09:20 -0400108void
Jan Kiszka58e6b3f2011-09-21 08:16:21 +0200109pci_probe_devices(void)
Kevin O'Connor096a9b12011-06-19 14:09:20 -0400110{
Kevin O'Connor0f654a92011-07-02 14:32:11 -0400111 dprintf(3, "PCI probe\n");
Kevin O'Connor096a9b12011-06-19 14:09:20 -0400112 struct pci_device *busdevs[256];
113 memset(busdevs, 0, sizeof(busdevs));
Kevin O'Connorb98a4b12013-06-08 21:53:36 -0400114 struct hlist_node **pprev = &PCIDevices.first;
Kevin O'Connorb044e772011-07-05 20:40:11 -0400115 int extraroots = romfile_loadint("etc/extra-pci-roots", 0);
Kevin O'Connor0f654a92011-07-02 14:32:11 -0400116 int bus = -1, lastbus = 0, rootbuses = 0, count=0;
Kevin O'Connorb044e772011-07-05 20:40:11 -0400117 while (bus < 0xff && (bus < MaxPCIBus || rootbuses < extraroots)) {
Kevin O'Connor0f654a92011-07-02 14:32:11 -0400118 bus++;
Kevin O'Connor2b333e42011-07-02 14:49:41 -0400119 int bdf;
120 foreachbdf(bdf, bus) {
Kevin O'Connor0f654a92011-07-02 14:32:11 -0400121 // Create new pci_device struct and add to list.
122 struct pci_device *dev = malloc_tmp(sizeof(*dev));
123 if (!dev) {
124 warn_noalloc();
125 return;
126 }
127 memset(dev, 0, sizeof(*dev));
Kevin O'Connorb98a4b12013-06-08 21:53:36 -0400128 hlist_add(&dev->node, pprev);
Kevin O'Connor2a9aeab2013-07-14 13:55:52 -0400129 pprev = &dev->node.next;
Kevin O'Connor0f654a92011-07-02 14:32:11 -0400130 count++;
Kevin O'Connor096a9b12011-06-19 14:09:20 -0400131
Kevin O'Connor0f654a92011-07-02 14:32:11 -0400132 // Find parent device.
133 int rootbus;
134 struct pci_device *parent = busdevs[bus];
135 if (!parent) {
136 if (bus != lastbus)
137 rootbuses++;
138 lastbus = bus;
139 rootbus = rootbuses;
Kevin O'Connor3076cfb2011-07-02 18:39:03 -0400140 if (bus > MaxPCIBus)
141 MaxPCIBus = bus;
Kevin O'Connor0f654a92011-07-02 14:32:11 -0400142 } else {
143 rootbus = parent->rootbus;
144 }
Kevin O'Connor096a9b12011-06-19 14:09:20 -0400145
Kevin O'Connor0f654a92011-07-02 14:32:11 -0400146 // Populate pci_device info.
147 dev->bdf = bdf;
148 dev->parent = parent;
149 dev->rootbus = rootbus;
150 u32 vendev = pci_config_readl(bdf, PCI_VENDOR_ID);
151 dev->vendor = vendev & 0xffff;
152 dev->device = vendev >> 16;
153 u32 classrev = pci_config_readl(bdf, PCI_CLASS_REVISION);
154 dev->class = classrev >> 16;
155 dev->prog_if = classrev >> 8;
156 dev->revision = classrev & 0xff;
157 dev->header_type = pci_config_readb(bdf, PCI_HEADER_TYPE);
158 u8 v = dev->header_type & 0x7f;
159 if (v == PCI_HEADER_TYPE_BRIDGE || v == PCI_HEADER_TYPE_CARDBUS) {
160 u8 secbus = pci_config_readb(bdf, PCI_SECONDARY_BUS);
161 dev->secondary_bus = secbus;
162 if (secbus > bus && !busdevs[secbus])
163 busdevs[secbus] = dev;
164 if (secbus > MaxPCIBus)
165 MaxPCIBus = secbus;
166 }
167 dprintf(4, "PCI device %02x:%02x.%x (vd=%04x:%04x c=%04x)\n"
168 , pci_bdf_to_bus(bdf), pci_bdf_to_dev(bdf)
169 , pci_bdf_to_fn(bdf)
170 , dev->vendor, dev->device, dev->class);
Kevin O'Connor096a9b12011-06-19 14:09:20 -0400171 }
172 }
Kevin O'Connor0f654a92011-07-02 14:32:11 -0400173 dprintf(1, "Found %d PCI devices (max PCI bus is %02x)\n", count, MaxPCIBus);
Kevin O'Connor096a9b12011-06-19 14:09:20 -0400174}
175
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -0500176// Search for a device with the specified vendor and device ids.
Kevin O'Connor0cd70052011-07-02 14:04:19 -0400177struct pci_device *
Kevin O'Connor4132e022008-12-04 19:39:10 -0500178pci_find_device(u16 vendid, u16 devid)
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -0500179{
Kevin O'Connor0cd70052011-07-02 14:04:19 -0400180 struct pci_device *pci;
181 foreachpci(pci) {
182 if (pci->vendor == vendid && pci->device == devid)
183 return pci;
Kevin O'Connor0f803e42008-05-24 23:07:16 -0400184 }
Kevin O'Connor0cd70052011-07-02 14:04:19 -0400185 return NULL;
Kevin O'Connor0f803e42008-05-24 23:07:16 -0400186}
187
Kevin O'Connor5fdaa032008-08-31 11:06:27 -0400188// Search for a device with the specified class id.
Kevin O'Connor0cd70052011-07-02 14:04:19 -0400189struct pci_device *
Kevin O'Connor4132e022008-12-04 19:39:10 -0500190pci_find_class(u16 classid)
Kevin O'Connor5fdaa032008-08-31 11:06:27 -0400191{
Kevin O'Connor0cd70052011-07-02 14:04:19 -0400192 struct pci_device *pci;
193 foreachpci(pci) {
194 if (pci->class == classid)
195 return pci;
Kevin O'Connor0f803e42008-05-24 23:07:16 -0400196 }
Kevin O'Connor0cd70052011-07-02 14:04:19 -0400197 return NULL;
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400198}
Isaku Yamahata968d3a82010-07-07 12:14:01 +0900199
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400200int pci_init_device(const struct pci_device_id *ids
201 , struct pci_device *pci, void *arg)
Isaku Yamahata968d3a82010-07-07 12:14:01 +0900202{
Isaku Yamahata968d3a82010-07-07 12:14:01 +0900203 while (ids->vendid || ids->class_mask) {
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400204 if ((ids->vendid == PCI_ANY_ID || ids->vendid == pci->vendor) &&
205 (ids->devid == PCI_ANY_ID || ids->devid == pci->device) &&
206 !((ids->class ^ pci->class) & ids->class_mask)) {
207 if (ids->func)
208 ids->func(pci, arg);
Isaku Yamahata968d3a82010-07-07 12:14:01 +0900209 return 0;
210 }
211 ids++;
212 }
213 return -1;
214}
Isaku Yamahata23173ac2010-07-20 16:37:15 +0900215
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400216struct pci_device *
217pci_find_init_device(const struct pci_device_id *ids, void *arg)
Isaku Yamahata23173ac2010-07-20 16:37:15 +0900218{
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400219 struct pci_device *pci;
220 foreachpci(pci) {
221 if (pci_init_device(ids, pci, arg) == 0)
222 return pci;
Isaku Yamahata23173ac2010-07-20 16:37:15 +0900223 }
Kevin O'Connor278b19f2011-06-21 22:41:15 -0400224 return NULL;
Isaku Yamahata23173ac2010-07-20 16:37:15 +0900225}
Kevin O'Connoradaf3732010-09-13 20:22:07 -0400226
227void
228pci_reboot(void)
229{
230 u8 v = inb(PORT_PCI_REBOOT) & ~6;
231 outb(v|2, PORT_PCI_REBOOT); /* Request hard reset */
232 udelay(50);
233 outb(v|6, PORT_PCI_REBOOT); /* Actually do the reset */
234 udelay(50);
235}
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100236
237// helper functions to access pci mmio bars from real mode
238
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100239u32 VISIBLE32FLAT
240pci_readl_32(u32 addr)
241{
Kevin O'Connorbf079e12012-03-11 11:19:52 -0400242 dprintf(9, "32: pci read : %x\n", addr);
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100243 return readl((void*)addr);
244}
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100245
246u32 pci_readl(u32 addr)
247{
248 if (MODESEGMENT) {
Kevin O'Connorbf079e12012-03-11 11:19:52 -0400249 dprintf(9, "16: pci read : %x\n", addr);
Kevin O'Connorf3fe3aa2010-12-05 12:38:33 -0500250 extern void _cfunc32flat_pci_readl_32(u32 addr);
251 return call32(_cfunc32flat_pci_readl_32, addr, -1);
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100252 } else {
253 return pci_readl_32(addr);
254 }
255}
256
257struct reg32 {
258 u32 addr;
259 u32 data;
260};
261
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100262void VISIBLE32FLAT
263pci_writel_32(struct reg32 *reg32)
264{
Kevin O'Connorbf079e12012-03-11 11:19:52 -0400265 dprintf(9, "32: pci write: %x, %x (%p)\n", reg32->addr, reg32->data, reg32);
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100266 writel((void*)(reg32->addr), reg32->data);
267}
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100268
269void pci_writel(u32 addr, u32 val)
270{
271 struct reg32 reg32 = { .addr = addr, .data = val };
272 if (MODESEGMENT) {
Kevin O'Connorbf079e12012-03-11 11:19:52 -0400273 dprintf(9, "16: pci write: %x, %x (%x:%p)\n",
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100274 reg32.addr, reg32.data, GET_SEG(SS), &reg32);
275 void *flatptr = MAKE_FLATPTR(GET_SEG(SS), &reg32);
Kevin O'Connorf3fe3aa2010-12-05 12:38:33 -0500276 extern void _cfunc32flat_pci_writel_32(struct reg32 *reg32);
277 call32(_cfunc32flat_pci_writel_32, (u32)flatptr, -1);
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100278 } else {
279 pci_writel_32(&reg32);
280 }
281}