The branch main has been updated by br:

URL: 
https://cgit.FreeBSD.org/src/commit/?id=41ce5498f8e69e6820962e813eb3b40c465079d0

commit 41ce5498f8e69e6820962e813eb3b40c465079d0
Author:     Ruslan Bukin <[email protected]>
AuthorDate: 2022-05-18 13:11:23 +0000
Commit:     Ruslan Bukin <[email protected]>
CommitDate: 2022-05-18 13:11:23 +0000

    Add OFW support to arm64's IOMMU framework.
    This is needed to support non-PCI devices like memory-mapped
    display controllers.
    Split-out some initialization code from iommu_ctx_alloc() into
    iommu_ctx_init() method so we could pass controller's MD-data
    obtained from DTS to the driver prior to a CTX initialization.
    
    Tested on Morello SoC.
    
    Sponsored by:   UKRI
---
 sys/arm64/iommu/iommu.c    | 163 ++++++++++++++++++++++++++++++++++++++++-----
 sys/arm64/iommu/iommu.h    |   1 +
 sys/arm64/iommu/iommu_if.m |  24 +++++++
 sys/arm64/iommu/smmu.c     |  92 +++++++++++++++++--------
 sys/arm64/iommu/smmu_fdt.c |   2 +
 5 files changed, 239 insertions(+), 43 deletions(-)

diff --git a/sys/arm64/iommu/iommu.c b/sys/arm64/iommu/iommu.c
index 447f3e141610..aa48dcf5ab5e 100644
--- a/sys/arm64/iommu/iommu.c
+++ b/sys/arm64/iommu/iommu.c
@@ -57,6 +57,12 @@ __FBSDID("$FreeBSD$");
 #include <dev/iommu/busdma_iommu.h>
 #include <machine/vmparam.h>
 
+#ifdef FDT
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#endif
+
 #include "iommu.h"
 #include "iommu_if.h"
 
@@ -180,23 +186,149 @@ iommu_tag_init(struct bus_dma_tag_iommu *t)
 }
 
 static struct iommu_ctx *
