xref: /openbsd/sys/arch/arm64/dev/acpiiort.c (revision ce76b8c8)
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