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