2 * Copyright 2013, Jérôme Duval, korli@users.berlios.de.
3 * Copyright 2010, Michael Lotz, mmlr@mlotz.ch. All Rights Reserved.
4 * Distributed under the terms of the MIT License.
8 #include "pci_arch_info.h"
10 #include "pci_private.h"
14 #include <arch/x86/msi.h>
20 static status_t
pci_unconfigure_msix(PCIDev
*device
);
21 static status_t
pci_disable_msix(PCIDev
*device
);
25 pci_ht_msi_map(PCIDev
*device
, uint64 address
)
27 ht_mapping_info
*info
= &device
->arch_info
.ht_mapping
;
28 if (!info
->ht_mapping_capable
)
31 bool enabled
= (info
->control_value
& PCI_ht_command_msi_enable
) != 0;
32 if ((address
!= 0) != enabled
) {
34 info
->control_value
&= ~PCI_ht_command_msi_enable
;
36 if ((address
>> 20) != (info
->address_value
>> 20))
38 dprintf("ht msi mapping enabled\n");
39 info
->control_value
|= PCI_ht_command_msi_enable
;
41 gPCI
->WriteConfig(device
, info
->capability_offset
+ PCI_ht_command
, 2,
48 pci_read_ht_mapping_info(PCIDev
*device
)
53 ht_mapping_info
*info
= &device
->arch_info
.ht_mapping
;
54 info
->ht_mapping_capable
= false;
57 if (gPCI
->FindHTCapability(device
, PCI_ht_command_cap_msi_mapping
,
59 info
->control_value
= gPCI
->ReadConfig(device
, offset
+ PCI_ht_command
,
61 info
->capability_offset
= offset
;
62 info
->ht_mapping_capable
= true;
63 if ((info
->control_value
& PCI_ht_command_msi_fixed
) != 0)
64 info
->address_value
= MSI_ADDRESS_BASE
;
66 info
->address_value
= gPCI
->ReadConfig(device
, offset
67 + PCI_ht_msi_address_high
, 4);
68 info
->address_value
<<= 32;
69 info
->address_value
|= gPCI
->ReadConfig(device
, offset
70 + PCI_ht_msi_address_low
, 4);
72 dprintf("found an ht msi mapping at %#" B_PRIx64
"\n",
79 pci_get_msi_count(uint8 virtualBus
, uint8 _device
, uint8 function
)
86 if (gPCI
->ResolveVirtualBus(virtualBus
, &domain
, &bus
) != B_OK
)
89 PCIDev
*device
= gPCI
->FindDevice(domain
, bus
, _device
, function
);
93 msi_info
*info
= &device
->arch_info
.msi
;
94 if (!info
->msi_capable
)
97 return info
->message_count
;
102 pci_configure_msi(uint8 virtualBus
, uint8 _device
, uint8 function
,
103 uint8 count
, uint8
*startVector
)
105 if (!msi_supported())
106 return B_UNSUPPORTED
;
108 if (count
== 0 || startVector
== NULL
)
113 status_t result
= gPCI
->ResolveVirtualBus(virtualBus
, &domain
, &bus
);
117 PCIDev
*device
= gPCI
->FindDevice(domain
, bus
, _device
, function
);
121 msi_info
*info
= &device
->arch_info
.msi
;
122 if (!info
->msi_capable
)
123 return B_UNSUPPORTED
;
125 if (count
> 32 || count
> info
->message_count
126 || ((count
- 1) & count
) != 0 /* needs to be a power of 2 */) {
130 if (info
->configured_count
!= 0)
133 result
= msi_allocate_vectors(count
, &info
->start_vector
,
134 &info
->address_value
, &info
->data_value
);
138 uint8 offset
= info
->capability_offset
;
139 gPCI
->WriteConfig(device
, offset
+ PCI_msi_address
, 4,
140 info
->address_value
& 0xffffffff);
141 if (info
->control_value
& PCI_msi_control_64bit
) {
142 gPCI
->WriteConfig(device
, offset
+ PCI_msi_address_high
, 4,
143 info
->address_value
>> 32);
144 gPCI
->WriteConfig(device
, offset
+ PCI_msi_data_64bit
, 2,
147 gPCI
->WriteConfig(device
, offset
+ PCI_msi_data
, 2, info
->data_value
);
149 info
->control_value
&= ~PCI_msi_control_mme_mask
;
150 info
->control_value
|= (ffs(count
) - 1) << 4;
151 gPCI
->WriteConfig(device
, offset
+ PCI_msi_control
, 2, info
->control_value
);
153 info
->configured_count
= count
;
154 *startVector
= info
->start_vector
;
160 pci_unconfigure_msi(uint8 virtualBus
, uint8 _device
, uint8 function
)
162 if (!msi_supported())
163 return B_UNSUPPORTED
;
167 status_t result
= gPCI
->ResolveVirtualBus(virtualBus
, &domain
, &bus
);
171 PCIDev
*device
= gPCI
->FindDevice(domain
, bus
, _device
, function
);
176 result
= pci_unconfigure_msix(device
);
177 if (result
!= B_UNSUPPORTED
&& result
!= B_NO_INIT
)
180 msi_info
*info
= &device
->arch_info
.msi
;
181 if (!info
->msi_capable
)
182 return B_UNSUPPORTED
;
184 if (info
->configured_count
== 0)
187 msi_free_vectors(info
->configured_count
, info
->start_vector
);
189 info
->control_value
&= ~PCI_msi_control_mme_mask
;
190 gPCI
->WriteConfig(device
, info
->capability_offset
+ PCI_msi_control
, 2,
191 info
->control_value
);
193 info
->configured_count
= 0;
194 info
->address_value
= 0;
195 info
->data_value
= 0;
201 pci_enable_msi(uint8 virtualBus
, uint8 _device
, uint8 function
)
203 if (!msi_supported())
204 return B_UNSUPPORTED
;
208 status_t result
= gPCI
->ResolveVirtualBus(virtualBus
, &domain
, &bus
);
212 PCIDev
*device
= gPCI
->FindDevice(domain
, bus
, _device
, function
);
216 msi_info
*info
= &device
->arch_info
.msi
;
217 if (!info
->msi_capable
)
218 return B_UNSUPPORTED
;
220 if (info
->configured_count
== 0)
223 // ensure the pinned interrupt is disabled
224 gPCI
->WriteConfig(device
, PCI_command
, 2,
225 gPCI
->ReadConfig(device
, PCI_command
, 2) | PCI_command_int_disable
);
227 // enable msi generation
228 info
->control_value
|= PCI_msi_control_enable
;
229 gPCI
->WriteConfig(device
, info
->capability_offset
+ PCI_msi_control
, 2,
230 info
->control_value
);
232 // enable HT msi mapping (if applicable)
233 pci_ht_msi_map(device
, info
->address_value
);
235 dprintf("msi enabled: 0x%04" B_PRIx32
"\n",
236 gPCI
->ReadConfig(device
, info
->capability_offset
+ PCI_msi_control
, 2));
242 pci_disable_msi(uint8 virtualBus
, uint8 _device
, uint8 function
)
244 if (!msi_supported())
245 return B_UNSUPPORTED
;
249 status_t result
= gPCI
->ResolveVirtualBus(virtualBus
, &domain
, &bus
);
253 PCIDev
*device
= gPCI
->FindDevice(domain
, bus
, _device
, function
);
258 result
= pci_disable_msix(device
);
259 if (result
!= B_UNSUPPORTED
&& result
!= B_NO_INIT
)
262 msi_info
*info
= &device
->arch_info
.msi
;
263 if (!info
->msi_capable
)
264 return B_UNSUPPORTED
;
266 if (info
->configured_count
== 0)
269 // disable HT msi mapping (if applicable)
270 pci_ht_msi_map(device
, 0);
272 // disable msi generation
273 info
->control_value
&= ~PCI_msi_control_enable
;
274 gPCI
->WriteConfig(device
, info
->capability_offset
+ PCI_msi_control
, 2,
275 info
->control_value
);
282 pci_read_msi_info(PCIDev
*device
)
284 if (!msi_supported())
287 msi_info
*info
= &device
->arch_info
.msi
;
288 info
->msi_capable
= false;
289 status_t result
= gPCI
->FindCapability(device
->domain
, device
->bus
,
290 device
->device
, device
->function
, PCI_cap_id_msi
,
291 &info
->capability_offset
);
295 info
->msi_capable
= true;
296 info
->control_value
= gPCI
->ReadConfig(device
->domain
, device
->bus
,
297 device
->device
, device
->function
,
298 info
->capability_offset
+ PCI_msi_control
, 2);
300 = 1 << ((info
->control_value
& PCI_msi_control_mmc_mask
) >> 1);
301 info
->configured_count
= 0;
302 info
->data_value
= 0;
303 info
->address_value
= 0;
308 pci_get_msix_count(uint8 virtualBus
, uint8 _device
, uint8 function
)
310 if (!msi_supported())
315 if (gPCI
->ResolveVirtualBus(virtualBus
, &domain
, &bus
) != B_OK
)
318 PCIDev
*device
= gPCI
->FindDevice(domain
, bus
, _device
, function
);
322 msix_info
*info
= &device
->arch_info
.msix
;
323 if (!info
->msix_capable
)
326 return info
->message_count
;
331 pci_configure_msix(uint8 virtualBus
, uint8 _device
, uint8 function
,
332 uint8 count
, uint8
*startVector
)
334 if (!msi_supported())
335 return B_UNSUPPORTED
;
337 if (count
== 0 || startVector
== NULL
)
342 status_t result
= gPCI
->ResolveVirtualBus(virtualBus
, &domain
, &bus
);
346 PCIDev
*device
= gPCI
->FindDevice(domain
, bus
, _device
, function
);
350 msix_info
*info
= &device
->arch_info
.msix
;
351 if (!info
->msix_capable
)
352 return B_UNSUPPORTED
;
354 if (count
> 32 || count
> info
->message_count
) {
358 if (info
->configured_count
!= 0)
362 size_t tableSize
= info
->message_count
* 16;
364 area_id area
= map_physical_memory("msi table map",
365 device
->info
.u
.h0
.base_registers
[info
->table_bar
],
366 tableSize
+ info
->table_offset
,
367 B_ANY_KERNEL_ADDRESS
, B_READ_AREA
| B_WRITE_AREA
, (void**)&address
);
370 info
->table_area_id
= area
;
371 info
->table_address
= address
+ info
->table_offset
;
373 // and the pba bar if necessary
374 if (info
->table_bar
!= info
->pba_bar
) {
375 area
= map_physical_memory("msi pba map",
376 device
->info
.u
.h0
.base_registers
[info
->pba_bar
],
377 tableSize
+ info
->pba_offset
,
378 B_ANY_KERNEL_ADDRESS
, B_READ_AREA
| B_WRITE_AREA
,
381 delete_area(info
->table_area_id
);
382 info
->table_area_id
= -1;
385 info
->pba_area_id
= area
;
387 info
->pba_area_id
= -1;
388 info
->pba_address
= address
+ info
->pba_offset
;
390 result
= msi_allocate_vectors(count
, &info
->start_vector
,
391 &info
->address_value
, &info
->data_value
);
392 if (result
!= B_OK
) {
393 delete_area(info
->pba_area_id
);
394 delete_area(info
->table_area_id
);
395 info
->pba_area_id
= -1;
396 info
->table_area_id
= -1;
400 // ensure the memory i/o is enabled
401 gPCI
->WriteConfig(device
, PCI_command
, 2,
402 gPCI
->ReadConfig(device
, PCI_command
, 2) | PCI_command_memory
);
404 uint32 data_value
= info
->data_value
;
405 for (uint32 index
= 0; index
< count
; index
++) {
406 volatile uint32
*entry
= (uint32
*)(info
->table_address
+ 16 * index
);
407 *(entry
+ 3) |= PCI_msix_vctrl_mask
;
408 *entry
++ = info
->address_value
& 0xffffffff;
409 *entry
++ = info
->address_value
>> 32;
410 *entry
++ = data_value
++;
411 *entry
&= ~PCI_msix_vctrl_mask
;
414 info
->configured_count
= count
;
415 *startVector
= info
->start_vector
;
416 dprintf("msix configured for %d vectors\n", count
);
422 pci_unconfigure_msix(PCIDev
*device
)
424 msix_info
*info
= &device
->arch_info
.msix
;
425 if (!info
->msix_capable
)
426 return B_UNSUPPORTED
;
428 if (info
->configured_count
== 0)
431 // disable msi-x generation
432 info
->control_value
&= ~PCI_msix_control_enable
;
433 gPCI
->WriteConfig(device
, info
->capability_offset
+ PCI_msix_control
, 2,
434 info
->control_value
);
436 msi_free_vectors(info
->configured_count
, info
->start_vector
);
437 for (uint8 index
= 0; index
< info
->configured_count
; index
++) {
438 volatile uint32
*entry
= (uint32
*)(info
->table_address
+ 16 * index
);
439 if ((*(entry
+ 3) & PCI_msix_vctrl_mask
) == 0)
440 *(entry
+ 3) |= PCI_msix_vctrl_mask
;
443 if (info
->pba_area_id
!= -1)
444 delete_area(info
->pba_area_id
);
445 if (info
->table_area_id
!= -1)
446 delete_area(info
->table_area_id
);
447 info
->pba_area_id
= -1;
448 info
->table_area_id
= -1;
450 info
->configured_count
= 0;
451 info
->address_value
= 0;
452 info
->data_value
= 0;
458 pci_enable_msix(uint8 virtualBus
, uint8 _device
, uint8 function
)
460 if (!msi_supported())
461 return B_UNSUPPORTED
;
465 status_t result
= gPCI
->ResolveVirtualBus(virtualBus
, &domain
, &bus
);
469 PCIDev
*device
= gPCI
->FindDevice(domain
, bus
, _device
, function
);
473 msix_info
*info
= &device
->arch_info
.msix
;
474 if (!info
->msix_capable
)
475 return B_UNSUPPORTED
;
477 if (info
->configured_count
== 0)
480 // ensure the pinned interrupt is disabled
481 gPCI
->WriteConfig(device
, PCI_command
, 2,
482 gPCI
->ReadConfig(device
, PCI_command
, 2) | PCI_command_int_disable
);
484 // enable msi-x generation
485 info
->control_value
|= PCI_msix_control_enable
;
486 gPCI
->WriteConfig(device
, info
->capability_offset
+ PCI_msix_control
, 2,
487 info
->control_value
);
489 // enable HT msi mapping (if applicable)
490 pci_ht_msi_map(device
, info
->address_value
);
492 dprintf("msi-x enabled: 0x%04" B_PRIx32
"\n",
493 gPCI
->ReadConfig(device
, info
->capability_offset
+ PCI_msix_control
, 2));
499 pci_disable_msix(PCIDev
*device
)
501 msix_info
*info
= &device
->arch_info
.msix
;
502 if (!info
->msix_capable
)
503 return B_UNSUPPORTED
;
505 if (info
->configured_count
== 0)
508 // disable HT msi mapping (if applicable)
509 pci_ht_msi_map(device
, 0);
511 // disable msi-x generation
512 info
->control_value
&= ~PCI_msix_control_enable
;
513 gPCI
->WriteConfig(device
, info
->capability_offset
+ PCI_msix_control
, 2,
514 info
->control_value
);
521 pci_read_msix_info(PCIDev
*device
)
523 if (!msi_supported())
526 msix_info
*info
= &device
->arch_info
.msix
;
527 info
->msix_capable
= false;
528 status_t result
= gPCI
->FindCapability(device
->domain
, device
->bus
,
529 device
->device
, device
->function
, PCI_cap_id_msix
,
530 &info
->capability_offset
);
534 info
->msix_capable
= true;
535 info
->control_value
= gPCI
->ReadConfig(device
->domain
, device
->bus
,
536 device
->device
, device
->function
,
537 info
->capability_offset
+ PCI_msix_control
, 2);
539 = (info
->control_value
& PCI_msix_control_table_size
) + 1;
540 info
->configured_count
= 0;
541 info
->data_value
= 0;
542 info
->address_value
= 0;
543 info
->table_area_id
= -1;
544 info
->pba_area_id
= -1;
545 uint32 table_value
= gPCI
->ReadConfig(device
->domain
, device
->bus
,
546 device
->device
, device
->function
,
547 info
->capability_offset
+ PCI_msix_table
, 4);
548 uint32 pba_value
= gPCI
->ReadConfig(device
->domain
, device
->bus
,
549 device
->device
, device
->function
,
550 info
->capability_offset
+ PCI_msix_pba
, 4);
552 info
->table_bar
= table_value
& PCI_msix_bir_mask
;
553 info
->table_offset
= table_value
& PCI_msix_offset_mask
;
554 info
->pba_bar
= pba_value
& PCI_msix_bir_mask
;
555 info
->pba_offset
= pba_value
& PCI_msix_offset_mask
;