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
15struct MSIConfiguration {
16	uint64*	fAddress;
17	uint32* fData;
18};
19
20static MSIConfiguration sMSIConfigurations[NUM_IO_VECTORS];
21
22static bool sMSISupported = false;
23static uint32 sBootCPUAPICId = 0;
24
25
26void
27msi_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
40bool
41msi_supported()
42{
43	return sMSISupported;
44}
45
46
47status_t
48msi_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
81void
82msi_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
96void
97msi_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