Add stream ID mapping support for PCIe controller found on M2 Pro/Max SoCs.
authorkettenis <kettenis@openbsd.org>
Thu, 28 Dec 2023 13:32:56 +0000 (13:32 +0000)
committerkettenis <kettenis@openbsd.org>
Thu, 28 Dec 2023 13:32:56 +0000 (13:32 +0000)
ok patrick@

sys/arch/arm64/dev/aplpcie.c

index f370014..fae016e 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: aplpcie.c,v 1.17 2023/09/21 20:26:17 kettenis Exp $   */
+/*     $OpenBSD: aplpcie.c,v 1.18 2023/12/28 13:32:56 kettenis Exp $   */
 /*
  * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org>
  *
 #define  PCIE_PORT_RID2SID_VALID       (1U << 31)
 #define  PCIE_PORT_RID2SID_SID_SHIFT   16
 #define  PCIE_PORT_RID2SID_RID_MASK    0x0000ffff
+#define  PCIE_PORT_MAX_RID2SID         64
 
 #define PCIE_T6020_PORT_MSI_DOORBELL_LO        0x016c
 #define PCIE_T6020_PORT_MSI_DOORBELL_HI        0x0170
 #define PCIE_T6020_PORT_PERST          0x082c
+#define PCIE_T6020_PORT_RID2SID(idx)   (0x3000 + (idx) * 4)
+#define  PCIE_T6020_PORT_MAX_RID2SID   512
 #define PCIE_T6020_PORT_MSI_MAP(idx)   (0x3800 + (idx) * 4)
 #define  PCIE_T6020_PORT_MSI_MAP_ENABLE        (1U << 31)
 
@@ -472,7 +475,7 @@ aplpcie_init_port(struct aplpcie_softc *sc, int node)
        /*
         * Clear stream ID mappings.
         */
-       for (idx = 0; idx < 16; idx++)
+       for (idx = 0; idx < PCIE_PORT_MAX_RID2SID; idx++)
                PWRITE4(sc, port, PCIE_PORT_RID2SID(idx), 0);
 
        /* Check if the link is already up. */
@@ -565,7 +568,7 @@ aplpcie_t6020_init_port(struct aplpcie_softc *sc, int node)
        uint32_t *reset_gpio;
        int pwren_gpiolen, reset_gpiolen;
        uint32_t stat;
-       int msi, port, timo;
+       int idx, msi, port, timo;
 
        if (OF_getprop(node, "status", status, sizeof(status)) > 0 &&
            strcmp(status, "disabled") == 0)
@@ -596,6 +599,12 @@ aplpcie_t6020_init_port(struct aplpcie_softc *sc, int node)
        PWRITE4(sc, port, PCIE_T6020_PORT_MSI_DOORBELL_HI,
            sc->sc_msi_doorbell >> 32);
 
+       /*
+        * Clear stream ID mappings.
+        */
+       for (idx = 0; idx < PCIE_T6020_PORT_MAX_RID2SID; idx++)
+               PWRITE4(sc, port, PCIE_T6020_PORT_RID2SID(idx), 0);
+
        /* Check if the link is already up. */
        stat = PREAD4(sc, port, PCIE_PORT_LINK_STAT);
        if (stat & PCIE_PORT_LINK_STAT_UP)
@@ -782,13 +791,76 @@ aplpcie_find_port(struct aplpcie_softc *sc, int bus)
        return -1;
 }
 
