1 /* 2 * Copyright 2010-2011, Michael Lotz, mmlr@mlotz.ch. All Rights Reserved. 3 * Distributed under the terms of the MIT license. 4 */ 5 6 #include <arch/x86/apic.h> 7 #include <arch/x86/msi.h> 8 #include <arch/x86/arch_smp.h> 9 10 #include <debug.h> 11 #include <int.h> 12 #include <lock.h> 13 14 15 struct MSIConfiguration { 16 uint64* fAddress; 17 uint32* fData; 18 }; 19 20 static MSIConfiguration sMSIConfigurations[NUM_IO_VECTORS]; 21 22 static bool sMSISupported = false; 23 static uint32 sBootCPUAPICId = 0; 24 25 26 void 27 msi_init(kernel_args* args) 28 { 29 if (!apic_available()) { 30 dprintf("disabling msi due to missing apic\n"); 31 return; 32 } 33 34 dprintf("msi support enabled\n"); 35 sMSISupported = true; 36 sBootCPUAPICId = args->arch_args.cpu_apic_id[0]; 37 } 38 39 40 bool 41 msi_supported() 42 { 43 return sMSISupported; 44 } 45 46 47 status_t 48 msi_allocate_vectors(uint32 count, uint32 *startVector, uint64 *address, 49 uint32 *data) 50 { 51 if (!sMSISupported) 52 return B_UNSUPPORTED; 53 54 int32 vector; 55 status_t result = allocate_io_interrupt_vectors(count, &vector, 56 INTERRUPT_TYPE_IRQ); 57 if (result != B_OK) 58 return result; 59 60 if (vector >= NUM_IO_VECTORS) { 61 free_io_interrupt_vectors(count, vector); 62 return B_NO_MEMORY; 63 } 64 65 sMSIConfigurations[vector].fAddress = address; 66 sMSIConfigurations[vector].fData = data; 67 x86_set_irq_source(vector, IRQ_SOURCE_MSI); 68 69 *startVector = (uint32)vector; 70 *address = MSI_ADDRESS_BASE | (sBootCPUAPICId << MSI_DESTINATION_ID_SHIFT) 71 | MSI_NO_REDIRECTION | MSI_DESTINATION_MODE_PHYSICAL; 72 *data = MSI_TRIGGER_MODE_EDGE | MSI_DELIVERY_MODE_FIXED 73 | ((uint16)vector + ARCH_INTERRUPT_BASE); 74 75 dprintf("msi_allocate_vectors: allocated %" B_PRIu32 " vectors starting from %" B_PRIu32 "\n", 76 count, *startVector); 77 return B_OK; 78 } 79 80 81 void 82 msi_free_vectors(uint32 count, uint32 startVector) 83 { 84 if (!sMSISupported) { 85 panic("trying to free msi vectors but msi not supported\n"); 86 return; 87 } 88 89 dprintf("msi_free_vectors: freeing %" B_PRIu32 " vectors starting from %" B_PRIu32 "\n", count, 90 startVector); 91 92 free_io_interrupt_vectors(count, startVector); 93 } 94 95 96 void 97 msi_assign_interrupt_to_cpu(uint32 irq, int32 cpu) 98 { 99 uint32 apic_id = x86_get_cpu_apic_id(cpu); 100 101 uint64* address = sMSIConfigurations[irq].fAddress; 102 *address = MSI_ADDRESS_BASE | (apic_id << MSI_DESTINATION_ID_SHIFT) 103 | MSI_NO_REDIRECTION | MSI_DESTINATION_MODE_PHYSICAL; 104 } 105 106