1/*
2 * Copyright 2006, Marcus Overhagen. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include <KernelExport.h>
7#include <driver_settings.h>
8#include <string.h>
9#include "pci_irq.h"
10#include "pci_bios.h"
11#include "pci_private.h"
12#include "pci_controller.h"
13#include "arch_cpu.h"
14
15#define PCI_MECH1_REQ_PORT				0xCF8
16#define PCI_MECH1_DATA_PORT 			0xCFC
17#define PCI_MECH1_REQ_DATA(bus, device, func, offset) \
18	(0x80000000 | (bus << 16) | (device << 11) | (func << 8) | (offset & ~3))
19
20#define PCI_MECH2_ENABLE_PORT			0x0cf8
21#define PCI_MECH2_FORWARD_PORT			0x0cfa
22#define PCI_MECH2_CONFIG_PORT(dev, offset) \
23	(uint16)(0xC00 | (dev << 8) | offset)
24
25#define PCI_LOCK_CONFIG(cpu_status)			\
26{											\
27       cpu_status = disable_interrupts();	\
28       acquire_spinlock(&sConfigLock);		\
29}
30
31#define PCI_UNLOCK_CONFIG(cpu_status)		\
32{											\
33       release_spinlock(&sConfigLock);		\
34       restore_interrupts(cpu_status);		\
35}
36
37spinlock sConfigLock = B_SPINLOCK_INITIALIZER;
38
39static status_t
40pci_mech1_read_config(void *cookie, uint8 bus, uint8 device, uint8 function,
41					  uint8 offset, uint8 size, uint32 *value)
42{
43	cpu_status cpu;
44	status_t status = B_OK;
45
46	PCI_LOCK_CONFIG(cpu);
47	out32(PCI_MECH1_REQ_DATA(bus, device, function, offset), PCI_MECH1_REQ_PORT);
48	switch (size) {
49		case 1:
50			*value = in8(PCI_MECH1_DATA_PORT + (offset & 3));
51			break;
52		case 2:
53			*value = in16(PCI_MECH1_DATA_PORT + (offset & 3));
54			break;
55		case 4:
56			*value = in32(PCI_MECH1_DATA_PORT);
57			break;
58		default:
59			status = B_ERROR;
60			break;
61	}
62	PCI_UNLOCK_CONFIG(cpu);
63
64	return status;
65}
66
67
68static status_t
69pci_mech1_write_config(void *cookie, uint8 bus, uint8 device, uint8 function,
70					   uint8 offset, uint8 size, uint32 value)
71{
72	cpu_status cpu;
73	status_t status = B_OK;
74
75	PCI_LOCK_CONFIG(cpu);
76	out32(PCI_MECH1_REQ_DATA(bus, device, function, offset), PCI_MECH1_REQ_PORT);
77	switch (size) {
78		case 1:
79			out8(value, PCI_MECH1_DATA_PORT + (offset & 3));
80			break;
81		case 2:
82			out16(value, PCI_MECH1_DATA_PORT + (offset & 3));
83			break;
84		case 4:
85			out32(value, PCI_MECH1_DATA_PORT);
86			break;
87		default:
88			status = B_ERROR;
89			break;
90	}
91	PCI_UNLOCK_CONFIG(cpu);
92
93	return status;
94}
95
96
97static status_t
98pci_mech1_get_max_bus_devices(void *cookie, int32 *count)
99{
100	*count = 32;
101	return B_OK;
102}
103
104
105static status_t
106pci_mech2_read_config(void *cookie, uint8 bus, uint8 device, uint8 function,
107					  uint8 offset, uint8 size, uint32 *value)
108{
109	cpu_status cpu;
110	status_t status = B_OK;
111
112	PCI_LOCK_CONFIG(cpu);
113	out8((uint8)(0xf0 | (function << 1)), PCI_MECH2_ENABLE_PORT);
114	out8(bus, PCI_MECH2_FORWARD_PORT);
115	switch (size) {
116		case 1:
117			*value = in8(PCI_MECH2_CONFIG_PORT(device, offset));
118			break;
119		case 2:
120			*value = in16(PCI_MECH2_CONFIG_PORT(device, offset));
121			break;
122		case 4:
123			*value = in32(PCI_MECH2_CONFIG_PORT(device, offset));
124			break;
125		default:
126			status = B_ERROR;
127			break;
128	}
129	out8(0, PCI_MECH2_ENABLE_PORT);
130	PCI_UNLOCK_CONFIG(cpu);
131
132	return status;
133}
134
135
136static status_t
137pci_mech2_write_config(void *cookie, uint8 bus, uint8 device, uint8 function,
138					   uint8 offset, uint8 size, uint32 value)
139{
140	cpu_status cpu;
141	status_t status = B_OK;
142
143	PCI_LOCK_CONFIG(cpu);
144	out8((uint8)(0xf0 | (function << 1)), PCI_MECH2_ENABLE_PORT);
145	out8(bus, PCI_MECH2_FORWARD_PORT);
146	switch (size) {
147		case 1:
148			out8(value, PCI_MECH2_CONFIG_PORT(device, offset));
149			break;
150		case 2:
151			out16(value, PCI_MECH2_CONFIG_PORT(device, offset));
152			break;
153		case 4:
154			out32(value, PCI_MECH2_CONFIG_PORT(device, offset));
155			break;
156		default:
157			status = B_ERROR;
158			break;
159	}
160	out8(0, PCI_MECH2_ENABLE_PORT);
161	PCI_UNLOCK_CONFIG(cpu);
162
163	return status;
164}
165
166
167static status_t
168pci_mech2_get_max_bus_devices(void *cookie, int32 *count)
169{
170	*count = 16;
171	return B_OK;
172}
173
174
175void *
176pci_ram_address(const void *physical_address_in_system_memory)
177{
178	return (void *)physical_address_in_system_memory;
179}
180
181
182pci_controller pci_controller_x86_mech1 =
183{
184	pci_mech1_read_config,
185	pci_mech1_write_config,
186	pci_mech1_get_max_bus_devices,
187	pci_x86_irq_read,
188	pci_x86_irq_write,
189};
190
191pci_controller pci_controller_x86_mech2 =
192{
193	pci_mech2_read_config,
194	pci_mech2_write_config,
195	pci_mech2_get_max_bus_devices,
196	pci_x86_irq_read,
197	pci_x86_irq_write,
198};
199
200pci_controller pci_controller_x86_bios =
201{
202	pci_bios_read_config,
203	pci_bios_write_config,
204	pci_bios_get_max_bus_devices,
205	pci_x86_irq_read,
206	pci_x86_irq_write,
207};
208
209
210status_t
211pci_controller_init(void)
212{
213	bool search_mech1 = true;
214	bool search_mech2 = true;
215	bool search_bios = true;
216	void *config = NULL;
217	status_t status;
218
219	status = pci_x86_irq_init();
220	if (status != B_OK)
221		return status;
222
223	config = load_driver_settings("pci");
224	if (config) {
225		const char *mech = get_driver_parameter(config, "mechanism",
226			NULL, NULL);
227		if (mech) {
228			search_mech1 = search_mech2 = search_bios = false;
229			if (strcmp(mech, "1") == 0)
230				search_mech1 = true;
231			else if (strcmp(mech, "2") == 0)
232				search_mech2 = true;
233			else if (strcmp(mech, "bios") == 0)
234				search_bios = true;
235			else
236				panic("Unknown pci config mechanism setting %s\n", mech);
237		}
238		unload_driver_settings(config);
239	}
240
241	// TODO: check safemode "don't call the BIOS" setting and unset search_bios!
242
243	// PCI configuration mechanism 1 is the preferred one.
244	// If it doesn't work, try mechanism 2.
245	// Finally, try to fallback to PCI BIOS
246
247	if (search_mech1) {
248		// check for mechanism 1
249		out32(0x80000000, PCI_MECH1_REQ_PORT);
250		if (0x80000000 == in32(PCI_MECH1_REQ_PORT)) {
251			dprintf("PCI: mechanism 1 controller found\n");
252			return pci_controller_add(&pci_controller_x86_mech1, NULL);
253		}
254	}
255
256	if (search_mech2) {
257		// check for mechanism 2
258		out8(0x00, 0xCFB);
259		out8(0x00, 0xCF8);
260		out8(0x00, 0xCFA);
261		if (in8(0xCF8) == 0x00 && in8(0xCFA) == 0x00) {
262			dprintf("PCI: mechanism 2 controller found\n");
263			return pci_controller_add(&pci_controller_x86_mech2, NULL);
264		}
265	}
266
267	if (search_bios) {
268		// check for PCI BIOS
269		if (pci_bios_init() == B_OK) {
270			dprintf("PCI: BIOS support found\n");
271			return pci_controller_add(&pci_controller_x86_bios, NULL);
272		}
273	}
274
275	dprintf("PCI: no configuration mechanism found\n");
276	return B_ERROR;
277}
278
279
280