This commit adds System MMU name to the driver data of each System
MMU. It is used by fault information.

Signed-off-by: KyongHo Cho <pullip....@samsung.com>
---
 drivers/iommu/exynos-iommu.c | 100 ++++++++++++++++++++++++++++++++-----------
 1 file changed, 76 insertions(+), 24 deletions(-)

diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
index 985d317..4981afe 100644
--- a/drivers/iommu/exynos-iommu.c
+++ b/drivers/iommu/exynos-iommu.c
@@ -26,6 +26,7 @@
 #include <linux/list.h>
 #include <linux/memblock.h>
 #include <linux/export.h>
+#include <linux/string.h>
 #include <linux/of.h>
 #include <linux/of_platform.h>
 
@@ -150,15 +151,21 @@ enum exynos_sysmmu_inttype {
        SYSMMU_FAULTS_NUM
 };
 
-/*
+/**
+ * fault handler function type
+ * @dev: the client device
+ * @mmuname: name of the System MMU that generates fault
  * @itype: type of fault.
  * @pgtable_base: the physical address of page table base. This is 0 if @itype
  *                is SYSMMU_BUSERROR.
  * @fault_addr: the device (virtual) address that the System MMU tried to
  *             translated. This is 0 if @itype is SYSMMU_BUSERROR.
  */
