[PATCH 2/5] iommu/mediatek: Add mt8173 IOMMU driver

Tomasz Figa tfiga at chromium.org
Mon Mar 9 04:11:40 PDT 2015


Hi,

You can find part 2 of my comments inline.

On Fri, Mar 6, 2015 at 7:48 PM,  <yong.wu at mediatek.com> wrote:

[snip]

> +static irqreturn_t mtk_iommu_isr(int irq, void *dev_id)
> +{
> +       struct iommu_domain *domain = dev_id;
> +       struct mtk_iommu_domain *mtkdomain = domain->priv;
> +       struct mtk_iommu_info *piommu = mtkdomain->piommuinfo;
> +
> +       if (irq == piommu->irq)
> +               report_iommu_fault(domain, piommu->dev, 0, 0);

In addition to my previous comment about how this gets called from
this handler, you need to keep in mind that the function called by
report_iommu_fault() might actually be a different function than
mtk_iommu_fault_handler(), because upper layers can provide their own
handlers. This means that you need to perform any operations on
hardware from this handler and only use the iommu fault handler as a
way to tell an upper layer about the fault (including notifying the
user through kernel log if there is no special handler installed and
the generic fallback is used).

> +       else
> +               dev_err(piommu->dev, "irq number:%d\n", irq);
> +
> +       return IRQ_HANDLED;
> +}

[snip]

