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
init_pci()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
uninit_pci()43 uninit_pci()
44 {
45 if (gPci != NULL)
46 put_module(B_PCI_MODULE_NAME);
47 }
48
49
50 pci_info*
get_device_pci_info(device_t device)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
pci_read_config(device_t dev,int offset,int size)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
pci_write_config(device_t dev,int offset,uint32_t value,int size)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
pci_get_vendor(device_t dev)85 pci_get_vendor(device_t dev)
86 {
87 return pci_read_config(dev, PCI_vendor_id, 2);
88 }
89
90
91 uint16_t
pci_get_device(device_t dev)92 pci_get_device(device_t dev)
93 {
94 return pci_read_config(dev, PCI_device_id, 2);
95 }
96
97
98 uint16_t
pci_get_subvendor(device_t dev)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
pci_get_subdevice(device_t dev)106 pci_get_subdevice(device_t dev)
107 {
108 return pci_read_config(dev, PCI_subsystem_id, 2);
109 }
110
111
112 uint8_t
pci_get_revid(device_t dev)113 pci_get_revid(device_t dev)
114 {
115 return pci_read_config(dev, PCI_revision, 1);
116 }
117
118
119 uint32_t
pci_get_domain(device_t dev)120 pci_get_domain(device_t dev)
121 {
122 return 0;
123 }
124
125 uint32_t
pci_get_devid(device_t dev)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
pci_get_cachelnsz(device_t dev)133 pci_get_cachelnsz(device_t dev)
134 {
135 return pci_read_config(dev, PCI_line_size, 1);
136 }
137
138 uint8_t *
pci_get_ether(device_t dev)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
pci_get_bus(device_t dev)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
pci_get_slot(device_t dev)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
pci_get_function(device_t dev)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
pci_find_dbsf(uint32_t domain,uint8_t bus,uint8_t slot,uint8_t func)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
pci_set_command_bit(device_t dev,uint16_t bit)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
pci_enable_busmaster(device_t dev)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
pci_enable_io(device_t dev,int space)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
pci_find_cap(device_t dev,int capability,int * capreg)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
pci_find_extcap(device_t dev,int capability,int * capreg)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
pci_msi_count(device_t dev)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
pci_alloc_msi(device_t dev,int * count)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
pci_release_msi(device_t dev)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
pci_msix_table_bar(device_t dev)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
pci_msix_count(device_t dev)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
pci_alloc_msix(device_t dev,int * count)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
pci_get_max_read_req(device_t dev)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
pci_set_max_read_req(device_t dev,int size)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
pci_get_powerstate(device_t dev)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
pci_set_powerstate(device_t dev,int newPowerState)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