1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
// PCI config space access functions.
//
// Copyright (C) 2008 Kevin O'Connor <kevin@koconnor.net>
// Copyright (C) 2002 MandrakeSoft S.A.
//
// This file may be distributed under the terms of the GNU LGPLv3 license.
#include "output.h" // dprintf
#include "pci.h" // pci_config_writel
#include "pci_regs.h" // PCI_VENDOR_ID
#include "util.h" // udelay
#include "x86.h" // outl
#define PORT_PCI_CMD 0x0cf8
#define PORT_PCI_DATA 0x0cfc
void pci_config_writel(u16 bdf, u32 addr, u32 val)
{
outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
outl(val, PORT_PCI_DATA);
}
void pci_config_writew(u16 bdf, u32 addr, u16 val)
{
outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
outw(val, PORT_PCI_DATA + (addr & 2));
}
void pci_config_writeb(u16 bdf, u32 addr, u8 val)
{
outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
outb(val, PORT_PCI_DATA + (addr & 3));
}
u32 pci_config_readl(u16 bdf, u32 addr)
{
outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
return inl(PORT_PCI_DATA);
}
u16 pci_config_readw(u16 bdf, u32 addr)
{
outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
return inw(PORT_PCI_DATA + (addr & 2));
}
u8 pci_config_readb(u16 bdf, u32 addr)
{
outl(0x80000000 | (bdf << 8) | (addr & 0xfc), PORT_PCI_CMD);
return inb(PORT_PCI_DATA + (addr & 3));
}
void
pci_config_maskw(u16 bdf, u32 addr, u16 off, u16 on)
{
u16 val = pci_config_readw(bdf, addr);
val = (val & ~off) | on;
pci_config_writew(bdf, addr, val);
}
u8 pci_find_capability(u16 bdf, u8 cap_id, u8 cap)
{
int i;
u16 status = pci_config_readw(bdf, PCI_STATUS);
if (!(status & PCI_STATUS_CAP_LIST))
return 0;
if (cap == 0) {
/* find first */
cap = pci_config_readb(bdf, PCI_CAPABILITY_LIST);
} else {
/* find next */
cap = pci_config_readb(bdf, cap + PCI_CAP_LIST_NEXT);
}
for (i = 0; cap && i <= 0xff; i++) {
if (pci_config_readb(bdf, cap + PCI_CAP_LIST_ID) == cap_id)
return cap;
cap = pci_config_readb(bdf, cap + PCI_CAP_LIST_NEXT);
}
return 0;
}
// Helper function for foreachbdf() macro - return next device
int
pci_next(int bdf, int bus)
{
if (pci_bdf_to_fn(bdf) == 0
&& (pci_config_readb(bdf, PCI_HEADER_TYPE) & 0x80) == 0)
// Last found device wasn't a multi-function device - skip to
// the next device.
bdf += 8;
else
bdf += 1;
for (;;) {
if (pci_bdf_to_bus(bdf) != bus)
return -1;
u16 v = pci_config_readw(bdf, PCI_VENDOR_ID);
if (v != 0x0000 && v != 0xffff)
// Device is present.
return bdf;
if (pci_bdf_to_fn(bdf) == 0)
bdf += 8;
else
bdf += 1;
}
}
// Check if PCI is available at all
int
pci_probe_host(void)
{
outl(0x80000000, PORT_PCI_CMD);
if (inl(PORT_PCI_CMD) != 0x80000000) {
dprintf(1, "Detected non-PCI system\n");
return -1;
}
return 0;
}
void
pci_reboot(void)
{
u8 v = inb(PORT_PCI_REBOOT) & ~6;
outb(v|2, PORT_PCI_REBOOT); /* Request hard reset */
udelay(50);
outb(v|6, PORT_PCI_REBOOT); /* Actually do the reset */
udelay(50);
}
|