+int
+aplpcie_map_rid(struct aplpcie_softc *sc, int port, uint16_t rid, uint32_t sid)
+{
+       uint32_t reg;
+       int idx;
+
+       for (idx = 0; idx < PCIE_PORT_MAX_RID2SID; idx++) {
+               reg = PREAD4(sc, port, PCIE_PORT_RID2SID(idx));
+
+               /* If already mapped, we're done. */
+               if ((reg & PCIE_PORT_RID2SID_RID_MASK) == rid)
+                       return 0;
+
+               /* Is this an empty slot? */
+               if (reg & PCIE_PORT_RID2SID_VALID)
+                       continue;
+
+               /* Map using this slot. */
+               reg = (sid << PCIE_PORT_RID2SID_SID_SHIFT) | rid |
+                   PCIE_PORT_RID2SID_VALID;
+               PWRITE4(sc, port, PCIE_PORT_RID2SID(idx), reg);
+
+               /* Read back to check the slot is implemented. */
+               if (PREAD4(sc, port, PCIE_PORT_RID2SID(idx)) != reg)
+                       return ENODEV;
+               return 0;
+       }
+
+       return ENODEV;
+}
+
+int
+aplpcie_t6020_map_rid(struct aplpcie_softc *sc, int port, uint16_t rid,
+    uint32_t sid)
+{
+       uint32_t reg;
+       int idx;
+
+       for (idx = 0; idx < PCIE_T6020_PORT_MAX_RID2SID; idx++) {
+               reg = PREAD4(sc, port, PCIE_T6020_PORT_RID2SID(idx));
+
+               /* If already mapped, we're done. */
+               if ((reg & PCIE_PORT_RID2SID_RID_MASK) == rid)
+                       return 0;
+
+               /* Is this an empty slot? */
+               if (reg & PCIE_PORT_RID2SID_VALID)
+                       continue;
+
+               /* Map using this slot. */
+               reg = (sid << PCIE_PORT_RID2SID_SID_SHIFT) | rid |
+                   PCIE_PORT_RID2SID_VALID;
+               PWRITE4(sc, port, PCIE_T6020_PORT_RID2SID(idx), reg);
+
+               /* Read back to check the slot is implemented. */
+               if (PREAD4(sc, port, PCIE_T6020_PORT_RID2SID(idx)) != reg)
+                       return ENODEV;
+               return 0;
+       }
+
+       return ENODEV;
+}
+
 int
 aplpcie_probe_device_hook(void *v, struct pci_attach_args *pa)
 {
        struct aplpcie_softc *sc = v;
-       uint32_t phandle, reg, sid;
+       uint32_t phandle, sid;
        uint16_t rid;
-       int idx, port;
+       int error, port;
 
        rid = pci_requester_id(pa->pa_pc, pa->pa_tag);
        pa->pa_dmat = iommu_device_map_pci(sc->sc_node, rid, pa->pa_dmat);
@@ -807,26 +879,19 @@ aplpcie_probe_device_hook(void *v, struct pci_attach_args *pa)
        if (port == -1)
                return EINVAL;
 
-       for (idx = 0; idx < 16; idx++) {
-               reg = PREAD4(sc, port, PCIE_PORT_RID2SID(idx));
-
-               /* If already mapped, we're done. */
-               if ((reg & PCIE_PORT_RID2SID_RID_MASK) == rid)
-                       return 0;
-
-               /* Is this an empty slot? */
-               if (reg & PCIE_PORT_RID2SID_VALID)
-                       continue;
-
-               /* Map using this slot. */
-               reg = (sid << PCIE_PORT_RID2SID_SID_SHIFT) | rid |
-                   PCIE_PORT_RID2SID_VALID;
-               PWRITE4(sc, port, PCIE_PORT_RID2SID(idx), reg);
-               return 0;
+       if (OF_is_compatible(sc->sc_node, "apple,t6020-pcie"))
+               error = aplpcie_t6020_map_rid(sc, port, rid, sid);
+       else
+               error = aplpcie_map_rid(sc, port, rid, sid);
+       if (error) {
+               printf("%s: out of stream ID mapping slots\n",
+                   sc->sc_dev.dv_xname);
        }
 
-       printf("%s: out of stream ID mapping slots\n",
-                   sc->sc_dev.dv_xname);
+       /*
+        * Not all PCI devices do DMA, so don't return an error if we
+        * ran out of stream ID mapping slots.
+        */
        return 0;
 }