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 "output.h" // dprintf
9 #include "pci.h" // pci_config_writel
10 #include "pci_regs.h" // PCI_VENDOR_ID
11 #include "util.h" // udelay
12 #include "x86.h" // outl
13
14 #define PORT_PCI_CMD 0x0cf8
15 #define PORT_PCI_DATA 0x0cfc
16
17 static u32 mmconfig;
18
mmconfig_addr(u16 bdf,u32 addr)19 static void *mmconfig_addr(u16 bdf, u32 addr)
20 {
21 return (void*)(mmconfig + ((u32)bdf << 12) + addr);
22 }
23
ioconfig_cmd(u16 bdf,u32 addr)24 static u32 ioconfig_cmd(u16 bdf, u32 addr)
25 {
26 return 0x80000000 | (bdf << 8) | (addr & 0xfc);
27 }
28
pci_config_writel(u16 bdf,u32 addr,u32 val)29 void pci_config_writel(u16 bdf, u32 addr, u32 val)
30 {
31 if (!MODESEGMENT && mmconfig) {
32 writel(mmconfig_addr(bdf, addr), val);
33 } else {
34 outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
35 outl(val, PORT_PCI_DATA);
36 }
37 }
38
pci_config_writew(u16 bdf,u32 addr,u16 val)39 void pci_config_writew(u16 bdf, u32 addr, u16 val)
40 {
41 if (!MODESEGMENT && mmconfig) {
42 writew(mmconfig_addr(bdf, addr), val);
43 } else {
44 outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
45 outw(val, PORT_PCI_DATA + (addr & 2));
46 }
47 }
48
pci_config_writeb(u16 bdf,u32 addr,u8 val)49 void pci_config_writeb(u16 bdf, u32 addr, u8 val)
50 {
51 if (!MODESEGMENT && mmconfig) {
52 writeb(mmconfig_addr(bdf, addr), val);
53 } else {
54 outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
55 outb(val, PORT_PCI_DATA + (addr & 3));
56 }
57 }
58
pci_config_readl(u16 bdf,u32 addr)59 u32 pci_config_readl(u16 bdf, u32 addr)
60 {
61 if (!MODESEGMENT && mmconfig) {
62 return readl(mmconfig_addr(bdf, addr));
63 } else {
64 outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
65 return inl(PORT_PCI_DATA);
66 }
67 }
68
pci_config_readw(u16 bdf,u32 addr)69 u16 pci_config_readw(u16 bdf, u32 addr)
70 {
71 if (!MODESEGMENT && mmconfig) {
72 return readw(mmconfig_addr(bdf, addr));
73 } else {
74 outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
75 return inw(PORT_PCI_DATA + (addr & 2));
76 }
77 }
78
pci_config_readb(u16 bdf,u32 addr)79 u8 pci_config_readb(u16 bdf, u32 addr)
80 {
81 if (!MODESEGMENT && mmconfig) {
82 return readb(mmconfig_addr(bdf, addr));
83 } else {
84 outl(ioconfig_cmd(bdf, addr), PORT_PCI_CMD);
85 return inb(PORT_PCI_DATA + (addr & 3));
86 }
87 }
88
89 void
pci_config_maskw(u16 bdf,u32 addr,u16 off,u16 on)90 pci_config_maskw(u16 bdf, u32 addr, u16 off, u16 on)
91 {
92 u16 val = pci_config_readw(bdf, addr);
93 val = (val & ~off) | on;
94 pci_config_writew(bdf, addr, val);
95 }
96
97 void
pci_enable_mmconfig(u64 addr,const char * name)98 pci_enable_mmconfig(u64 addr, const char *name)
99 {
100 if (addr >= 0x100000000ll)
101 return;
102 dprintf(1, "PCIe: using %s mmconfig at 0x%llx\n", name, addr);
103 mmconfig = addr;
104 }
105
pci_find_capability(u16 bdf,u8 cap_id,u8 cap)106 u8 pci_find_capability(u16 bdf, u8 cap_id, u8 cap)
107 {
108 int i;
109 u16 status = pci_config_readw(bdf, PCI_STATUS);
110
111 if (!(status & PCI_STATUS_CAP_LIST))
112 return 0;
113
114 if (cap == 0) {
115 /* find first */
116 cap = pci_config_readb(bdf, PCI_CAPABILITY_LIST);
117 } else {
118 /* find next */
119 cap = pci_config_readb(bdf, cap + PCI_CAP_LIST_NEXT);
120 }
121 for (i = 0; cap && i <= 0xff; i++) {
122 if (pci_config_readb(bdf, cap + PCI_CAP_LIST_ID) == cap_id)
123 return cap;
124 cap = pci_config_readb(bdf, cap + PCI_CAP_LIST_NEXT);
125 }
126
127 return 0;
128 }
129
130 // Helper function for foreachbdf() macro - return next device
131 int
pci_next(int bdf,int bus)132 pci_next(int bdf, int bus)
133 {
134 if (pci_bdf_to_fn(bdf) == 0
135 && (pci_config_readb(bdf, PCI_HEADER_TYPE) & 0x80) == 0)
136 // Last found device wasn't a multi-function device - skip to
137 // the next device.
138 bdf += 8;
139 else
140 bdf += 1;
141
142 for (;;) {
143 if (pci_bdf_to_bus(bdf) != bus)
144 return -1;
145
146 u16 v = pci_config_readw(bdf, PCI_VENDOR_ID);
147 if (v != 0x0000 && v != 0xffff)
148 // Device is present.
149 return bdf;
150
151 if (pci_bdf_to_fn(bdf) == 0)
152 bdf += 8;
153 else
154 bdf += 1;
155 }
156 }
157
158 // Check if PCI is available at all
159 int
pci_probe_host(void)160 pci_probe_host(void)
161 {
162 outl(0x80000000, PORT_PCI_CMD);
163 if (inl(PORT_PCI_CMD) != 0x80000000) {
164 dprintf(1, "Detected non-PCI system\n");
165 return -1;
166 }
167 return 0;
168 }
169
170 void
pci_reboot(void)171 pci_reboot(void)
172 {
173 u8 v = inb(PORT_PCI_REBOOT) & ~6;
174 outb(v|2, PORT_PCI_REBOOT); /* Request hard reset */
175 udelay(50);
176 outb(v|6, PORT_PCI_REBOOT); /* Actually do the reset */
177 udelay(50);
178 }
179