blob: bb4ed7f2c6405dcfcd3a26d4bd46c711524b745c [file] [log] [blame]
Martin Roth9b1b3352016-02-24 12:27:06 -08001/* pci.c - MemTest-86 Version 3.2
2 *
3 * Released under version 2 of the Gnu Public License.
4 * By Chris Brady
5 * ----------------------------------------------------
6 * MemTest86+ V5.00 Specific code (GPL V2.0)
7 * By Samuel DEMEULEMEESTER, sdemeule@memtest.org
8 * http://www.x86-secret.com - http://www.memtest.org
9 */
10
11#include "io.h"
12#include "pci.h"
13#include "test.h"
14#include "stdint.h"
15#include "cpuid.h"
16
17#define PCI_CONF_TYPE_NONE 0
18#define PCI_CONF_TYPE_1 1
19#define PCI_CONF_TYPE_2 2
20
21extern struct cpu_ident cpu_id;
22
23static unsigned char pci_conf_type = PCI_CONF_TYPE_NONE;
24
25#define PCI_CONF1_ADDRESS(bus, dev, fn, reg) \
26 (0x80000000 | (bus << 16) | (dev << 11) | (fn << 8) | (reg & ~3))
27
Ben Gardner90f7d112016-03-15 15:25:22 -050028#define PCI_CONF2_ADDRESS(dev, reg) (unsigned short)(0xC000 | (dev << 8) | reg)
Martin Roth9b1b3352016-02-24 12:27:06 -080029
30#define PCI_CONF3_ADDRESS(bus, dev, fn, reg) \
31 (0x80000000 | (((reg >> 8) & 0xF) << 24) | (bus << 16) | ((dev & 0x1F) << 11) | (fn << 8) | (reg & 0xFF))
32
33int pci_conf_read(unsigned bus, unsigned dev, unsigned fn, unsigned reg, unsigned len, unsigned long *value)
34{
35 int result;
36
37 if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255 && pci_conf_type != PCI_CONF_TYPE_1))
38 return -1;
39
40 result = -1;
Ben Gardner90f7d112016-03-15 15:25:22 -050041 switch (pci_conf_type) {
Martin Roth9b1b3352016-02-24 12:27:06 -080042 case PCI_CONF_TYPE_1:
Ben Gardner90f7d112016-03-15 15:25:22 -050043 if (reg < 256) {
Martin Roth9b1b3352016-02-24 12:27:06 -080044 outl(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8);
Ben Gardner90f7d112016-03-15 15:25:22 -050045 } else {
Martin Roth4dcd13d2016-02-24 13:53:07 -080046 outl(PCI_CONF3_ADDRESS(bus, dev, fn, reg), 0xCF8);
Martin Roth9b1b3352016-02-24 12:27:06 -080047 }
Ben Gardner90f7d112016-03-15 15:25:22 -050048 switch (len) {
Martin Roth9b1b3352016-02-24 12:27:06 -080049 case 1: *value = inb(0xCFC + (reg & 3)); result = 0; break;
50 case 2: *value = inw(0xCFC + (reg & 2)); result = 0; break;
51 case 4: *value = inl(0xCFC); result = 0; break;
52 }
53 break;
54 case PCI_CONF_TYPE_2:
55 outb(0xF0 | (fn << 1), 0xCF8);
56 outb(bus, 0xCFA);
57
Ben Gardner90f7d112016-03-15 15:25:22 -050058 switch (len) {
Martin Roth9b1b3352016-02-24 12:27:06 -080059 case 1: *value = inb(PCI_CONF2_ADDRESS(dev, reg)); result = 0; break;
60 case 2: *value = inw(PCI_CONF2_ADDRESS(dev, reg)); result = 0; break;
61 case 4: *value = inl(PCI_CONF2_ADDRESS(dev, reg)); result = 0; break;
62 }
63 outb(0, 0xCF8);
64 break;
65 }
66 return result;
67}
68
69int pci_conf_write(unsigned bus, unsigned dev, unsigned fn, unsigned reg, unsigned len, unsigned long value)
70{
71 int result;
72
73 if (!value || (bus > 255) || (dev > 31) || (fn > 7) || (reg > 255 && pci_conf_type != PCI_CONF_TYPE_1))
74 return -1;
75
76 result = -1;
Martin Roth4dcd13d2016-02-24 13:53:07 -080077
Ben Gardner90f7d112016-03-15 15:25:22 -050078 switch (pci_conf_type) {
79 case PCI_CONF_TYPE_1:
80 if (reg < 256) {
81 outl(PCI_CONF1_ADDRESS(bus, dev, fn, reg), 0xCF8);
82 } else {
83 outl(PCI_CONF3_ADDRESS(bus, dev, fn, reg), 0xCF8);
84 }
85 switch (len) {
86 case 1: outb(value, 0xCFC + (reg & 3)); result = 0; break;
87 case 2: outw(value, 0xCFC + (reg & 2)); result = 0; break;
88 case 4: outl(value, 0xCFC); result = 0; break;
89 }
90 break;
91 case PCI_CONF_TYPE_2:
92 outb(0xF0 | (fn << 1), 0xCF8);
93 outb(bus, 0xCFA);
Martin Roth4dcd13d2016-02-24 13:53:07 -080094
Ben Gardner90f7d112016-03-15 15:25:22 -050095 switch (len) {
96 case 1: outb(value, PCI_CONF2_ADDRESS(dev, reg)); result = 0; break;
97 case 2: outw(value, PCI_CONF2_ADDRESS(dev, reg)); result = 0; break;
98 case 4: outl(value, PCI_CONF2_ADDRESS(dev, reg)); result = 0; break;
99 }
100 outb(0, 0xCF8);
101 break;
Martin Roth9b1b3352016-02-24 12:27:06 -0800102 }
103 return result;
104}
105
106static int pci_sanity_check(void)
107{
108 unsigned long value;
109 int result;
110 /* Do a trivial check to make certain we can see a host bridge.
111 * There are reportedly some buggy chipsets from intel and
112 * compaq where this test does not work, I will worry about
113 * that when we support them.
114 */
115 result = pci_conf_read(0, 0, 0, PCI_CLASS_DEVICE, 2, &value);
116 if (result == 0) {
117 result = -1;
118 if (value == PCI_CLASS_BRIDGE_HOST) {
119 result = 0;
120 }
121 }
122 return result;
123}
124
125static int pci_check_direct(void)
126{
127 unsigned char tmpCFB;
Ben Gardner90f7d112016-03-15 15:25:22 -0500128 unsigned int tmpCF8;
Martin Roth4dcd13d2016-02-24 13:53:07 -0800129
Martin Roth9b1b3352016-02-24 12:27:06 -0800130 if (cpu_id.vend_id.char_array[0] == 'A' && cpu_id.vers.bits.family == 0xF) {
Ben Gardner90f7d112016-03-15 15:25:22 -0500131 pci_conf_type = PCI_CONF_TYPE_1;
132 return 0;
Martin Roth9b1b3352016-02-24 12:27:06 -0800133 }
Elyes HAOUASaeb2c322019-06-09 20:14:44 +0200134 /* Check if configuration type 1 works. */
135 pci_conf_type = PCI_CONF_TYPE_1;
136 tmpCFB = inb(0xCFB);
137 outb(0x01, 0xCFB);
138 tmpCF8 = inl(0xCF8);
139 outl(0x80000000, 0xCF8);
140 if ((inl(0xCF8) == 0x80000000) && (pci_sanity_check() == 0)) {
141 outl(tmpCF8, 0xCF8);
142 outb(tmpCFB, 0xCFB);
143 return 0;
144 }
145 outl(tmpCF8, 0xCF8);
146
147 /* Check if configuration type 2 works. */
148
149 pci_conf_type = PCI_CONF_TYPE_2;
150 outb(0x00, 0xCFB);
151 outb(0x00, 0xCF8);
152 outb(0x00, 0xCFA);
153 if (inb(0xCF8) == 0x00 && inb(0xCFA) == 0x00 && (pci_sanity_check() == 0)) {
154 outb(tmpCFB, 0xCFB);
155 return 0;
156 }
157
158 outb(tmpCFB, 0xCFB);
159
160 /* Nothing worked return an error */
161 pci_conf_type = PCI_CONF_TYPE_NONE;
162 return -1;
Martin Roth9b1b3352016-02-24 12:27:06 -0800163}
164
165int pci_init(void)
166{
167 int result;
168 /* For now just make certain we can directly
169 * use the pci functions.
170 */
171 result = pci_check_direct();
172 return result;
173}