-iommu_ctx_alloc(device_t dev, struct iommu_domain *iodom, bool disabled)
+iommu_ctx_alloc(device_t requester, struct iommu_domain *iodom, bool disabled)
 {
        struct iommu_unit *iommu;
        struct iommu_ctx *ioctx;
 
        iommu = iodom->iommu;
 
-       ioctx = IOMMU_CTX_ALLOC(iommu->dev, iodom, dev, disabled);
+       ioctx = IOMMU_CTX_ALLOC(iommu->dev, iodom, requester, disabled);
        if (ioctx == NULL)
                return (NULL);
 
+       ioctx->domain = iodom;
+
+       return (ioctx);
+}
+
+static int
+iommu_ctx_init(device_t requester, struct iommu_ctx *ioctx)
+{
+       struct bus_dma_tag_iommu *tag;
+       struct iommu_domain *iodom;
+       struct iommu_unit *iommu;
+       int error;
+
+       iodom = ioctx->domain;
+       iommu = iodom->iommu;
+
+       error = IOMMU_CTX_INIT(iommu->dev, ioctx);
+       if (error)
+               return (error);
+
+       tag = ioctx->tag = malloc(sizeof(struct bus_dma_tag_iommu),
+           M_IOMMU, M_WAITOK | M_ZERO);
+       tag->owner = requester;
+       tag->ctx = ioctx;
+       tag->ctx->domain = iodom;
+
+       iommu_tag_init(tag);
+
+       return (error);
+}
+
+static struct iommu_unit *
+iommu_lookup(device_t dev)
+{
+       struct iommu_entry *entry;
+       struct iommu_unit *iommu;
+
+       IOMMU_LIST_LOCK();
+       LIST_FOREACH(entry, &iommu_list, next) {
+               iommu = entry->iommu;
+               if (iommu->dev == dev) {
+                       IOMMU_LIST_UNLOCK();
+                       return (iommu);
+               }
+       }
+       IOMMU_LIST_UNLOCK();
+
+       return (NULL);
+}
+
+struct iommu_ctx *
+iommu_get_ctx_ofw(device_t dev, int channel)
+{
+       struct iommu_domain *iodom;
+       struct iommu_unit *iommu;
+       struct iommu_ctx *ioctx;
+       phandle_t node, parent;
+       device_t iommu_dev;
+       pcell_t *cells;
+       int niommus;
+       int ncells;
+       int error;
+
+       node = ofw_bus_get_node(dev);
+       if (node <= 0) {
+               device_printf(dev,
+                   "%s called on not ofw based device.\n", __func__);
+               return (NULL);
+       }
+
+       error = ofw_bus_parse_xref_list_get_length(node,
+           "iommus", "#iommu-cells", &niommus);
+       if (error) {
+               device_printf(dev, "%s can't get iommu list.\n", __func__);
+               return (NULL);
+       }
+
+       if (niommus == 0) {
+               device_printf(dev, "%s iommu list is empty.\n", __func__);
+               return (NULL);
+       }
+
+       error = ofw_bus_parse_xref_list_alloc(node, "iommus", "#iommu-cells",
+           channel, &parent, &ncells, &cells);
+       if (error != 0) {
+               device_printf(dev, "%s can't get iommu device xref.\n",
+                   __func__);
+               return (NULL);
+       }
+
+       iommu_dev = OF_device_from_xref(parent);
+       if (iommu_dev == NULL) {
+               device_printf(dev, "%s can't get iommu device.\n", __func__);
+               return (NULL);
+       }
+
+       iommu = iommu_lookup(iommu_dev);
+       if (iommu == NULL) {
+               device_printf(dev, "%s can't lookup iommu.\n", __func__);
+               return (NULL);
+       }
+
        /*
-        * iommu can also be used for non-PCI based devices.
-        * This should be reimplemented as new newbus method with
-        * pci_get_rid() as a default for PCI device class.
+        * In our current configuration we have a domain per each ctx,
+        * so allocate a domain first.
         */
-       ioctx->rid = pci_get_rid(dev);
+       iodom = iommu_domain_alloc(iommu);
+       if (iodom == NULL) {
+               device_printf(dev, "%s can't allocate domain.\n", __func__);
+               return (NULL);
+       }
+
+       ioctx = iommu_ctx_alloc(dev, iodom, false);
+       if (ioctx == NULL) {
+               iommu_domain_free(iodom);
+               return (NULL);
+       }
+
+       ioctx->domain = iodom;
+
+       error = IOMMU_OFW_MD_DATA(iommu->dev, ioctx, cells, ncells);
+       if (error) {
+               device_printf(dev, "%s can't set MD data\n", __func__);
+               return (NULL);
+       }
+
+       error = iommu_ctx_init(dev, ioctx);
+       if (error) {
+               IOMMU_CTX_FREE(iommu->dev, ioctx);
+               iommu_domain_free(iodom);
+               return (NULL);
+       }
 
        return (ioctx);
 }
@@ -205,9 +337,9 @@ struct iommu_ctx *
 iommu_get_ctx(struct iommu_unit *iommu, device_t requester,
     uint16_t rid, bool disabled, bool rmrr)
 {
-       struct iommu_ctx *ioctx;
        struct iommu_domain *iodom;
-       struct bus_dma_tag_iommu *tag;
+       struct iommu_ctx *ioctx;
+       int error;
 
        IOMMU_LOCK(iommu);
        ioctx = IOMMU_CTX_LOOKUP(iommu->dev, requester);
@@ -231,15 +363,12 @@ iommu_get_ctx(struct iommu_unit *iommu, device_t 
requester,
                return (NULL);
        }
 
-       tag = ioctx->tag = malloc(sizeof(struct bus_dma_tag_iommu),
-           M_IOMMU, M_WAITOK | M_ZERO);
-       tag->owner = requester;
-       tag->ctx = ioctx;
-       tag->ctx->domain = iodom;
-
-       iommu_tag_init(tag);
-
-       ioctx->domain = iodom;
+       error = iommu_ctx_init(requester, ioctx);
+       if (error) {
+               IOMMU_CTX_FREE(iommu->dev, ioctx);
+               iommu_domain_free(iodom);
+               return (NULL);
+       }
 
        return (ioctx);
 }
