1 /*
2  * Copyright (c) 2020 Intel Corporation
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #define DT_DRV_COMPAT intel_vt_d
7 
8 #include <errno.h>
9 
10 #include <kernel.h>
11 #include <arch/cpu.h>
12 
13 #include <soc.h>
14 #include <device.h>
15 #include <init.h>
16 #include <string.h>
17 
18 #include <zephyr.h>
19 
20 #include <arch/x86/intel_vtd.h>
21 #include <drivers/interrupt_controller/intel_vtd.h>
22 
23 #include "intc_intel_vtd.h"
24 
vtd_write_reg64(const struct device * dev,uint16_t reg,uint64_t value)25 static void vtd_write_reg64(const struct device *dev,
26 			    uint16_t reg, uint64_t value)
27 {
28 	uintptr_t base_address = DEVICE_MMIO_GET(dev);
29 
30 	sys_write64(value, (base_address + reg));
31 }
32 
vtd_read_reg(const struct device * dev,uint16_t reg)33 static uint32_t vtd_read_reg(const struct device *dev, uint16_t reg)
34 {
35 	uintptr_t base_address = DEVICE_MMIO_GET(dev);
36 
37 	return sys_read32(base_address + reg);
38 }
39 
vtd_send_cmd(const struct device * dev,uint16_t cmd_bit,uint16_t status_bit)40 static void vtd_send_cmd(const struct device *dev,
41 			 uint16_t cmd_bit, uint16_t status_bit)
42 {
43 	uintptr_t base_address = DEVICE_MMIO_GET(dev);
44 
45 	sys_set_bit((base_address + VTD_GCMD_REG), cmd_bit);
46 
47 	while (!sys_test_bit((base_address + VTD_GSTS_REG),
48 			     status_bit)) {
49 		/* Do nothing */
50 	}
51 
52 }
53 
vtd_ictl_allocate_entries(const struct device * dev,uint8_t n_entries)54 static int vtd_ictl_allocate_entries(const struct device *dev,
55 				     uint8_t n_entries)
56 {
57 	struct vtd_ictl_data *data = dev->data;
58 	int irte_idx_start;
59 
60 	if ((data->irte_num_used + n_entries) > IRTE_NUM) {
61 		return -EBUSY;
62 	}
63 
64 	irte_idx_start = data->irte_num_used;
65 	data->irte_num_used += n_entries;
66 
67 	return irte_idx_start;
68 }
69 
vtd_ictl_remap_msi(const struct device * dev,msi_vector_t * vector)70 static uint32_t vtd_ictl_remap_msi(const struct device *dev,
71 				   msi_vector_t *vector)
72 {
73 	return VTD_MSI_MAP(vector->arch.irte);
74 }
75 
vtd_ictl_remap(const struct device * dev,msi_vector_t * vector)76 static int vtd_ictl_remap(const struct device *dev,
77 			  msi_vector_t *vector)
78 {
79 	struct vtd_ictl_data *data = dev->data;
80 	uint8_t irte_idx = vector->arch.irte;
81 
82 	memset(&data->irte[irte_idx], 0, sizeof(struct vtd_irte));
83 
84 	data->irte[irte_idx].l.vector = vector->arch.vector;
85 	data->irte[irte_idx].l.dst_id = arch_curr_cpu()->id;
86 	data->irte[irte_idx].l.present = 1;
87 
88 	return 0;
89 }
90 
vtd_ictl_init(const struct device * dev)91 static int vtd_ictl_init(const struct device *dev)
92 {
93 	struct vtd_ictl_data *data = dev->data;
94 	unsigned int key = irq_lock();
95 	uint64_t eime = 0;
96 	uint64_t irta;
97 
98 	DEVICE_MMIO_MAP(dev, K_MEM_CACHE_NONE);
99 
100 	if (IS_ENABLED(CONFIG_X2APIC)) {
101 		eime = VTD_IRTA_EIME;
102 	}
103 
104 	irta = VTD_IRTA_REG_GEN_CONTENT((uintptr_t)data->irte,
105 					IRTA_SIZE, eime);
106 
107 	vtd_write_reg64(dev, VTD_IRTA_REG, irta);
108 
109 	vtd_send_cmd(dev, VTD_GCMD_SIRTP, VTD_GSTS_SIRTPS);
110 	vtd_send_cmd(dev, VTD_GCMD_IRE, VTD_GSTS_IRES);
111 
112 	printk("Intel VT-D up and running (status 0x%x)\n",
113 	       vtd_read_reg(dev, VTD_GSTS_REG));
114 
115 	irq_unlock(key);
116 
117 	return 0;
118 }
119 
120 static const struct vtd_driver_api vtd_api = {
121 	.allocate_entries = vtd_ictl_allocate_entries,
122 	.remap_msi = vtd_ictl_remap_msi,
123 	.remap = vtd_ictl_remap,
124 };
125 
126 static struct vtd_ictl_data vtd_ictl_data_0;
127 
128 static const struct vtd_ictl_cfg vtd_ictl_cfg_0 = {
129 	DEVICE_MMIO_ROM_INIT(DT_DRV_INST(0)),
130 };
131 
132 DEVICE_DT_INST_DEFINE(0,
133 	      vtd_ictl_init, NULL,
134 	      &vtd_ictl_data_0, &vtd_ictl_cfg_0,
135 	      PRE_KERNEL_1, CONFIG_INTEL_VTD_ICTL_INIT_PRIORITY, &vtd_api);
136