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