diff --git a/sys/arm64/iommu/iommu.h b/sys/arm64/iommu/iommu.h
index 2071173070df..c221f619b5db 100644
--- a/sys/arm64/iommu/iommu.h
+++ b/sys/arm64/iommu/iommu.h
@@ -40,5 +40,6 @@
 
 int iommu_unregister(struct iommu_unit *unit);
 int iommu_register(struct iommu_unit *unit);
+struct iommu_ctx * iommu_get_ctx_ofw(device_t dev, int channel);
 
 #endif /* _ARM64_IOMMU_IOMMU_H_ */
diff --git a/sys/arm64/iommu/iommu_if.m b/sys/arm64/iommu/iommu_if.m
index ef8c3e7b57b8..6d4f963c5ff1 100644
--- a/sys/arm64/iommu/iommu_if.m
+++ b/sys/arm64/iommu/iommu_if.m
@@ -42,6 +42,12 @@
 #include <dev/pci/pcivar.h>
 #include <dev/iommu/iommu.h>
 
+#ifdef FDT
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#endif
+
 INTERFACE iommu;
 
 #
@@ -116,6 +122,14 @@ METHOD struct iommu_ctx * ctx_alloc {
        bool                    disabled;
 };
 
+#
+# Initialize the new iommu context.
+#
+METHOD int ctx_init {
+       device_t                dev;
+       struct iommu_ctx        *ioctx;
+};
+
 #
 # Free the iommu context.
 #
@@ -123,3 +137,13 @@ METHOD void ctx_free {
        device_t                dev;
        struct iommu_ctx        *ioctx;
 };
