blob: c95baca1c05123cee3fca151daa6b872f2d43c47 [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'Connore6338322008-11-29 20:31:49 -05008#include "pci.h" // pci_config_writel
Kevin O'Connora0dc2962008-03-16 14:29:32 -04009#include "ioport.h" // outl
Kevin O'Connordb03d5d2008-06-21 11:55:29 -040010#include "util.h" // dprintf
Kevin O'Connorcde7a582008-08-17 11:08:46 -040011#include "config.h" // CONFIG_*
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +010012#include "farptr.h" // CONFIG_*
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
62pci_next(int bdf, int *pmax)
Kevin O'Connor0f803e42008-05-24 23:07:16 -040063{
Kevin O'Connore6338322008-11-29 20:31:49 -050064 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;
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050069
Kevin O'Connore6338322008-11-29 20:31:49 -050070 int max = *pmax;
71 for (;;) {
Kevin O'Connor6707c302009-02-28 12:26:39 -050072 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 }
Kevin O'Connore6338322008-11-29 20:31:49 -050081
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) {
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -050097 v = pci_config_readl(bdf, PCI_PRIMARY_BUS);
98 int newmax = (v & 0xff00) + 0x0100;
99 if (newmax > max)
Kevin O'Connore6338322008-11-29 20:31:49 -0500100 *pmax = newmax;
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -0500101 }
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -0500102
Kevin O'Connore6338322008-11-29 20:31:49 -0500103 return bdf;
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -0500104}
105
Kevin O'Connor04eece22009-07-19 19:05:30 -0400106// Find a vga device with legacy address decoding enabled.
107int
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500108pci_find_vga(void)
Kevin O'Connor04eece22009-07-19 19:05:30 -0400109{
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
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -0500161// Search for a device with the specified vendor and device ids.
162int
Kevin O'Connor4132e022008-12-04 19:39:10 -0500163pci_find_device(u16 vendid, u16 devid)
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -0500164{
Kevin O'Connor0f803e42008-05-24 23:07:16 -0400165 u32 id = (devid << 16) | vendid;
Kevin O'Connore6338322008-11-29 20:31:49 -0500166 int bdf, max;
Kevin O'Connorbaac6b62011-06-19 10:46:28 -0400167 foreachbdf(bdf, max) {
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -0500168 u32 v = pci_config_readl(bdf, PCI_VENDOR_ID);
Kevin O'Connor59f02832009-10-12 10:09:15 -0400169 if (v == id)
170 return bdf;
Kevin O'Connor0f803e42008-05-24 23:07:16 -0400171 }
172 return -1;
173}
174
Kevin O'Connor5fdaa032008-08-31 11:06:27 -0400175// Search for a device with the specified class id.
176int
Kevin O'Connor4132e022008-12-04 19:39:10 -0500177pci_find_class(u16 classid)
Kevin O'Connor5fdaa032008-08-31 11:06:27 -0400178{
Kevin O'Connore6338322008-11-29 20:31:49 -0500179 int bdf, max;
Kevin O'Connorbaac6b62011-06-19 10:46:28 -0400180 foreachbdf(bdf, max) {
Kevin O'Connorbe19cdc2008-11-09 15:33:47 -0500181 u16 v = pci_config_readw(bdf, PCI_CLASS_DEVICE);
Kevin O'Connor59f02832009-10-12 10:09:15 -0400182 if (v == classid)
183 return bdf;
Kevin O'Connor0f803e42008-05-24 23:07:16 -0400184 }
185 return -1;
Kevin O'Connora0dc2962008-03-16 14:29:32 -0400186}
Isaku Yamahata968d3a82010-07-07 12:14:01 +0900187
Kevin O'Connord08eb9c2011-01-10 00:48:45 -0500188int *PCIpaths;
189
190// Build the PCI path designations.
191void
192pci_path_setup(void)
193{
194 PCIpaths = malloc_tmp(sizeof(*PCIpaths) * 256);
195 if (!PCIpaths)
196 return;
Kevin O'Connor6fc7cf12011-01-22 10:53:08 -0500197 memset(PCIpaths, 0, sizeof(*PCIpaths) * 256);
Kevin O'Connord08eb9c2011-01-10 00:48:45 -0500198
199 int roots = 0;
200 int bdf, max;
Kevin O'Connorbaac6b62011-06-19 10:46:28 -0400201 foreachbdf(bdf, max) {
Kevin O'Connord08eb9c2011-01-10 00:48:45 -0500202 int bus = pci_bdf_to_bus(bdf);
203 if (! PCIpaths[bus])
204 PCIpaths[bus] = (roots++) | PP_ROOT;
205
206 // Check if found device is a bridge.
207 u32 v = pci_config_readb(bdf, PCI_HEADER_TYPE);
208 v &= 0x7f;
209 if (v == PCI_HEADER_TYPE_BRIDGE || v == PCI_HEADER_TYPE_CARDBUS) {
210 v = pci_config_readl(bdf, PCI_PRIMARY_BUS);
211 int childbus = (v >> 8) & 0xff;
Kevin O'Connor6fc7cf12011-01-22 10:53:08 -0500212 if (childbus > bus)
213 PCIpaths[childbus] = bdf | PP_PCIBRIDGE;
Kevin O'Connord08eb9c2011-01-10 00:48:45 -0500214 }
215 }
216}
217
Isaku Yamahata968d3a82010-07-07 12:14:01 +0900218int pci_init_device(const struct pci_device_id *ids, u16 bdf, void *arg)
219{
220 u16 vendor_id = pci_config_readw(bdf, PCI_VENDOR_ID);
221 u16 device_id = pci_config_readw(bdf, PCI_DEVICE_ID);
222 u16 class = pci_config_readw(bdf, PCI_CLASS_DEVICE);
223
224 while (ids->vendid || ids->class_mask) {
225 if ((ids->vendid == PCI_ANY_ID || ids->vendid == vendor_id) &&
226 (ids->devid == PCI_ANY_ID || ids->devid == device_id) &&
227 !((ids->class ^ class) & ids->class_mask)) {
228 if (ids->func) {
229 ids->func(bdf, arg);
230 }
231 return 0;
232 }
233 ids++;
234 }
235 return -1;
236}
Isaku Yamahata23173ac2010-07-20 16:37:15 +0900237
238int pci_find_init_device(const struct pci_device_id *ids, void *arg)
239{
240 int bdf, max;
241
Kevin O'Connorbaac6b62011-06-19 10:46:28 -0400242 foreachbdf(bdf, max) {
Isaku Yamahata23173ac2010-07-20 16:37:15 +0900243 if (pci_init_device(ids, bdf, arg) == 0) {
244 return bdf;
245 }
246 }
247 return -1;
248}
Kevin O'Connoradaf3732010-09-13 20:22:07 -0400249
250void
251pci_reboot(void)
252{
253 u8 v = inb(PORT_PCI_REBOOT) & ~6;
254 outb(v|2, PORT_PCI_REBOOT); /* Request hard reset */
255 udelay(50);
256 outb(v|6, PORT_PCI_REBOOT); /* Actually do the reset */
257 udelay(50);
258}
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100259
260// helper functions to access pci mmio bars from real mode
261
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100262u32 VISIBLE32FLAT
263pci_readl_32(u32 addr)
264{
265 dprintf(3, "32: pci read : %x\n", addr);
266 return readl((void*)addr);
267}
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100268
269u32 pci_readl(u32 addr)
270{
271 if (MODESEGMENT) {
272 dprintf(3, "16: pci read : %x\n", addr);
Kevin O'Connorf3fe3aa2010-12-05 12:38:33 -0500273 extern void _cfunc32flat_pci_readl_32(u32 addr);
274 return call32(_cfunc32flat_pci_readl_32, addr, -1);
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100275 } else {
276 return pci_readl_32(addr);
277 }
278}
279
280struct reg32 {
281 u32 addr;
282 u32 data;
283};
284
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100285void VISIBLE32FLAT
286pci_writel_32(struct reg32 *reg32)
287{
288 dprintf(3, "32: pci write: %x, %x (%p)\n", reg32->addr, reg32->data, reg32);
289 writel((void*)(reg32->addr), reg32->data);
290}
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100291
292void pci_writel(u32 addr, u32 val)
293{
294 struct reg32 reg32 = { .addr = addr, .data = val };
295 if (MODESEGMENT) {
296 dprintf(3, "16: pci write: %x, %x (%x:%p)\n",
297 reg32.addr, reg32.data, GET_SEG(SS), &reg32);
298 void *flatptr = MAKE_FLATPTR(GET_SEG(SS), &reg32);
Kevin O'Connorf3fe3aa2010-12-05 12:38:33 -0500299 extern void _cfunc32flat_pci_writel_32(struct reg32 *reg32);
300 call32(_cfunc32flat_pci_writel_32, (u32)flatptr, -1);
Gerd Hoffmannf1f18eb2010-11-29 09:42:10 +0100301 } else {
302 pci_writel_32(&reg32);
303 }
304}