1 /* $OpenBSD: smmu_acpi.c,v 1.8 2024/06/19 21:25:41 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 #include <sys/pool.h>
22
23 #include <dev/acpi/acpireg.h>
24 #include <dev/acpi/acpivar.h>
25 #include <dev/acpi/dsdt.h>
26
27 #include <dev/pci/pcivar.h>
28 #include <arm64/dev/acpiiort.h>
29 #include <arm64/dev/smmuvar.h>
30 #include <arm64/dev/smmureg.h>
31
32 struct smmu_acpi_softc {
33 struct smmu_softc sc_smmu;
34 void *sc_gih;
35 };
36
37 int smmu_acpi_match(struct device *, void *, void *);
38 void smmu_acpi_attach(struct device *, struct device *, void *);
39
40 int smmu_acpi_foundqcom(struct aml_node *, void *);
41
42 const struct cfattach smmu_acpi_ca = {
43 sizeof(struct smmu_acpi_softc), smmu_acpi_match, smmu_acpi_attach
44 };
45
46 int
smmu_acpi_match(struct device * parent,void * match,void * aux)47 smmu_acpi_match(struct device *parent, void *match, void *aux)
48 {
49 struct acpiiort_attach_args *aia = aux;
50 struct acpi_iort_node *node = aia->aia_node;
51
52 if (node->type != ACPI_IORT_SMMU)
53 return 0;
54
55 return 1;
56 }
57
58 void
smmu_acpi_attach(struct device * parent,struct device * self,void * aux)59 smmu_acpi_attach(struct device *parent, struct device *self, void *aux)
60 {
61 struct smmu_acpi_softc *asc = (struct smmu_acpi_softc *)self;
62 struct smmu_softc *sc = &asc->sc_smmu;
63 struct acpiiort_attach_args *aia = aux;
64 struct acpi_iort_node *node = aia->aia_node;
65 struct acpi_iort_smmu_node *smmu;
66 struct acpi_iort_smmu_global_interrupt *girq;
67 struct acpi_iort_smmu_context_interrupt *cirq;
68 struct acpiiort_smmu *as;
69 int i;
70
71 smmu = (struct acpi_iort_smmu_node *)&node[1];
72
73 printf(" addr 0x%llx/0x%llx", smmu->base_address, smmu->span);
74
75 sc->sc_dmat = aia->aia_dmat;
76 sc->sc_iot = aia->aia_memt;
77 if (bus_space_map(sc->sc_iot, smmu->base_address, smmu->span,
78 0, &sc->sc_ioh)) {
79 printf(": can't map registers\n");
80 return;
81 }
82
83 switch (smmu->model) {
84 case ACPI_IORT_SMMU_V1:
85 case ACPI_IORT_SMMU_CORELINK_MMU400:
86 case ACPI_IORT_SMMU_CORELINK_MMU401:
87 printf(": SMMUv1 is unsupported\n");
88 break;
89 case ACPI_IORT_SMMU_CORELINK_MMU500:
90 sc->sc_is_mmu500 = 1;
91 case ACPI_IORT_SMMU_V2:
92 case ACPI_IORT_SMMU_CAVIUM_THUNDERX:
93 break;
94 default:
95 printf(": unknown model %u\n", smmu->model);
96 return;
97 }
98
99 if (smmu->flags & ACPI_IORT_SMMU_COHERENT)
100 sc->sc_coherent = 1;
101
102 /* Check for QCOM devices to enable quirk. */
103 aml_find_node(acpi_softc->sc_root, "_HID", smmu_acpi_foundqcom, sc);
104
105 /* FIXME: Don't configure on QCOM until its runtime use is fixed. */
106 if (sc->sc_is_qcom) {
107 printf(": disabled\n");
108 return;
109 }
110
111 if (smmu_attach(sc) != 0)
112 return;
113
114 girq = (struct acpi_iort_smmu_global_interrupt *)
115 ((char *)node + smmu->global_interrupt_offset);
116 asc->sc_gih = acpi_intr_establish(girq->nsgirpt_gsiv,
117 girq->nsgirpt_flags & ACPI_IORT_SMMU_INTR_EDGE ?
118 LR_EXTIRQ_MODE : 0, IPL_TTY, smmu_global_irq,
119 sc, sc->sc_dev.dv_xname);
120 if (asc->sc_gih == NULL)
121 return;
122
123 cirq = (struct acpi_iort_smmu_context_interrupt *)
124 ((char *)node + smmu->context_interrupt_offset);
125 for (i = 0; i < smmu->number_of_context_interrupts; i++) {
126 struct smmu_cb_irq *cbi = malloc(sizeof(*cbi),
127 M_DEVBUF, M_WAITOK);
128 cbi->cbi_sc = sc;
129 cbi->cbi_idx = i;
130 acpi_intr_establish(cirq[i].gsiv,
131 cirq[i].flags & ACPI_IORT_SMMU_INTR_EDGE ?
132 LR_EXTIRQ_MODE : 0, IPL_TTY, smmu_context_irq,
133 cbi, sc->sc_dev.dv_xname);
134 }
135
136 as = malloc(sizeof(*as), M_DEVBUF, M_WAITOK | M_ZERO);
137 as->as_node = node;
138 as->as_cookie = sc;
139 as->as_map = smmu_device_map;
140 as->as_reserve = smmu_reserve_region;
141 acpiiort_smmu_register(as);
142 }
143
144 int
smmu_acpi_foundqcom(struct aml_node * node,void * arg)145 smmu_acpi_foundqcom(struct aml_node *node, void *arg)
146 {
147 struct smmu_softc *sc = (struct smmu_softc *)arg;
148 char cdev[32], dev[32];
149
150 if (acpi_parsehid(node, arg, cdev, dev, sizeof(dev)) != 0)
151 return 0;
152
153 if (strcmp(dev, "QCOM0409") == 0 || /* SC8180X/XP */
154 strcmp(dev, "QCOM0609") == 0 || /* SC8280XP */
155 strcmp(dev, "QCOM0809") == 0 || /* SC7180 */
156 strcmp(dev, "QCOM0C09") == 0) /* X1E80100 */
157 sc->sc_is_qcom = 1;
158
159 return 0;
160 }
161