This patch adds support for mediatek m4u (MultiMedia Memory Management[...]
Unit).
Signed-off-by: Yong Wu <yong.wu@xxxxxxxxxxxx>
+static void mtk_iommu_flush_pgtable(void *ptr, size_t size, void *cookie)
+{
+ struct mtk_iommu_domain *domain = cookie;
+ unsigned long offset = (unsigned long)ptr & ~PAGE_MASK;
+
+ dma_map_page(domain->data->dev, virt_to_page(ptr), offset,
+ size, DMA_TO_DEVICE);
+static int mtk_iommu_parse_dt(struct platform_device *pdev,
+ struct mtk_iommu_data *data)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *ofnode;
+ struct resource *res;
+ int i;
+
+ ofnode = dev->of_node;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ data->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(data->base))
+ return PTR_ERR(data->base);
+
+ data->irq = platform_get_irq(pdev, 0);
+ if (data->irq < 0)
+ return data->irq;
+
+ data->bclk = devm_clk_get(dev, "bclk");
+ if (IS_ERR(data->bclk))
+ return PTR_ERR(data->bclk);
+
+ data->larb_nr = of_count_phandle_with_args(
+ ofnode, "mediatek,larb", NULL);
+ if (data->larb_nr < 0)
+ return data->larb_nr;
+
+ for (i = 0; i < data->larb_nr; i++) {
+ struct device_node *larbnode;
+ struct platform_device *plarbdev;
+
+ larbnode = of_parse_phandle(ofnode, "mediatek,larb", i);
+ if (!larbnode)
+ return -EINVAL;
+
+ plarbdev = of_find_device_by_node(larbnode);
+ of_node_put(larbnode);
+ if (!plarbdev)
+ return -EPROBE_DEFER;
+ data->larbdev[i] = &plarbdev->dev;
+ }
+ return 0;[...]
+}
+
+static int mtk_iommu_init_domain_context(struct mtk_iommu_domain *dom)
+{
+ int ret;
+
+ if (dom->iop)
+ return 0;
+
+ spin_lock_init(&dom->pgtlock);
+ dom->cfg.quirks = IO_PGTABLE_QUIRK_ARM_NS |
+ IO_PGTABLE_QUIRK_SHORT_SUPERSECTION |
+ IO_PGTABLE_QUIRK_SHORT_MTK;
+ dom->cfg.pgsize_bitmap = mtk_iommu_ops.pgsize_bitmap,
+ dom->cfg.ias = 32;
+ dom->cfg.oas = 32;
+ dom->cfg.tlb = &mtk_iommu_gather_ops;
+
+ dom->iop = alloc_io_pgtable_ops(ARM_SHORT_DESC, &dom->cfg, dom);
+ if (!dom->iop) {
+ pr_err("Failed to alloc io pgtable\n");
+ return -EINVAL;
+ }
+
+ /* Update our support page sizes bitmap */
+ mtk_iommu_ops.pgsize_bitmap = dom->cfg.pgsize_bitmap;
+
+ ret = mtk_iommu_hw_init(dom);
+ if (ret)
+ free_io_pgtable_ops(dom->iop);
+
+ return ret;
+}
+static struct iommu_domain *mtk_iommu_domain_alloc(unsigned type)
+{
+ struct mtk_iommu_domain *priv;
+
+ /* We only support unmanaged domains for now */
+ if (type != IOMMU_DOMAIN_UNMANAGED)
+ return NULL;
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return NULL;
+
+ priv->domain.geometry.aperture_start = 0;
+ priv->domain.geometry.aperture_end = (1ULL << 32) - 1;
+ priv->domain.geometry.force_aperture = true;[...]
+
+ return &priv->domain;
+}
+
+static int mtk_iommu_add_device(struct device *dev)
+{
+ struct mtk_iommu_domain *mtkdom;
+ struct iommu_group *group;
+ struct mtk_iommu_client_priv *devhead;
+ int ret;
+
+ if (!dev->archdata.dma_ops)/* Not a iommu client device */
+ return -ENODEV;
+ group = iommu_group_get(dev);[...]
+ if (!group) {
+ group = iommu_group_alloc();
+ if (IS_ERR(group)) {
+ dev_err(dev, "Failed to allocate IOMMU group\n");
+ return PTR_ERR(group);
+ }
+ }
+
+ ret = iommu_group_add_device(group, dev);
+ if (ret) {
+ dev_err(dev, "Failed to add IOMMU group\n");
+ goto err_group_put;
+ }
+
+ /* get the mtk_iommu_domain from the iommu device */
+ devhead = dev->archdata.iommu;
+ mtkdom = devhead->imudev->archdata.iommu;
+ ret = iommu_attach_group(&mtkdom->domain, group);
+ if (ret)
+ dev_err(dev, "Failed to attach IOMMU group\n");
+
+err_group_put:
+ iommu_group_put(group);
+ return ret;
+}
+
+static struct iommu_ops mtk_iommu_ops = {
+ .domain_alloc = mtk_iommu_domain_alloc,
+ .domain_free = mtk_iommu_domain_free,
+ .attach_dev = mtk_iommu_attach_device,
+ .detach_dev = mtk_iommu_detach_device,
+ .map = mtk_iommu_map,
+ .unmap = mtk_iommu_unmap,
+ .map_sg = default_iommu_map_sg,
+ .iova_to_phys = mtk_iommu_iova_to_phys,
+ .add_device = mtk_iommu_add_device,
+ .of_xlate = mtk_iommu_of_xlate,
+ .pgsize_bitmap = -1UL,
+};[...]
+
+static int mtk_iommu_suspend(struct device *dev)
+{
+ struct mtk_iommu_domain *mtkdom = dev_get_drvdata(dev);
+ struct mtk_iommu_data *data = mtkdom->data;
+ void __iomem *base = data->base;
+ unsigned int i = 0;
+
+ data->reg[i++] = readl(base + REG_MMU_PT_BASE_ADDR);
+ data->reg[i++] = readl(base + REG_MMU_STANDARD_AXI_MODE);
+ data->reg[i++] = readl(base + REG_MMU_DCM_DIS);
+ data->reg[i++] = readl(base + REG_MMU_CTRL_REG);
+ data->reg[i++] = readl(base + REG_MMU_IVRP_PADDR);
+ data->reg[i++] = readl(base + REG_MMU_INT_CONTROL0);
+ data->reg[i++] = readl(base + REG_MMU_INT_MAIN_CONTROL);
+ return 0;
+}
+
+static int mtk_iommu_resume(struct device *dev)
+{
+ struct mtk_iommu_domain *mtkdom = dev_get_drvdata(dev);
+ struct mtk_iommu_data *data = mtkdom->data;
+ void __iomem *base = data->base;
+ unsigned int i = 0;
+
+ writel(data->reg[i++], base + REG_MMU_PT_BASE_ADDR);
+ writel(data->reg[i++], base + REG_MMU_STANDARD_AXI_MODE);
+ writel(data->reg[i++], base + REG_MMU_DCM_DIS);
+ writel(data->reg[i++], base + REG_MMU_CTRL_REG);
+ writel(data->reg[i++], base + REG_MMU_IVRP_PADDR);
+ writel(data->reg[i++], base + REG_MMU_INT_CONTROL0);
+ writel(data->reg[i++], base + REG_MMU_INT_MAIN_CONTROL);
+ return 0;
+}