xref: /openbsd/sys/arch/arm64/dev/smmu_acpi.c (revision d415bd75)
1 /* $OpenBSD: smmu_acpi.c,v 1.7 2022/09/08 19:30:05 kettenis 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
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
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
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 		sc->sc_is_qcom = 1;
157 
158 	return 0;
159 }
160