-typedef int (*sysmmu_fault_handler_t)(enum exynos_sysmmu_inttype itype,
-                       unsigned long pgtable_base, unsigned long fault_addr);
+typedef int (*sysmmu_fault_handler_t)(struct device *dev,
+                                     const char *mmuname,
+                                     enum exynos_sysmmu_inttype itype,
+                                     unsigned long pgtable_base,
+                                     unsigned long fault_addr);
 
 static unsigned short fault_reg_offset[SYSMMU_FAULTS_NUM] = {
        REG_PAGE_FAULT_ADDR,
@@ -234,6 +241,7 @@ struct sysmmu_drvdata {
        sysmmu_fault_handler_t fault_handler;
        unsigned long pgtable;
        bool runtime_active;
+       const char **mmuname;
        void __iomem *sfrbases[0];
 };
 
@@ -611,16 +619,18 @@ void exynos_sysmmu_set_fault_handler(struct device *dev,
        spin_unlock_irqrestore(&owner->lock, flags);
 }
 
-static int default_fault_handler(enum exynos_sysmmu_inttype itype,
-                    unsigned long pgtable_base, unsigned long fault_addr)
+static int default_fault_handler(struct device *dev, const char *mmuname,
+                                       enum exynos_sysmmu_inttype itype,
+                                       unsigned long pgtable_base,
+                                       unsigned long fault_addr)
 {
        unsigned long *ent;
 
        if ((itype >= SYSMMU_FAULTS_NUM) || (itype < SYSMMU_PAGEFAULT))
                itype = SYSMMU_FAULT_UNKNOWN;
 
-       pr_err("%s occurred at 0x%lx(Page table base: 0x%lx)\n",
-                       sysmmu_fault_name[itype], fault_addr, pgtable_base);
+       dev_err(dev, "%s occured at 0x%lx by '%s'(Page table base: 0x%lx)\n",
+               sysmmu_fault_name[itype], fault_addr, mmuname, pgtable_base);
 
        ent = section_entry(__va(pgtable_base), fault_addr);
        pr_err("\tLv1 entry: 0x%lx\n", *ent);
@@ -641,25 +651,30 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void 
*dev_id)
 {
        /* SYSMMU is in blocked when interrupt occurred. */
        struct sysmmu_drvdata *data = dev_id;
-       struct resource *irqres;
-       struct platform_device *pdev;
+       struct exynos_iommu_owner *owner = NULL;
        enum exynos_sysmmu_inttype itype;
        unsigned long addr = -1;
-
+       const char *mmuname = NULL;
        int i, ret = -ENOSYS;
 
-       spin_lock(&data->lock);
+       if (data->master)
+               owner = data->master->archdata.iommu;
+
+       if (owner)
+               spin_lock(&owner->lock);
 
        WARN_ON(!is_sysmmu_active(data));
 
-       pdev = to_platform_device(data->sysmmu);
-       for (i = 0; i < (pdev->num_resources / 2); i++) {
-               irqres = platform_get_resource(pdev, IORESOURCE_IRQ, i);
+       for (i = 0; i < data->nsfrs; i++) {
+               struct resource *irqres;
+               irqres = platform_get_resource(
+                               to_platform_device(data->sysmmu),
+                               IORESOURCE_IRQ, i);
                if (irqres && ((int)irqres->start == irq))
                        break;
        }
 
-       if (i == pdev->num_resources) {
+       if (i == data->nsfrs) {
                itype = SYSMMU_FAULT_UNKNOWN;
        } else {
                itype = (enum exynos_sysmmu_inttype)
@@ -671,28 +686,34 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void 
*dev_id)
                                data->sfrbases[i] + fault_reg_offset[itype]);
        }
 
-       if (data->domain)
-               ret = report_iommu_fault(data->domain, data->master,
+       if (data->domain) /* owner is always set if data->domain exists */
+               ret = report_iommu_fault(data->domain, owner->dev,
                                addr, itype);
 
        if ((ret == -ENOSYS) && data->fault_handler) {
                unsigned long base = data->pgtable;
+               mmuname = (data->mmuname) ?  data->mmuname[i]
+                                               : dev_name(data->sysmmu);
+
                if (itype != SYSMMU_FAULT_UNKNOWN)
                        base = __raw_readl(
                                        data->sfrbases[i] + REG_PT_BASE_ADDR);
-               ret = data->fault_handler(itype, base, addr);
+               ret = data->fault_handler(owner ? owner->dev : data->sysmmu,
+                                       mmuname, itype, base, addr);
        }
 
        if (!ret && (itype != SYSMMU_FAULT_UNKNOWN))
                __raw_writel(1 << itype, data->sfrbases[i] + REG_INT_CLEAR);
        else
-               dev_dbg(data->sysmmu, "%s is not handled.\n",
-                               sysmmu_fault_name[itype]);
+               dev_dbg(owner ? owner->dev : data->sysmmu,
+                               "%s is not handled by %s\n",
+                               sysmmu_fault_name[itype],
+                               dev_name(data->sysmmu));
 
-       if (itype != SYSMMU_FAULT_UNKNOWN)
-               sysmmu_unblock(data->sfrbases[i]);
+       sysmmu_unblock(data->sfrbases[i]);
 
-       spin_unlock(&data->lock);
+       if (owner)
+               spin_unlock(&owner->lock);
 
        return IRQ_HANDLED;
 }
@@ -1050,6 +1071,30 @@ err_dev_put:
        return ret;
 }
 
+static void __init __sysmmu_init_mmuname(struct device *sysmmu,
+                                       struct sysmmu_drvdata *drvdata)
+{
+       int i;
+       const char *mmuname;
+
+       if (of_property_count_strings(sysmmu->of_node, "mmuname") !=
+                                                       drvdata->nsfrs)
+               return;
+
+       drvdata->mmuname = (void *)drvdata->sfrbases +
+                               sizeof(drvdata->sfrbases[0]) * drvdata->nsfrs;
+
+       for (i = 0; i < drvdata->nsfrs; i++) {
+               if (of_property_read_string_index(sysmmu->of_node,
+                                       "mmuname", i, &mmuname))
+                       dev_err(sysmmu, "Failed read mmuname[%d]\n", i);
+               else
+                       drvdata->mmuname[i] = kstrdup(mmuname, GFP_KERNEL);
+               if (!drvdata->mmuname[i])
+                       drvdata->mmuname[i] = "noname";
+       }
+}
+
 static int __init exynos_sysmmu_probe(struct platform_device *pdev)
 {
        int i, ret;
@@ -1061,9 +1106,14 @@ static int __init exynos_sysmmu_probe(struct 
platform_device *pdev)
                return -ENODEV;
        }
 
+       ret = of_property_count_strings(pdev->dev.of_node, "mmuname");
+       if (ret != (int)(pdev->num_resources / 2))
+               ret = 0;
+
        data = devm_kzalloc(dev,
                        sizeof(*data)
-                       + sizeof(*data->sfrbases) * (pdev->num_resources / 2),
+                       + sizeof(*data->sfrbases) * (pdev->num_resources / 2)
+                       + sizeof(*data->mmuname) * ret,
                        GFP_KERNEL);
        if (!data) {
                dev_err(dev, "Not enough memory\n");
@@ -1107,6 +1157,8 @@ static int __init exynos_sysmmu_probe(struct 
platform_device *pdev)
 
        ret = __sysmmu_setup(dev, data);
        if (!ret) {
+               __sysmmu_init_mmuname(dev, data);
+
                data->runtime_active = !pm_runtime_enabled(dev);
                data->sysmmu = dev;
                spin_lock_init(&data->lock);
-- 
1.8.0


--
To unsubscribe from this list: send the line "unsubscribe linux-samsung-soc" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to