1*ce76b8c8Spatrick /* $OpenBSD: acpiiort.c,v 1.9 2022/09/07 18:25:08 patrick Exp $ */
2ffdeadb5Spatrick /*
3ffdeadb5Spatrick * Copyright (c) 2021 Patrick Wildt <patrick@blueri.se>
4ffdeadb5Spatrick *
5ffdeadb5Spatrick * Permission to use, copy, modify, and distribute this software for any
6ffdeadb5Spatrick * purpose with or without fee is hereby granted, provided that the above
7ffdeadb5Spatrick * copyright notice and this permission notice appear in all copies.
8ffdeadb5Spatrick *
9ffdeadb5Spatrick * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10ffdeadb5Spatrick * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11ffdeadb5Spatrick * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12ffdeadb5Spatrick * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13ffdeadb5Spatrick * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14ffdeadb5Spatrick * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15ffdeadb5Spatrick * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16ffdeadb5Spatrick */
17ffdeadb5Spatrick
18ffdeadb5Spatrick #include <sys/param.h>
19ffdeadb5Spatrick #include <sys/systm.h>
20ffdeadb5Spatrick #include <sys/device.h>
21ffdeadb5Spatrick
22ffdeadb5Spatrick #include <dev/acpi/acpireg.h>
23ffdeadb5Spatrick #include <dev/acpi/acpivar.h>
247d187974Spatrick #include <dev/acpi/dsdt.h>
25ffdeadb5Spatrick
26ffdeadb5Spatrick #include <arm64/dev/acpiiort.h>
27ffdeadb5Spatrick
28ffdeadb5Spatrick SIMPLEQ_HEAD(, acpiiort_smmu) acpiiort_smmu_list =
29ffdeadb5Spatrick SIMPLEQ_HEAD_INITIALIZER(acpiiort_smmu_list);
30ffdeadb5Spatrick
31ffdeadb5Spatrick int acpiiort_match(struct device *, void *, void *);
32ffdeadb5Spatrick void acpiiort_attach(struct device *, struct device *, void *);
33ffdeadb5Spatrick
34471aeecfSnaddy const struct cfattach acpiiort_ca = {
35ffdeadb5Spatrick sizeof(struct device), acpiiort_match, acpiiort_attach
36ffdeadb5Spatrick };
37ffdeadb5Spatrick
38ffdeadb5Spatrick struct cfdriver acpiiort_cd = {
39ffdeadb5Spatrick NULL, "acpiiort", DV_DULL
40ffdeadb5Spatrick };
41ffdeadb5Spatrick
42ffdeadb5Spatrick int
acpiiort_match(struct device * parent,void * match,void * aux)43ffdeadb5Spatrick acpiiort_match(struct device *parent, void *match, void *aux)
44ffdeadb5Spatrick {
45ffdeadb5Spatrick struct acpi_attach_args *aaa = aux;
46ffdeadb5Spatrick struct acpi_table_header *hdr;
47ffdeadb5Spatrick
48ffdeadb5Spatrick /* If we do not have a table, it is not us */
49ffdeadb5Spatrick if (aaa->aaa_table == NULL)
50ffdeadb5Spatrick return 0;
51ffdeadb5Spatrick
52ffdeadb5Spatrick /* If it is an IORT table, we can attach */
53ffdeadb5Spatrick hdr = (struct acpi_table_header *)aaa->aaa_table;
54ffdeadb5Spatrick if (memcmp(hdr->signature, IORT_SIG, sizeof(IORT_SIG) - 1) != 0)
55ffdeadb5Spatrick return 0;
56ffdeadb5Spatrick
57ffdeadb5Spatrick return 1;
58ffdeadb5Spatrick }
59ffdeadb5Spatrick
60ffdeadb5Spatrick void
acpiiort_attach(struct device * parent,struct device * self,void * aux)61ffdeadb5Spatrick acpiiort_attach(struct device *parent, struct device *self, void *aux)
62ffdeadb5Spatrick {
63ffdeadb5Spatrick struct acpi_attach_args *aaa = aux;
64ffdeadb5Spatrick struct acpi_iort *iort = (struct acpi_iort *)aaa->aaa_table;
65ffdeadb5Spatrick struct acpi_iort_node *node;
66ffdeadb5Spatrick struct acpiiort_attach_args aia;
67ffdeadb5Spatrick uint32_t offset;
68ffdeadb5Spatrick int i;
69ffdeadb5Spatrick
70ffdeadb5Spatrick printf("\n");
71ffdeadb5Spatrick
72ffdeadb5Spatrick memset(&aia, 0, sizeof(aia));
73ffdeadb5Spatrick aia.aia_iot = aaa->aaa_iot;
74ffdeadb5Spatrick aia.aia_memt = aaa->aaa_memt;
75ffdeadb5Spatrick aia.aia_dmat = aaa->aaa_dmat;
76ffdeadb5Spatrick
77ffdeadb5Spatrick offset = iort->offset;
78ffdeadb5Spatrick for (i = 0; i < iort->number_of_nodes; i++) {
79ffdeadb5Spatrick node = (struct acpi_iort_node *)((char *)iort + offset);
80ffdeadb5Spatrick aia.aia_node = node;
81ffdeadb5Spatrick config_found(self, &aia, NULL);
82ffdeadb5Spatrick offset += node->length;
83ffdeadb5Spatrick }
84ffdeadb5Spatrick }
85ffdeadb5Spatrick
86ffdeadb5Spatrick void
acpiiort_smmu_register(struct acpiiort_smmu * as)87ffdeadb5Spatrick acpiiort_smmu_register(struct acpiiort_smmu *as)
88ffdeadb5Spatrick {
89ffdeadb5Spatrick SIMPLEQ_INSERT_TAIL(&acpiiort_smmu_list, as, as_list);
90ffdeadb5Spatrick }
91ffdeadb5Spatrick
9288933a1fSpatrick bus_dma_tag_t
acpiiort_smmu_map(struct acpi_iort_node * node,uint32_t rid,bus_dma_tag_t dmat)9388933a1fSpatrick acpiiort_smmu_map(struct acpi_iort_node *node, uint32_t rid,
9488933a1fSpatrick bus_dma_tag_t dmat)
95ffdeadb5Spatrick {
96ffdeadb5Spatrick struct acpiiort_smmu *as;
97ffdeadb5Spatrick
98ffdeadb5Spatrick SIMPLEQ_FOREACH(as, &acpiiort_smmu_list, as_list) {
9988933a1fSpatrick if (as->as_node == node)
10088933a1fSpatrick return as->as_map(as->as_cookie, rid, dmat);
101ffdeadb5Spatrick }
10288933a1fSpatrick
10388933a1fSpatrick return dmat;
104ffdeadb5Spatrick }
1057d187974Spatrick
106415019ceSpatrick void
acpiiort_smmu_reserve_region(struct acpi_iort_node * node,uint32_t rid,bus_addr_t addr,bus_size_t size)107415019ceSpatrick acpiiort_smmu_reserve_region(struct acpi_iort_node *node, uint32_t rid,
108415019ceSpatrick bus_addr_t addr, bus_size_t size)
109415019ceSpatrick {
110415019ceSpatrick struct acpiiort_smmu *as;
111415019ceSpatrick
112415019ceSpatrick SIMPLEQ_FOREACH(as, &acpiiort_smmu_list, as_list) {
113415019ceSpatrick if (as->as_node == node) {
114415019ceSpatrick as->as_reserve(as->as_cookie, rid, addr, size);
115415019ceSpatrick return;
116415019ceSpatrick }
117415019ceSpatrick }
118415019ceSpatrick }
119415019ceSpatrick
1207d187974Spatrick bus_dma_tag_t
acpiiort_device_map(struct aml_node * root,bus_dma_tag_t dmat)1217d187974Spatrick acpiiort_device_map(struct aml_node *root, bus_dma_tag_t dmat)
1227d187974Spatrick {
1237d187974Spatrick struct acpi_table_header *hdr;
1247d187974Spatrick struct acpi_iort *iort = NULL;
1257d187974Spatrick struct acpi_iort_node *node;
1267d187974Spatrick struct acpi_iort_mapping *map;
1277d187974Spatrick struct acpi_iort_nc_node *nc;
1287d187974Spatrick struct acpi_q *entry;
1293dbfdff0Spatrick struct aml_node *anc;
1307d187974Spatrick uint32_t rid, offset;
1317d187974Spatrick int i;
1327d187974Spatrick
1337d187974Spatrick /* Look for IORT table. */
1347d187974Spatrick SIMPLEQ_FOREACH(entry, &acpi_softc->sc_tables, q_next) {
1357d187974Spatrick hdr = entry->q_table;
1367d187974Spatrick if (strncmp(hdr->signature, IORT_SIG,
1377d187974Spatrick sizeof(hdr->signature)) == 0) {
1387d187974Spatrick iort = entry->q_table;
1397d187974Spatrick break;
1407d187974Spatrick }
1417d187974Spatrick }
1427d187974Spatrick if (iort == NULL)
1437d187974Spatrick return dmat;
1447d187974Spatrick
1457d187974Spatrick /* Find our named component. */
1467d187974Spatrick offset = iort->offset;
1477d187974Spatrick for (i = 0; i < iort->number_of_nodes; i++) {
1487d187974Spatrick node = (struct acpi_iort_node *)((char *)iort + offset);
1497d187974Spatrick if (node->type == ACPI_IORT_NAMED_COMPONENT) {
1507d187974Spatrick nc = (struct acpi_iort_nc_node *)&node[1];
1513dbfdff0Spatrick anc = aml_searchname(acpi_softc->sc_root,
1523dbfdff0Spatrick nc->device_object_name);
1533dbfdff0Spatrick if (anc == root)
1547d187974Spatrick break;
1557d187974Spatrick }
1567d187974Spatrick offset += node->length;
1577d187974Spatrick }
1587d187974Spatrick
1597d187974Spatrick /* No NC found? Weird. */
1607d187974Spatrick if (i >= iort->number_of_nodes)
1617d187974Spatrick return dmat;
1627d187974Spatrick
1637d187974Spatrick /* Find our output base towards SMMU. */
1647d187974Spatrick map = (struct acpi_iort_mapping *)((char *)node + node->mapping_offset);
1657d187974Spatrick for (i = 0; i < node->number_of_mappings; i++) {
1667d187974Spatrick offset = map[i].output_reference;
1677d187974Spatrick
1687d187974Spatrick if (map[i].flags & ACPI_IORT_MAPPING_SINGLE) {
1697d187974Spatrick rid = map[i].output_base;
1707d187974Spatrick break;
1717d187974Spatrick }
1727d187974Spatrick }
1737d187974Spatrick
174*ce76b8c8Spatrick /*
175*ce76b8c8Spatrick * The IORT spec allows NCs to use implementation-defined IDs, whose
176*ce76b8c8Spatrick * interpretation is up to the device driver. For now simply take the
177*ce76b8c8Spatrick * mapping if there's a single one. This might change in the future.
178*ce76b8c8Spatrick */
179*ce76b8c8Spatrick if (i >= node->number_of_mappings && node->number_of_mappings == 1) {
180*ce76b8c8Spatrick i = 0;
181*ce76b8c8Spatrick rid = map[i].output_base;
182*ce76b8c8Spatrick }
183*ce76b8c8Spatrick
1847d187974Spatrick /* No mapping found? Even weirder. */
1857d187974Spatrick if (i >= node->number_of_mappings)
1867d187974Spatrick return dmat;
1877d187974Spatrick
1887d187974Spatrick node = (struct acpi_iort_node *)((char *)iort + offset);
189227a6d54Spatrick if (node->type == ACPI_IORT_SMMU || node->type == ACPI_IORT_SMMU_V3)
1907d187974Spatrick return acpiiort_smmu_map(node, rid, dmat);
1917d187974Spatrick
1927d187974Spatrick return dmat;
1937d187974Spatrick }
194