xref: /openbsd-src/sys/arch/arm64/dev/smmu_acpi.c (revision 3b51aead4a3bf57a11b7626029d51e025878b350)
1*3b51aeadSpatrick /* $OpenBSD: smmu_acpi.c,v 1.8 2024/06/19 21:25:41 patrick Exp $ */
21a30e4e7Spatrick /*
31a30e4e7Spatrick  * Copyright (c) 2021 Patrick Wildt <patrick@blueri.se>
41a30e4e7Spatrick  *
51a30e4e7Spatrick  * Permission to use, copy, modify, and distribute this software for any
61a30e4e7Spatrick  * purpose with or without fee is hereby granted, provided that the above
71a30e4e7Spatrick  * copyright notice and this permission notice appear in all copies.
81a30e4e7Spatrick  *
91a30e4e7Spatrick  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
101a30e4e7Spatrick  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
111a30e4e7Spatrick  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
121a30e4e7Spatrick  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
131a30e4e7Spatrick  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
141a30e4e7Spatrick  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
151a30e4e7Spatrick  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
161a30e4e7Spatrick  */
171a30e4e7Spatrick 
181a30e4e7Spatrick #include <sys/param.h>
191a30e4e7Spatrick #include <sys/systm.h>
201a30e4e7Spatrick #include <sys/device.h>
211a30e4e7Spatrick #include <sys/pool.h>
221a30e4e7Spatrick 
231a30e4e7Spatrick #include <dev/acpi/acpireg.h>
241a30e4e7Spatrick #include <dev/acpi/acpivar.h>
251a30e4e7Spatrick #include <dev/acpi/dsdt.h>
261a30e4e7Spatrick 
271a30e4e7Spatrick #include <dev/pci/pcivar.h>
281a30e4e7Spatrick #include <arm64/dev/acpiiort.h>
291a30e4e7Spatrick #include <arm64/dev/smmuvar.h>
301a30e4e7Spatrick #include <arm64/dev/smmureg.h>
311a30e4e7Spatrick 
321a30e4e7Spatrick struct smmu_acpi_softc {
331a30e4e7Spatrick 	struct smmu_softc	 sc_smmu;
341a30e4e7Spatrick 	void			*sc_gih;
351a30e4e7Spatrick };
361a30e4e7Spatrick 
371a30e4e7Spatrick int smmu_acpi_match(struct device *, void *, void *);
381a30e4e7Spatrick void smmu_acpi_attach(struct device *, struct device *, void *);
391a30e4e7Spatrick 
40d0fbdf68Spatrick int smmu_acpi_foundqcom(struct aml_node *, void *);
41d0fbdf68Spatrick 
42471aeecfSnaddy const struct cfattach smmu_acpi_ca = {
431a30e4e7Spatrick 	sizeof(struct smmu_acpi_softc), smmu_acpi_match, smmu_acpi_attach
441a30e4e7Spatrick };
451a30e4e7Spatrick 
461a30e4e7Spatrick int
smmu_acpi_match(struct device * parent,void * match,void * aux)471a30e4e7Spatrick smmu_acpi_match(struct device *parent, void *match, void *aux)
481a30e4e7Spatrick {
491a30e4e7Spatrick 	struct acpiiort_attach_args *aia = aux;
501a30e4e7Spatrick 	struct acpi_iort_node *node = aia->aia_node;
511a30e4e7Spatrick 
521a30e4e7Spatrick 	if (node->type != ACPI_IORT_SMMU)
531a30e4e7Spatrick 		return 0;
541a30e4e7Spatrick 
551a30e4e7Spatrick 	return 1;
561a30e4e7Spatrick }
571a30e4e7Spatrick 
581a30e4e7Spatrick void
smmu_acpi_attach(struct device * parent,struct device * self,void * aux)591a30e4e7Spatrick smmu_acpi_attach(struct device *parent, struct device *self, void *aux)
601a30e4e7Spatrick {
611a30e4e7Spatrick 	struct smmu_acpi_softc *asc = (struct smmu_acpi_softc *)self;
621a30e4e7Spatrick 	struct smmu_softc *sc = &asc->sc_smmu;
631a30e4e7Spatrick 	struct acpiiort_attach_args *aia = aux;
641a30e4e7Spatrick 	struct acpi_iort_node *node = aia->aia_node;
651a30e4e7Spatrick 	struct acpi_iort_smmu_node *smmu;
661a30e4e7Spatrick 	struct acpi_iort_smmu_global_interrupt *girq;
671a30e4e7Spatrick 	struct acpi_iort_smmu_context_interrupt *cirq;
681a30e4e7Spatrick 	struct acpiiort_smmu *as;
691a30e4e7Spatrick 	int i;
701a30e4e7Spatrick 
711a30e4e7Spatrick 	smmu = (struct acpi_iort_smmu_node *)&node[1];
721a30e4e7Spatrick 
731a30e4e7Spatrick 	printf(" addr 0x%llx/0x%llx", smmu->base_address, smmu->span);
741a30e4e7Spatrick 
751a30e4e7Spatrick 	sc->sc_dmat = aia->aia_dmat;
761a30e4e7Spatrick 	sc->sc_iot = aia->aia_memt;
771a30e4e7Spatrick 	if (bus_space_map(sc->sc_iot, smmu->base_address, smmu->span,
781a30e4e7Spatrick 	    0, &sc->sc_ioh)) {
791a30e4e7Spatrick 		printf(": can't map registers\n");
801a30e4e7Spatrick 		return;
811a30e4e7Spatrick 	}
821a30e4e7Spatrick 
831a30e4e7Spatrick 	switch (smmu->model) {
841a30e4e7Spatrick 	case ACPI_IORT_SMMU_V1:
851a30e4e7Spatrick 	case ACPI_IORT_SMMU_CORELINK_MMU400:
861a30e4e7Spatrick 	case ACPI_IORT_SMMU_CORELINK_MMU401:
871a30e4e7Spatrick 		printf(": SMMUv1 is unsupported\n");
881a30e4e7Spatrick 		break;
891a30e4e7Spatrick 	case ACPI_IORT_SMMU_CORELINK_MMU500:
901a30e4e7Spatrick 		sc->sc_is_mmu500 = 1;
911a30e4e7Spatrick 	case ACPI_IORT_SMMU_V2:
921a30e4e7Spatrick 	case ACPI_IORT_SMMU_CAVIUM_THUNDERX:
931a30e4e7Spatrick 		break;
941a30e4e7Spatrick 	default:
951a30e4e7Spatrick 		printf(": unknown model %u\n", smmu->model);
961a30e4e7Spatrick 		return;
971a30e4e7Spatrick 	}
981a30e4e7Spatrick 
991a30e4e7Spatrick 	if (smmu->flags & ACPI_IORT_SMMU_COHERENT)
1001a30e4e7Spatrick 		sc->sc_coherent = 1;
1011a30e4e7Spatrick 
102d0fbdf68Spatrick 	/* Check for QCOM devices to enable quirk. */
103d0fbdf68Spatrick 	aml_find_node(acpi_softc->sc_root, "_HID", smmu_acpi_foundqcom, sc);
104d0fbdf68Spatrick 
105e62200aeSpatrick 	/* FIXME: Don't configure on QCOM until its runtime use is fixed. */
106e62200aeSpatrick 	if (sc->sc_is_qcom) {
107e62200aeSpatrick 		printf(": disabled\n");
108e62200aeSpatrick 		return;
109e62200aeSpatrick 	}
110e62200aeSpatrick 
1111a30e4e7Spatrick 	if (smmu_attach(sc) != 0)
1121a30e4e7Spatrick 		return;
1131a30e4e7Spatrick 
1141a30e4e7Spatrick 	girq = (struct acpi_iort_smmu_global_interrupt *)
1151a30e4e7Spatrick 	    ((char *)node + smmu->global_interrupt_offset);
1161a30e4e7Spatrick 	asc->sc_gih = acpi_intr_establish(girq->nsgirpt_gsiv,
1171a30e4e7Spatrick 	    girq->nsgirpt_flags & ACPI_IORT_SMMU_INTR_EDGE ?
1181a30e4e7Spatrick 	    LR_EXTIRQ_MODE : 0, IPL_TTY, smmu_global_irq,
1191a30e4e7Spatrick 	    sc, sc->sc_dev.dv_xname);
1201a30e4e7Spatrick 	if (asc->sc_gih == NULL)
1211a30e4e7Spatrick 		return;
1221a30e4e7Spatrick 
1231a30e4e7Spatrick 	cirq = (struct acpi_iort_smmu_context_interrupt *)
1241a30e4e7Spatrick 	    ((char *)node + smmu->context_interrupt_offset);
1251a30e4e7Spatrick 	for (i = 0; i < smmu->number_of_context_interrupts; i++) {
1261a30e4e7Spatrick 		struct smmu_cb_irq *cbi = malloc(sizeof(*cbi),
1271a30e4e7Spatrick 		    M_DEVBUF, M_WAITOK);
1281a30e4e7Spatrick 		cbi->cbi_sc = sc;
1291a30e4e7Spatrick 		cbi->cbi_idx = i;
1301a30e4e7Spatrick 		acpi_intr_establish(cirq[i].gsiv,
1311a30e4e7Spatrick 		    cirq[i].flags & ACPI_IORT_SMMU_INTR_EDGE ?
1321a30e4e7Spatrick 		    LR_EXTIRQ_MODE : 0, IPL_TTY, smmu_context_irq,
1331a30e4e7Spatrick 		    cbi, sc->sc_dev.dv_xname);
1341a30e4e7Spatrick 	}
1351a30e4e7Spatrick 
1361a30e4e7Spatrick 	as = malloc(sizeof(*as), M_DEVBUF, M_WAITOK | M_ZERO);
1371a30e4e7Spatrick 	as->as_node = node;
1381a30e4e7Spatrick 	as->as_cookie = sc;
13988933a1fSpatrick 	as->as_map = smmu_device_map;
140415019ceSpatrick 	as->as_reserve = smmu_reserve_region;
1411a30e4e7Spatrick 	acpiiort_smmu_register(as);
1421a30e4e7Spatrick }
143d0fbdf68Spatrick 
144d0fbdf68Spatrick int
smmu_acpi_foundqcom(struct aml_node * node,void * arg)145d0fbdf68Spatrick smmu_acpi_foundqcom(struct aml_node *node, void *arg)
146d0fbdf68Spatrick {
147d0fbdf68Spatrick 	struct smmu_softc	*sc = (struct smmu_softc *)arg;
148d0fbdf68Spatrick 	char			 cdev[32], dev[32];
149d0fbdf68Spatrick 
150d0fbdf68Spatrick 	if (acpi_parsehid(node, arg, cdev, dev, sizeof(dev)) != 0)
151d0fbdf68Spatrick 		return 0;
152d0fbdf68Spatrick 
153ec3fe8a0Skettenis 	if (strcmp(dev, "QCOM0409") == 0 || /* SC8180X/XP */
154ec3fe8a0Skettenis 	    strcmp(dev, "QCOM0609") == 0 || /* SC8280XP */
155*3b51aeadSpatrick 	    strcmp(dev, "QCOM0809") == 0 || /* SC7180 */
156*3b51aeadSpatrick 	    strcmp(dev, "QCOM0C09") == 0) /* X1E80100 */
157d0fbdf68Spatrick 		sc->sc_is_qcom = 1;
158d0fbdf68Spatrick 
159d0fbdf68Spatrick 	return 0;
160d0fbdf68Spatrick }
161