> +static int mtk_iommu_fault_handler(struct iommu_domain *imudomain,
> +                                  struct device *dev, unsigned long iova,
> +                                  int m4uindex, void *pimu)
> +{
> +       void __iomem *m4u_base;
> +       u32 int_state, regval;
> +       int m4u_slave_id = 0;
> +       unsigned int layer, write, m4u_port;
> +       unsigned int fault_mva, fault_pa;
> +       struct mtk_iommu_info *piommu = pimu;
> +       struct mtk_iommu_domain *mtkdomain = imudomain->priv;
> +
> +       m4u_base = piommu->m4u_base;
> +       int_state = readl(m4u_base + REG_MMU_MAIN_FAULT_ST);
> +
> +       /* read error info from registers */
> +       fault_mva = readl(m4u_base + REG_MMU_FAULT_VA(m4u_slave_id));
> +       layer = !!(fault_mva & F_MMU_FAULT_VA_LAYER_BIT);
> +       write = !!(fault_mva & F_MMU_FAULT_VA_WRITE_BIT);
> +       fault_mva &= F_MMU_FAULT_VA_MSK;
> +       fault_pa = readl(m4u_base + REG_MMU_INVLD_PA(m4u_slave_id));
> +       regval = readl(m4u_base + REG_MMU_INT_ID(m4u_slave_id));
> +       regval &= F_MMU0_INT_ID_TF_MSK;
> +       m4u_port = mtk_iommu_get_port_by_tfid(piommu, regval);
> +
> +       if (int_state & F_INT_TRANSLATION_FAULT(m4u_slave_id)) {
> +               struct m4u_pte_info_t pte;
> +               unsigned long flags;
> +
> +               spin_lock_irqsave(&mtkdomain->pgtlock, flags);
> +               m4u_get_pte_info(mtkdomain, fault_mva, &pte);
> +               spin_unlock_irqrestore(&mtkdomain->pgtlock, flags);
> +
> +               if (pte.size == MMU_SMALL_PAGE_SIZE ||
> +                   pte.size == MMU_LARGE_PAGE_SIZE) {
> +                       dev_err_ratelimited(
> +                               dev,
> +                               "fault:port=%s iova=0x%x pa=0x%x layer=%d %s;"
> +                               "pgd(0x%x)->pte(0x%x)->pa(%pad)sz(0x%x)Valid(%d)\n",
> +                               mtk_iommu_get_port_name(piommu, m4u_port),
> +                               fault_mva, fault_pa, layer,
> +                               write ? "write" : "read",
> +                               imu_pgd_val(*pte.pgd), imu_pte_val(*pte.pte),
> +                               &pte.pa, pte.size, pte.valid);
> +               } else {
> +                       dev_err_ratelimited(
> +                               dev,
> +                               "fault:port=%s iova=0x%x pa=0x%x layer=%d %s;"
> +                               "pgd(0x%x)->pa(%pad)sz(0x%x)Valid(%d)\n",
> +                               mtk_iommu_get_port_name(piommu, m4u_port),
> +                               fault_mva, fault_pa, layer,
> +                               write ? "write" : "read",
> +                               imu_pgd_val(*pte.pgd),
> +                               &pte.pa, pte.size, pte.valid);
> +               }
> +       }
> +
> +       if (int_state & F_INT_MAIN_MULTI_HIT_FAULT(m4u_slave_id))
> +               dev_err_ratelimited(dev, "multi-hit!port=%s iova=0x%x\n",
> +                                   mtk_iommu_get_port_name(piommu, m4u_port),
> +                                   fault_mva);
> +
> +       if (int_state & F_INT_INVALID_PA_FAULT(m4u_slave_id)) {
> +               if (!(int_state & F_INT_TRANSLATION_FAULT(m4u_slave_id)))
> +                       dev_err_ratelimited(dev, "invalid pa!port=%s iova=0x%x\n",
> +                                           mtk_iommu_get_port_name(piommu,
> +                                                                   m4u_port),
> +                                           fault_mva);
> +       }
> +       if (int_state & F_INT_ENTRY_REPLACEMENT_FAULT(m4u_slave_id))
> +               dev_err_ratelimited(dev, "replace-fault!port=%s iova=0x%x\n",
> +                                   mtk_iommu_get_port_name(piommu, m4u_port),
> +                                   fault_mva);
> +
> +       if (int_state & F_INT_TLB_MISS_FAULT(m4u_slave_id))
> +               dev_err_ratelimited(dev, "tlb miss-fault!port=%s iova=0x%x\n",
> +                                   mtk_iommu_get_port_name(piommu, m4u_port),
> +                                   fault_mva);
> +
> +       mtk_iommu_invalidate_tlb(piommu, 1, 0, 0);
> +
> +       mtk_iommu_clear_intr(m4u_base);

As per my comment above, most of what is happening in this function
should be actually done in interrupt handler, excluding printing of
course.

> +
> +       return 0;
> +}
> +
> +static int mtk_iommu_parse_dt(struct platform_device *pdev,
> +                             struct mtk_iommu_info *piommu)
> +{
> +       struct device *piommudev = &pdev->dev;
> +       struct device_node *ofnode;
> +       struct resource *res;
> +       unsigned int mtk_iommu_cell = 0;
> +       unsigned int i;
> +
> +       ofnode = piommudev->of_node;

You could do this assignment on the same line as declaration.

nit: The usual naming convention for such variables are "dev" for
struct device * and "np" for struct device_node *.

> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       piommu->m4u_base = devm_ioremap_resource(&pdev->dev, res);

nit: There doesn't seem to be any other "bases" involved in this
driver. Could you simply name the field "base", without the obvious
prefix "m4u_"?

> +       if (IS_ERR(piommu->m4u_base)) {
> +               dev_err(piommudev, "m4u_base %p err\n", piommu->m4u_base);
> +               goto iommu_dts_err;
> +       }
> +
> +       piommu->irq = platform_get_irq(pdev, 0);
> +       if (piommu->irq < 0) {
> +               dev_err(piommudev, "irq err %d\n", piommu->irq);

Please keep the messages human readable, e.g. "Failed to get IRQ (%d)\n"

> +               goto iommu_dts_err;
> +       }
> +
> +       piommu->m4u_infra_clk = devm_clk_get(piommudev, "infra_m4u");
> +       if (IS_ERR(piommu->m4u_infra_clk)) {
> +               dev_err(piommudev, "clk err %p\n", piommu->m4u_infra_clk);

"Failed to get clock 'infra_m4u' (%d)\n" (and extract the error code
using PTR_ERR() helper.

> +               goto iommu_dts_err;
> +       }
> +
> +       of_property_read_u32(ofnode, "#iommu-cells", &mtk_iommu_cell);
> +       if (mtk_iommu_cell != 1) {
> +               dev_err(piommudev, "iommu-cell fail:%d\n", mtk_iommu_cell);
> +               goto iommu_dts_err;
> +       }

I don't think you should be checking this here. The function
performing translation from phandle and specifier should check whether
the correct cell count was provided.

> +
> +       for (i = 0; i < piommu->imucfg->larb_nr; i++) {
> +               struct device_node *larbnode;
> +
> +               larbnode = of_parse_phandle(ofnode, "larb", i);
> +               piommu->larbpdev[i] = of_find_device_by_node(larbnode);
> +               of_node_put(larbnode);

I need to take a look at further changes, but this looks like syscon
should be used here instead of using the device directly.

> +               if (!piommu->larbpdev[i]) {
> +                       dev_err(piommudev, "larb pdev fail at larb%d\n", i);
> +                       goto iommu_dts_err;
> +               }
> +       }
> +
> +       return 0;
> +
> +iommu_dts_err:
> +       return -EINVAL;

Please return the return value that actually brought us here.

> +}
> +
> +static int mtk_iommu_hw_init(const struct mtk_iommu_domain *mtkdomain)
> +{
> +       struct mtk_iommu_info *piommu = mtkdomain->piommuinfo;
> +       void __iomem *gm4ubaseaddr = piommu->m4u_base;

Hmm, gm4ubaseaddr is exactly as long as piommu->base (if you follow my
comment to rename this field). In general,

> +       phys_addr_t protectpa;
> +       u32 regval, protectreg;
> +       int ret = 0;
> +
> +       ret = clk_prepare_enable(piommu->m4u_infra_clk);
> +       if (ret) {
> +               dev_err(piommu->dev, "m4u clk enable error\n");
> +               return -ENODEV;
> +       }
> +
> +       writel((u32)mtkdomain->pgd_pa, gm4ubaseaddr + REG_MMUG_PT_BASE);

Why this has to be casted? Is the type of pgd_pa field correct?

> +
> +       regval = F_MMU_CTRL_REROUTE_PFQ_TO_MQ_EN |
> +               F_MMU_CTRL_TF_PROT_VAL(2) |
> +               F_MMU_CTRL_COHERE_EN;
> +       writel(regval, gm4ubaseaddr + REG_MMU_CTRL_REG);
> +
> +       writel(0x6f, gm4ubaseaddr + REG_MMU_INT_L2_CONTROL);
> +       writel(0xffffffff, gm4ubaseaddr + REG_MMU_INT_MAIN_CONTROL);

Please define all the bitfields and use the definitions here instead
of magic numbers.

> +
> +       /* protect memory,HW will write here while translation fault */
> +       protectpa = __virt_to_phys(piommu->protect_va);

Why the underscore variant? virt_to_phys() should be just fine.

> +       protectpa = ALIGN(protectpa, MTK_PROTECT_PA_ALIGN);

Shouldn't protect_va be already aligned?

> +       protectreg = (u32)F_MMU_IVRP_PA_SET(protectpa);

Again, why is this cast needed?

> +       writel(protectreg, gm4ubaseaddr + REG_MMU_IVRP_PADDR);
> +
> +       writel(0, gm4ubaseaddr + REG_MMU_DCM_DIS);
> +       writel(0, gm4ubaseaddr + REG_MMU_STANDARD_AXI_MODE);
> +
> +       return 0;
> +}
> +
> +static inline void mtk_iommu_config_port(struct mtk_iommu_info *piommu,
> +                                        int portid)

Please let the compiler decide whether to inline this function or not.

> +{
> +       int larb, larb_port;
> +
> +       larb = piommu->imucfg->pport[portid].larb_id;
> +       larb_port = piommu->imucfg->pport[portid].port_id;
> +
> +       mtk_smi_config_port(piommu->larbpdev[larb], larb_port);
> +}
> +
> +/*
> + * pimudev is a global var for dma_alloc_coherent.
> + * It is not accepatable, we will delete it if "domain_alloc" is enabled
> + */
> +static struct device *pimudev;

This is indeed not acceptable. Could you replace dma_alloc_coherent()
with something that doesn't require device pointer, e.g.
alloc_pages()? (Although that would require you to handle cache
maintenance in the driver, due to cached memory allocated.) I need to
think about a better solution for this.

OK. Enough for part 2. Stay tuned for part 3.

Best regards,
Tomasz



More information about the linux-arm-kernel mailing list