On the Qualcomm SoC as implemented on the Lenovo x13s the BIOS already
authorpatrick <patrick@openbsd.org>
Wed, 10 Aug 2022 17:02:37 +0000 (17:02 +0000)
committerpatrick <patrick@openbsd.org>
Wed, 10 Aug 2022 17:02:37 +0000 (17:02 +0000)
configures and makes use of the SMMU.  We need to keep those mappings
alive as otherwise the machine will die and reboot.  Unfortunately we
cannot simply set those domains to bypass, as when we set a domain to
bypass it is actually set to fault.  Instead reserve a domain and set
it to disabled, which behaves the same as if we used a bypass mapping.

With feedback from kettenis@

sys/arch/arm64/dev/smmu.c
sys/arch/arm64/dev/smmu_acpi.c
sys/arch/arm64/dev/smmuvar.h

index 6849487..e8f103a 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: smmu.c,v 1.18 2021/06/25 19:55:22 patrick Exp $ */
+/* $OpenBSD: smmu.c,v 1.19 2022/08/10 17:02:37 patrick Exp $ */
 /*
  * Copyright (c) 2008-2009,2014-2016 Dale Rahn <drahn@dalerahn.com>
  * Copyright (c) 2021 Patrick Wildt <patrick@blueri.se>
@@ -242,13 +242,48 @@ smmu_attach(struct smmu_softc *sc)
                break;
        }
 
-       printf(": %u CBs (%u S2-only)\n",
+       printf(": %u CBs (%u S2-only)",
            sc->sc_num_context_banks, sc->sc_num_s2_context_banks);
+       if (sc->sc_is_qcom) {
+               /*
+                * In theory we should check if bypass quirk is needed by
+                * modifying S2CR and re-checking if the value is different.
+                * This does not work on the last S2CR, but on the first,
+                * which is in use.  Revisit this once we have other QCOM HW.
+                */
+               sc->sc_bypass_quirk = 1;
+               printf(", bypass quirk");
+               /*
+                * Create special context that is turned off.  This allows us
+                * to map a stream to a context bank where translation is not
+                * happening, and hence bypassed.
+                */
+               sc->sc_cb[sc->sc_num_context_banks - 1] =
+                   malloc(sizeof(struct smmu_cb), M_DEVBUF, M_WAITOK | M_ZERO);
+               smmu_gr1_write_4(sc, SMMU_CBAR(sc->sc_num_context_banks - 1),
+                   SMMU_CBAR_TYPE_S1_TRANS_S2_BYPASS);
+       }
+       printf("\n");
 
        /* Clear Global Fault Status Register */
        smmu_gr0_write_4(sc, SMMU_SGFSR, smmu_gr0_read_4(sc, SMMU_SGFSR));
 
        for (i = 0; i < sc->sc_num_streams; i++) {
+               /* On QCOM HW we need to keep current streams running. */
+               if (sc->sc_is_qcom && sc->sc_smr &&
+                   smmu_gr0_read_4(sc, SMMU_SMR(i)) & SMMU_SMR_VALID) {
+                       sc->sc_smr[i] = malloc(sizeof(struct smmu_smr),
+                           M_DEVBUF, M_WAITOK | M_ZERO);
+                       if (sc->sc_bypass_quirk) {
+                               smmu_gr0_write_4(sc, SMMU_S2CR(i),
+                                   SMMU_S2CR_TYPE_TRANS |
+                                   sc->sc_num_context_banks - 1);
+                       } else {
+                               smmu_gr0_write_4(sc, SMMU_S2CR(i),
+                                   SMMU_S2CR_TYPE_BYPASS | 0xff);
+                       }
+                       continue;
+               }
 #if 1
                /* Setup all streams to fault by default */
                smmu_gr0_write_4(sc, SMMU_S2CR(i), SMMU_S2CR_TYPE_FAULT);
index 72f888b..97b4804 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: smmu_acpi.c,v 1.4 2022/04/06 18:59:26 naddy Exp $ */
+/* $OpenBSD: smmu_acpi.c,v 1.5 2022/08/10 17:02:37 patrick Exp $ */
 /*
  * Copyright (c) 2021 Patrick Wildt <patrick@blueri.se>
  *
@@ -37,6 +37,8 @@ struct smmu_acpi_softc {
 int smmu_acpi_match(struct device *, void *, void *);
 void smmu_acpi_attach(struct device *, struct device *, void *);
 
+int smmu_acpi_foundqcom(struct aml_node *, void *);
+
 const struct cfattach smmu_acpi_ca = {
        sizeof(struct smmu_acpi_softc), smmu_acpi_match, smmu_acpi_attach
 };
@@ -97,6 +99,9 @@ smmu_acpi_attach(struct device *parent, struct device *self, void *aux)
        if (smmu->flags & ACPI_IORT_SMMU_COHERENT)
                sc->sc_coherent = 1;
 
+       /* Check for QCOM devices to enable quirk. */
+       aml_find_node(acpi_softc->sc_root, "_HID", smmu_acpi_foundqcom, sc);
+
        if (smmu_attach(sc) != 0)
                return;
 
@@ -129,3 +134,18 @@ smmu_acpi_attach(struct device *parent, struct device *self, void *aux)
        as->as_reserve = smmu_reserve_region;
        acpiiort_smmu_register(as);
 }
+
+int
+smmu_acpi_foundqcom(struct aml_node *node, void *arg)
+{
+       struct smmu_softc       *sc = (struct smmu_softc *)arg;
+       char                     cdev[32], dev[32];
+
+       if (acpi_parsehid(node, arg, cdev, dev, sizeof(dev)) != 0)
+               return 0;
+
+       if (strcmp(dev, "QCOM0609") == 0)
+               sc->sc_is_qcom = 1;
+
+       return 0;
+}
index 7824c97..e7d97fd 100644 (file)
@@ -1,4 +1,4 @@
-/* $OpenBSD: smmuvar.h,v 1.6 2021/06/25 17:41:22 patrick Exp $ */
+/* $OpenBSD: smmuvar.h,v 1.7 2022/08/10 17:02:37 patrick Exp $ */
 /*
  * Copyright (c) 2021 Patrick Wildt <patrick@blueri.se>
  *
@@ -55,6 +55,8 @@ struct smmu_softc {
        bus_dma_tag_t             sc_dmat;
        int                       sc_is_mmu500;
        int                       sc_is_ap806;
+       int                       sc_is_qcom;
+       int                       sc_bypass_quirk;
        size_t                    sc_pagesize;
        int                       sc_numpage;
        int                       sc_num_context_banks;