1 /* $OpenBSD: acpiiort.c,v 1.9 2022/09/07 18:25:08 patrick Exp $ */
2 /*
3 * Copyright (c) 2021 Patrick Wildt <patrick@blueri.se>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 #include <sys/param.h>
19 #include <sys/systm.h>
20 #include <sys/device.h>
21
22 #include <dev/acpi/acpireg.h>
23 #include <dev/acpi/acpivar.h>
24 #include <dev/acpi/dsdt.h>
25
26 #include <arm64/dev/acpiiort.h>
27
28 SIMPLEQ_HEAD(, acpiiort_smmu) acpiiort_smmu_list =
29 SIMPLEQ_HEAD_INITIALIZER(acpiiort_smmu_list);
30
31 int acpiiort_match(struct device *, void *, void *);
32 void acpiiort_attach(struct device *, struct device *, void *);
33
34 const struct cfattach acpiiort_ca = {
35 sizeof(struct device), acpiiort_match, acpiiort_attach
36 };
37
38 struct cfdriver acpiiort_cd = {
39 NULL, "acpiiort", DV_DULL
40 };
41
42 int
acpiiort_match(struct device * parent,void * match,void * aux)43 acpiiort_match(struct device *parent, void *match, void *aux)
44 {
45 struct acpi_attach_args *aaa = aux;
46 struct acpi_table_header *hdr;
47
48 /* If we do not have a table, it is not us */
49 if (aaa->aaa_table == NULL)
50 return 0;
51
52 /* If it is an IORT table, we can attach */
53 hdr = (struct acpi_table_header *)aaa->aaa_table;
54 if (memcmp(hdr->signature, IORT_SIG, sizeof(IORT_SIG) - 1) != 0)
55 return 0;
56
57 return 1;
58 }
59
60 void
acpiiort_attach(struct device * parent,struct device * self,void * aux)61 acpiiort_attach(struct device *parent, struct device *self, void *aux)
62 {
63 struct acpi_attach_args *aaa = aux;
64 struct acpi_iort *iort = (struct acpi_iort *)aaa->aaa_table;
65 struct acpi_iort_node *node;
66 struct acpiiort_attach_args aia;
67 uint32_t offset;
68 int i;
69
70 printf("\n");
71
72 memset(&aia, 0, sizeof(aia));
73 aia.aia_iot = aaa->aaa_iot;
74 aia.aia_memt = aaa->aaa_memt;
75 aia.aia_dmat = aaa->aaa_dmat;
76
77 offset = iort->offset;
78 for (i = 0; i < iort->number_of_nodes; i++) {
79 node = (struct acpi_iort_node *)((char *)iort + offset);
80 aia.aia_node = node;
81 config_found(self, &aia, NULL);
82 offset += node->length;
83 }
84 }
85
86 void
acpiiort_smmu_register(struct acpiiort_smmu * as)87 acpiiort_smmu_register(struct acpiiort_smmu *as)
88 {
89 SIMPLEQ_INSERT_TAIL(&acpiiort_smmu_list, as, as_list);
90 }
91
92 bus_dma_tag_t
acpiiort_smmu_map(struct acpi_iort_node * node,uint32_t rid,bus_dma_tag_t dmat)93 acpiiort_smmu_map(struct acpi_iort_node *node, uint32_t rid,
94 bus_dma_tag_t dmat)
95 {
96 struct acpiiort_smmu *as;
97
98 SIMPLEQ_FOREACH(as, &acpiiort_smmu_list, as_list) {
99 if (as->as_node == node)
100 return as->as_map(as->as_cookie, rid, dmat);
101 }
102
103 return dmat;
104 }
105
106 void
acpiiort_smmu_reserve_region(struct acpi_iort_node * node,uint32_t rid,bus_addr_t addr,bus_size_t size)107 acpiiort_smmu_reserve_region(struct acpi_iort_node *node, uint32_t rid,
108 bus_addr_t addr, bus_size_t size)
109 {
110 struct acpiiort_smmu *as;
111
112 SIMPLEQ_FOREACH(as, &acpiiort_smmu_list, as_list) {
113 if (as->as_node == node) {
114 as->as_reserve(as->as_cookie, rid, addr, size);
115 return;
116 }
117 }
118 }
119
120 bus_dma_tag_t
acpiiort_device_map(struct aml_node * root,bus_dma_tag_t dmat)121 acpiiort_device_map(struct aml_node *root, bus_dma_tag_t dmat)
122 {
123 struct acpi_table_header *hdr;
124 struct acpi_iort *iort = NULL;
125 struct acpi_iort_node *node;
126 struct acpi_iort_mapping *map;
127 struct acpi_iort_nc_node *nc;
128 struct acpi_q *entry;
129 struct aml_node *anc;
130 uint32_t rid, offset;
131 int i;
132
133 /* Look for IORT table. */
134 SIMPLEQ_FOREACH(entry, &acpi_softc->sc_tables, q_next) {
135 hdr = entry->q_table;
136 if (strncmp(hdr->signature, IORT_SIG,
137 sizeof(hdr->signature)) == 0) {
138 iort = entry->q_table;
139 break;
140 }
141 }
142 if (iort == NULL)
143 return dmat;
144
145 /* Find our named component. */
146 offset = iort->offset;
147 for (i = 0; i < iort->number_of_nodes; i++) {
148 node = (struct acpi_iort_node *)((char *)iort + offset);
149 if (node->type == ACPI_IORT_NAMED_COMPONENT) {
150 nc = (struct acpi_iort_nc_node *)&node[1];
151 anc = aml_searchname(acpi_softc->sc_root,
152 nc->device_object_name);
153 if (anc == root)
154 break;
155 }
156 offset += node->length;
157 }
158
159 /* No NC found? Weird. */
160 if (i >= iort->number_of_nodes)
161 return dmat;
162
163 /* Find our output base towards SMMU. */
164 map = (struct acpi_iort_mapping *)((char *)node + node->mapping_offset);
165 for (i = 0; i < node->number_of_mappings; i++) {
166 offset = map[i].output_reference;
167
168 if (map[i].flags & ACPI_IORT_MAPPING_SINGLE) {
169 rid = map[i].output_base;
170 break;
171 }
172 }
173
174 /*
175 * The IORT spec allows NCs to use implementation-defined IDs, whose
176 * interpretation is up to the device driver. For now simply take the
177 * mapping if there's a single one. This might change in the future.
178 */
179 if (i >= node->number_of_mappings && node->number_of_mappings == 1) {
180 i = 0;
181 rid = map[i].output_base;
182 }
183
184 /* No mapping found? Even weirder. */
185 if (i >= node->number_of_mappings)
186 return dmat;
187
188 node = (struct acpi_iort_node *)((char *)iort + offset);
189 if (node->type == ACPI_IORT_SMMU || node->type == ACPI_IORT_SMMU_V3)
190 return acpiiort_smmu_map(node, rid, dmat);
191
192 return dmat;
193 }
194