xref: /haiku/src/libs/compat/freebsd_network/pci.cpp (revision 9f3bdf3d039430b5172c424def20ce5d9f7367d4)
1 /*
2  * Copyright 2022, Haiku, Inc. All rights reserved.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 extern "C" {
7 #include "device.h"
8 
9 #include <compat/machine/resource.h>
10 #include <compat/dev/pci/pcireg.h>
11 #include <compat/dev/pci/pcivar.h>
12 }
13 
14 #include <PCI.h>
15 
16 
17 //#define DEBUG_PCI
18 #ifdef DEBUG_PCI
19 #	define TRACE_PCI(dev, format, args...) device_printf(dev, format , ##args)
20 #else
21 #	define TRACE_PCI(dev, format, args...) do { } while (0)
22 #endif
23 
24 
25 pci_module_info *gPci;
26 
27 
28 status_t
29 init_pci()
30 {
31 	if (gPci != NULL)
32 		return B_OK;
33 
34 	status_t status = get_module(B_PCI_MODULE_NAME, (module_info **)&gPci);
35 	if (status != B_OK)
36 		return status;
37 
38 	return B_OK;
39 }
40 
41 
42 void
43 uninit_pci()
44 {
45 	if (gPci != NULL)
46 		put_module(B_PCI_MODULE_NAME);
47 }
48 
49 
50 pci_info*
51 get_device_pci_info(device_t device)
52 {
53 	struct root_device_softc* root_softc = (struct root_device_softc*)device->root->softc;
54 	if (root_softc->bus != root_device_softc::BUS_pci)
55 		return NULL;
56 	return &root_softc->pci_info;
57 }
58 
59 
60 uint32_t
61 pci_read_config(device_t dev, int offset, int size)
62 {
63 	pci_info* info = get_device_pci_info(dev);
64 
65 	uint32_t value = gPci->read_pci_config(info->bus, info->device,
66 		info->function, offset, size);
67 	TRACE_PCI(dev, "pci_read_config(%i, %i) = 0x%x\n", offset, size, value);
68 	return value;
69 }
70 
71 
72 void
73 pci_write_config(device_t dev, int offset, uint32_t value, int size)
74 {
75 	pci_info* info = get_device_pci_info(dev);
76 
77 	TRACE_PCI(dev, "pci_write_config(%i, 0x%x, %i)\n", offset, value, size);
78 
79 	gPci->write_pci_config(info->bus, info->device, info->function, offset,
80 		size, value);
81 }
82 
83 
84 uint16_t
85 pci_get_vendor(device_t dev)
86 {
87 	return pci_read_config(dev, PCI_vendor_id, 2);
88 }
89 
90 
91 uint16_t
92 pci_get_device(device_t dev)
93 {
94 	return pci_read_config(dev, PCI_device_id, 2);
95 }
96 
97 
98 uint16_t
99 pci_get_subvendor(device_t dev)
100 {
101 	return pci_read_config(dev, PCI_subsystem_vendor_id, 2);
102 }
103 
104 
105 uint16_t
106 pci_get_subdevice(device_t dev)
107 {
108 	return pci_read_config(dev, PCI_subsystem_id, 2);
109 }
110 
111 
112 uint8_t
113 pci_get_revid(device_t dev)
114 {
115 	return pci_read_config(dev, PCI_revision, 1);
116 }
117 
118 
119 uint32_t
120 pci_get_domain(device_t dev)
121 {
122 	return 0;
123 }
124 
125 uint32_t
126 pci_get_devid(device_t dev)
127 {
128 	return pci_read_config(dev, PCI_device_id, 2) << 16 |
129 		pci_read_config(dev, PCI_vendor_id, 2);
130 }
131 
132 uint8_t
133 pci_get_cachelnsz(device_t dev)
134 {
135 	return pci_read_config(dev, PCI_line_size, 1);
136 }
137 
138 uint8_t *
139 pci_get_ether(device_t dev)
140 {
141 	/* used in if_dc to get the MAC from CardBus CIS for Xircom card */
142 	return NULL; /* NULL is handled in the caller correctly */
143 }
144 
145 uint8_t
146 pci_get_bus(device_t dev)
147 {
148 	pci_info *info
149 		= &((struct root_device_softc *)dev->root->softc)->pci_info;
150 	return info->bus;
151 }
152 
153 
154 uint8_t
155 pci_get_slot(device_t dev)
156 {
157 	pci_info *info
158 		= &((struct root_device_softc *)dev->root->softc)->pci_info;
159 	return info->device;
160 }
161 
162 
163 uint8_t
164 pci_get_function(device_t dev)
165 {
166 	pci_info* info = get_device_pci_info(dev);
167 	return info->function;
168 }
169 
170 
171 device_t
172 pci_find_dbsf(uint32_t domain, uint8_t bus, uint8_t slot, uint8_t func)
173 {
174 	// We don't support that yet - if we want to support the multi port
175 	// feature of the Broadcom BCM 570x driver, we would have to change
176 	// that.
177 	return NULL;
178 }
179 
180 
181 static void
182 pci_set_command_bit(device_t dev, uint16_t bit)
183 {
184 	uint16_t command = pci_read_config(dev, PCI_command, 2);
185 	pci_write_config(dev, PCI_command, command | bit, 2);
186 }
187 
188 
189 int
190 pci_enable_busmaster(device_t dev)
191 {
192 	// We do this a bit later than FreeBSD does.
193 	if (pci_get_powerstate(dev) != PCI_POWERSTATE_D0)
194 		pci_set_powerstate(dev, PCI_POWERSTATE_D0);
195 
196 	pci_set_command_bit(dev, PCI_command_master);
197 	return 0;
198 }
199 
200 
201 int
202 pci_enable_io(device_t dev, int space)
203 {
204 	/* adapted from FreeBSD's pci_enable_io_method */
205 	int bit = 0;
206 
207 	switch (space) {
208 		case SYS_RES_IOPORT:
209 			bit = PCI_command_io;
210 			break;
211 		case SYS_RES_MEMORY:
212 			bit = PCI_command_memory;
213 			break;
214 		default:
215 			return EINVAL;
216 	}
217 
218 	pci_set_command_bit(dev, bit);
219 	if (pci_read_config(dev, PCI_command, 2) & bit)
220 		return 0;
221 
222 	device_printf(dev, "pci_enable_io(%d) failed.\n", space);
223 
224 	return ENXIO;
225 }
226 
227 
228 int
229 pci_find_cap(device_t dev, int capability, int *capreg)
230 {
231 	pci_info* info = get_device_pci_info(dev);
232 	uint8 offset;
233 	status_t status;
234 
235 	status = gPci->find_pci_capability(info->bus, info->device, info->function,
236 		capability, &offset);
237 	*capreg = offset;
238 	return status;
239 }
240 
241 
242 int
243 pci_find_extcap(device_t dev, int capability, int *capreg)
244 {
245 	pci_info* info = get_device_pci_info(dev);
246 	uint16 offset;
247 	status_t status;
248 
249 	status = gPci->find_pci_extended_capability(info->bus, info->device, info->function,
250 		capability, &offset);
251 	*capreg = offset;
252 	return status;
253 }
254 
255 
256 int
257 pci_msi_count(device_t dev)
258 {
259 	pci_info* info = get_device_pci_info(dev);
260 	return gPci->get_msi_count(info->bus, info->device, info->function);
261 }
262 
263 
264 int
265 pci_alloc_msi(device_t dev, int *count)
266 {
267 	pci_info* info = get_device_pci_info(dev);
268 	uint32 startVector = 0;
269 	if (gPci->configure_msi(info->bus, info->device, info->function, *count,
270 			&startVector) != B_OK) {
271 		return ENODEV;
272 	}
273 
274 	((struct root_device_softc *)dev->root->softc)->is_msi = true;
275 	info->u.h0.interrupt_line = startVector;
276 	return EOK;
277 }
278 
279 
280 int
281 pci_release_msi(device_t dev)
282 {
283 	pci_info* info = get_device_pci_info(dev);
284 	gPci->unconfigure_msi(info->bus, info->device, info->function);
285 	((struct root_device_softc *)dev->root->softc)->is_msi = false;
286 	((struct root_device_softc *)dev->root->softc)->is_msix = false;
287 	return EOK;
288 }
289 
290 
291 int
292 pci_msix_table_bar(device_t dev)
293 {
294 	pci_info* info = get_device_pci_info(dev);
295 
296 	uint8 capability_offset;
297 	if (gPci->find_pci_capability(info->bus, info->device, info->function,
298 			PCI_cap_id_msix, &capability_offset) != B_OK)
299 		return -1;
300 
301 	uint32 table_value = gPci->read_pci_config(info->bus, info->device, info->function,
302 		capability_offset + PCI_msix_table, 4);
303 
304 	uint32 bar = table_value & PCI_msix_bir_mask;
305 	return PCIR_BAR(bar);
306 }
307 
308 
309 int
310 pci_msix_count(device_t dev)
311 {
312 	pci_info* info = get_device_pci_info(dev);
313 	return gPci->get_msix_count(info->bus, info->device, info->function);
314 }
315 
316 
317 int
318 pci_alloc_msix(device_t dev, int *count)
319 {
320 	pci_info* info = get_device_pci_info(dev);
321 	uint32 startVector = 0;
322 	if (gPci->configure_msix(info->bus, info->device, info->function, *count,
323 			&startVector) != B_OK) {
324 		return ENODEV;
325 	}
326 
327 	((struct root_device_softc *)dev->root->softc)->is_msix = true;
328 	info->u.h0.interrupt_line = startVector;
329 	return EOK;
330 }
331 
332 
333 int
334 pci_get_max_read_req(device_t dev)
335 {
336 	int cap;
337 	uint16_t val;
338 
339 	if (pci_find_extcap(dev, PCIY_EXPRESS, &cap) != 0)
340 		return (0);
341 	val = pci_read_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, 2);
342 	val &= PCIM_EXP_CTL_MAX_READ_REQUEST;
343 	val >>= 12;
344 	return (1 << (val + 7));
345 }
346 
347 
348 int
349 pci_set_max_read_req(device_t dev, int size)
350 {
351 	int cap;
352 	uint16_t val;
353 
354 	if (pci_find_extcap(dev, PCIY_EXPRESS, &cap) != 0)
355 		return (0);
356 	if (size < 128)
357 		size = 128;
358 	if (size > 4096)
359 		size = 4096;
360 	size = (1 << (fls(size) - 1));
361 	val = pci_read_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, 2);
362 	val &= ~PCIM_EXP_CTL_MAX_READ_REQUEST;
363 	val |= (fls(size) - 8) << 12;
364 	pci_write_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, val, 2);
365 	return (size);
366 }
367 
368 
369 int
370 pci_get_powerstate(device_t dev)
371 {
372 	int capabilityRegister;
373 	uint16 status;
374 	int powerState = PCI_POWERSTATE_D0;
375 
376 	if (pci_find_extcap(dev, PCIY_PMG, &capabilityRegister) != EOK)
377 		return powerState;
378 
379 	status = pci_read_config(dev, capabilityRegister + PCIR_POWER_STATUS, 2);
380 	switch (status & PCI_pm_mask) {
381 		case PCI_pm_state_d0:
382 			break;
383 		case PCI_pm_state_d1:
384 			powerState = PCI_POWERSTATE_D1;
385 			break;
386 		case PCI_pm_state_d2:
387 			powerState = PCI_POWERSTATE_D2;
388 			break;
389 		case PCI_pm_state_d3:
390 			powerState = PCI_POWERSTATE_D3;
391 			break;
392 		default:
393 			powerState = PCI_POWERSTATE_UNKNOWN;
394 			break;
395 	}
396 
397 	TRACE_PCI(dev, "%s: D%i\n", __func__, powerState);
398 	return powerState;
399 }
400 
401 
402 int
403 pci_set_powerstate(device_t dev, int newPowerState)
404 {
405 	int capabilityRegister;
406 	int oldPowerState;
407 	uint8 currentPowerManagementStatus;
408 	uint8 newPowerManagementStatus;
409 	uint16 powerManagementCapabilities;
410 	bigtime_t stateTransitionDelayInUs = 0;
411 
412 	if (pci_find_extcap(dev, PCIY_PMG, &capabilityRegister) != EOK)
413 		return EOPNOTSUPP;
414 
415 	oldPowerState = pci_get_powerstate(dev);
416 	if (oldPowerState == newPowerState)
417 		return EOK;
418 
419 	switch (max_c(oldPowerState, newPowerState)) {
420 		case PCI_POWERSTATE_D2:
421 			stateTransitionDelayInUs = 200;
422 			break;
423 		case PCI_POWERSTATE_D3:
424 			stateTransitionDelayInUs = 10000;
425 			break;
426 	}
427 
428 	currentPowerManagementStatus = pci_read_config(dev, capabilityRegister
429 		+ PCIR_POWER_STATUS, 2);
430 	newPowerManagementStatus = currentPowerManagementStatus & ~PCI_pm_mask;
431 	powerManagementCapabilities = pci_read_config(dev, capabilityRegister
432 		+ PCIR_POWER_CAP, 2);
433 
434 	switch (newPowerState) {
435 		case PCI_POWERSTATE_D0:
436 			newPowerManagementStatus |= PCIM_PSTAT_D0;
437 			break;
438 		case PCI_POWERSTATE_D1:
439 			if ((powerManagementCapabilities & PCI_pm_d1supp) == 0)
440 				return EOPNOTSUPP;
441 			newPowerManagementStatus |= PCIM_PSTAT_D1;
442 			break;
443 		case PCI_POWERSTATE_D2:
444 			if ((powerManagementCapabilities & PCI_pm_d2supp) == 0)
445 				return EOPNOTSUPP;
446 			newPowerManagementStatus |= PCIM_PSTAT_D2;
447 			break;
448 		case PCI_POWERSTATE_D3:
449 			newPowerManagementStatus |= PCIM_PSTAT_D3;
450 			break;
451 		default:
452 			return EINVAL;
453 	}
454 
455 	TRACE_PCI(dev, "%s: D%i -> D%i\n", __func__, oldPowerState, newPowerState);
456 	pci_write_config(dev, capabilityRegister + PCIR_POWER_STATUS,
457 		newPowerManagementStatus, 2);
458 	if (stateTransitionDelayInUs != 0)
459 		snooze(stateTransitionDelayInUs);
460 
461 	return EOK;
462 }
463