+
+#
+# Notify controller we have machine-dependent data.
+#
+METHOD int ofw_md_data {
+       device_t dev;
+       struct iommu_ctx *ioctx;
+       pcell_t *cells;
+       int ncells;
+};
diff --git a/sys/arm64/iommu/smmu.c b/sys/arm64/iommu/smmu.c
index 5d4401c5cee9..57f6826faa5e 100644
--- a/sys/arm64/iommu/smmu.c
+++ b/sys/arm64/iommu/smmu.c
@@ -1844,42 +1844,63 @@ smmu_ctx_alloc(device_t dev, struct iommu_domain 
*iodom, device_t child,
     bool disabled)
 {
        struct smmu_domain *domain;
+       struct smmu_ctx *ctx;
+
+       domain = (struct smmu_domain *)iodom;
+
+       ctx = malloc(sizeof(struct smmu_ctx), M_SMMU, M_WAITOK | M_ZERO);
+       ctx->dev = child;
+       ctx->domain = domain;
+       if (disabled)
+               ctx->bypass = true;
+
+       IOMMU_DOMAIN_LOCK(iodom);
+       LIST_INSERT_HEAD(&domain->ctx_list, ctx, next);
+       IOMMU_DOMAIN_UNLOCK(iodom);
+
+       return (&ctx->ioctx);
+}
+
+static int
+smmu_ctx_init(device_t dev, struct iommu_ctx *ioctx)
+{
+       struct smmu_domain *domain;
+       struct iommu_domain *iodom;
        struct smmu_softc *sc;
        struct smmu_ctx *ctx;
        devclass_t pci_class;
        u_int sid;
        int err;
 
+       ctx = (struct smmu_ctx *)ioctx;
+
        sc = device_get_softc(dev);
-       domain = (struct smmu_domain *)iodom;
 
-       pci_class = devclass_find("pci");
-       if (device_get_devclass(device_get_parent(child)) != pci_class)
-               return (NULL);
+       domain = ctx->domain;
+       iodom = (struct iommu_domain *)domain;
 
+       pci_class = devclass_find("pci");
+       if (device_get_devclass(device_get_parent(ctx->dev)) == pci_class) {
 #ifdef DEV_ACPI
-       err = smmu_pci_get_sid_acpi(child, NULL, &sid);
+               err = smmu_pci_get_sid_acpi(ctx->dev, NULL, &sid);
 #else
-       err = smmu_pci_get_sid_fdt(child, NULL, &sid);
+               err = smmu_pci_get_sid_fdt(ctx->dev, NULL, &sid);
 #endif
-       if (err)
-               return (NULL);
+               if (err)
+                       return (err);
+
+               ioctx->rid = pci_get_rid(dev);
+               ctx->sid = sid;
+               ctx->vendor = pci_get_vendor(ctx->dev);
+               ctx->device = pci_get_device(ctx->dev);
+       }
 
        if (sc->features & SMMU_FEATURE_2_LVL_STREAM_TABLE) {
-               err = smmu_init_l1_entry(sc, sid);
+               err = smmu_init_l1_entry(sc, ctx->sid);
                if (err)
-                       return (NULL);
+                       return (err);
        }
 
-       ctx = malloc(sizeof(struct smmu_ctx), M_SMMU, M_WAITOK | M_ZERO);
-       ctx->vendor = pci_get_vendor(child);
-       ctx->device = pci_get_device(child);
-       ctx->dev = child;
-       ctx->sid = sid;
-       ctx->domain = domain;
-       if (disabled)
-               ctx->bypass = true;
-
        /*
         * Neoverse N1 SDP:
         * 0x800 xhci
@@ -1889,14 +1910,11 @@ smmu_ctx_alloc(device_t dev, struct iommu_domain 
*iodom, device_t child,
 
        smmu_init_ste(sc, domain->cd, ctx->sid, ctx->bypass);
 
-       if (iommu_is_buswide_ctx(iodom->iommu, pci_get_bus(ctx->dev)))
-               smmu_set_buswide(dev, domain, ctx);
+       if (device_get_devclass(device_get_parent(ctx->dev)) == pci_class)
+               if (iommu_is_buswide_ctx(iodom->iommu, pci_get_bus(ctx->dev)))
+                       smmu_set_buswide(dev, domain, ctx);
 
-       IOMMU_DOMAIN_LOCK(iodom);
-       LIST_INSERT_HEAD(&domain->ctx_list, ctx, next);
-       IOMMU_DOMAIN_UNLOCK(iodom);
-
-       return (&ctx->ioctx);
+       return (0);
 }
 
 static void
@@ -1993,6 +2011,24 @@ smmu_find(device_t dev, device_t child)
        return (0);
 }
 
+#ifdef FDT
+static int
+smmu_ofw_md_data(device_t dev, struct iommu_ctx *ioctx, pcell_t *cells,
+    int ncells)
+{
+       struct smmu_ctx *ctx;
+
+       ctx = (struct smmu_ctx *)ioctx;
+
+       if (ncells != 1)
+               return (-1);
+
+       ctx->sid = cells[0];
+
+       return (0);
+}
+#endif
+
 static device_method_t smmu_methods[] = {
        /* Device interface */
        DEVMETHOD(device_detach,        smmu_detach),
@@ -2004,8 +2040,12 @@ static device_method_t smmu_methods[] = {
        DEVMETHOD(iommu_domain_alloc,   smmu_domain_alloc),
        DEVMETHOD(iommu_domain_free,    smmu_domain_free),
        DEVMETHOD(iommu_ctx_alloc,      smmu_ctx_alloc),
+       DEVMETHOD(iommu_ctx_init,       smmu_ctx_init),
        DEVMETHOD(iommu_ctx_free,       smmu_ctx_free),
        DEVMETHOD(iommu_ctx_lookup,     smmu_ctx_lookup),
+#ifdef FDT
+       DEVMETHOD(iommu_ofw_md_data,    smmu_ofw_md_data),
+#endif
 
        /* Bus interface */
        DEVMETHOD(bus_read_ivar,        smmu_read_ivar),
diff --git a/sys/arm64/iommu/smmu_fdt.c b/sys/arm64/iommu/smmu_fdt.c
index f2d441fe8340..e5541b50058f 100644
--- a/sys/arm64/iommu/smmu_fdt.c
+++ b/sys/arm64/iommu/smmu_fdt.c
@@ -176,6 +176,8 @@ smmu_fdt_attach(device_t dev)
                return (ENXIO);
        }
 
+       OF_device_register_xref(OF_xref_from_node(node), dev);
+
        return (0);
 
 error:

Reply via email to