* [PATCH v6 1/5] dt-bindings: iommu: Add binding for mediatek IOMMU
2015-12-08 9:49 [PATCH v6 0/5] MT8173 IOMMU SUPPORT Yong Wu
@ 2015-12-08 9:49 ` Yong Wu
[not found] ` <1449568153-15643-2-git-send-email-yong.wu-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
2015-12-08 9:49 ` [PATCH v6 2/5] dt-bindings: mediatek: Add smi dts binding Yong Wu
` (3 subsequent siblings)
4 siblings, 1 reply; 24+ messages in thread
From: Yong Wu @ 2015-12-08 9:49 UTC (permalink / raw)
To: Joerg Roedel, Thierry Reding, Mark Rutland, Matthias Brugger
Cc: Robin Murphy, Will Deacon, Daniel Kurtz, Tomasz Figa, Lucas Stach,
Rob Herring, Catalin Marinas, linux-mediatek, Sasha Hauer,
srv_heupstream, devicetree, linux-kernel, linux-arm-kernel, iommu,
pebolle, arnd, mitchelh, p.zabel, yingjoe.chen, Yong Wu
This patch add mediatek iommu dts binding document.
Signed-off-by: Yong Wu <yong.wu@mediatek.com>
---
.../devicetree/bindings/iommu/mediatek,iommu.txt | 68 +++++++++++++
include/dt-bindings/memory/mt8173-larb-port.h | 111 +++++++++++++++++++++
2 files changed, 179 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iommu/mediatek,iommu.txt
create mode 100644 include/dt-bindings/memory/mt8173-larb-port.h
diff --git a/Documentation/devicetree/bindings/iommu/mediatek,iommu.txt b/Documentation/devicetree/bindings/iommu/mediatek,iommu.txt
new file mode 100644
index 0000000..c2fb06e
--- /dev/null
+++ b/Documentation/devicetree/bindings/iommu/mediatek,iommu.txt
@@ -0,0 +1,68 @@
+* Mediatek IOMMU Architecture Implementation
+
+ Some Mediatek SOCs contain a Multimedia Memory Management Unit (M4U) which
+uses the ARM Short-Descriptor translation table format for address translation.
+
+ About the M4U Hardware Block Diagram, please check below:
+
+ EMI (External Memory Interface)
+ |
+ m4u (Multimedia Memory Management Unit)
+ |
+ SMI Common(Smart Multimedia Interface Common)
+ |
+ +----------------+-------
+ | |
+ | |
+ SMI larb0 SMI larb1 ... SoCs have several SMI local arbiter(larb).
+ (display) (vdec)
+ | |
+ | |
+ +-----+-----+ +----+----+
+ | | | | | |
+ | | |... | | | ... There are different ports in each larb.
+ | | | | | |
+OVL0 RDMA0 WDMA0 MC PP VLD
+
+ As above, The Multimedia HW will go through SMI and M4U while it
+access EMI. SMI is a brige between m4u and the Multimedia HW. It contain
+smi local arbiter and smi common. It will control whether the Multimedia
+HW should go though the m4u for translation or bypass it and talk
+directly with EMI. And also SMI help control the power domain and clocks for
+each local arbiter.
+ Normally we specify a local arbiter(larb) for each multimedia HW
+like display, video decode, and camera. And there are different ports
+in each larb. Take a example, There are many ports like MC, PP, VLD in the
+video decode local arbiter, all these ports are according to the video HW.
+
+Required properties:
+- compatible : must be "mediatek,mt8173-m4u".
+- reg : m4u register base and size.
+- interrupts : the interrupt of m4u.
+- clocks : must contain one entry for each clock-names.
+- clock-names : must be "bclk", It is the block clock of m4u.
+- mediatek,larbs : List of phandle to the local arbiters in the current Socs.
+ Refer to bindings/memory-controllers/mediatek,smi-larb.txt. It must sort
+ according to the local arbiter index, like larb0, larb1, larb2...
+- iommu-cells : must be 1. This is the mtk_m4u_id according to the HW.
+ Specifies the mtk_m4u_id as defined in
+ dt-binding/memory/mt8173-larb-port.h.
+
+Example:
+ iommu: iommu@10205000 {
+ compatible = "mediatek,mt8173-m4u";
+ reg = <0 0x10205000 0 0x1000>;
+ interrupts = <GIC_SPI 139 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&infracfg CLK_INFRA_M4U>;
+ clock-names = "bclk";
+ mediatek,larbs = <&larb0 &larb1 &larb2 &larb3 &larb4 &larb5>;
+ #iommu-cells = <1>;
+ };
+
+Example for a client device:
+ display {
+ compatible = "mediatek,mt8173-disp";
+ iommus = <&iommu M4U_PORT_DISP_OVL0>,
+ <&iommu M4U_PORT_DISP_RDMA0>;
+ ...
+ };
diff --git a/include/dt-bindings/memory/mt8173-larb-port.h b/include/dt-bindings/memory/mt8173-larb-port.h
new file mode 100644
index 0000000..50ccb93
--- /dev/null
+++ b/include/dt-bindings/memory/mt8173-larb-port.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 2014-2015 MediaTek Inc.
+ * Author: Yong Wu <yong.wu@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef __DTS_IOMMU_PORT_MT8173_H
+#define __DTS_IOMMU_PORT_MT8173_H
+
+#define MTK_M4U_ID(larb, port) (((larb) << 5) | (port))
+/* Local arbiter ID */
+#define MTK_M4U_TO_LARB(id) (((id) >> 5) & 0x7)
+/* PortID within the local arbiter */
+#define MTK_M4U_TO_PORT(id) ((id) & 0x1f)
+
+#define M4U_LARB0_ID 0
+#define M4U_LARB1_ID 1
+#define M4U_LARB2_ID 2
+#define M4U_LARB3_ID 3
+#define M4U_LARB4_ID 4
+#define M4U_LARB5_ID 5
+
+/* larb0 */
+#define M4U_PORT_DISP_OVL0 MTK_M4U_ID(M4U_LARB0_ID, 0)
+#define M4U_PORT_DISP_RDMA0 MTK_M4U_ID(M4U_LARB0_ID, 1)
+#define M4U_PORT_DISP_WDMA0 MTK_M4U_ID(M4U_LARB0_ID, 2)
+#define M4U_PORT_DISP_OD_R MTK_M4U_ID(M4U_LARB0_ID, 3)
+#define M4U_PORT_DISP_OD_W MTK_M4U_ID(M4U_LARB0_ID, 4)
+#define M4U_PORT_MDP_RDMA0 MTK_M4U_ID(M4U_LARB0_ID, 5)
+#define M4U_PORT_MDP_WDMA MTK_M4U_ID(M4U_LARB0_ID, 6)
+#define M4U_PORT_MDP_WROT0 MTK_M4U_ID(M4U_LARB0_ID, 7)
+
+/* larb1 */
+#define M4U_PORT_HW_VDEC_MC_EXT MTK_M4U_ID(M4U_LARB1_ID, 0)
+#define M4U_PORT_HW_VDEC_PP_EXT MTK_M4U_ID(M4U_LARB1_ID, 1)
+#define M4U_PORT_HW_VDEC_UFO_EXT MTK_M4U_ID(M4U_LARB1_ID, 2)
+#define M4U_PORT_HW_VDEC_VLD_EXT MTK_M4U_ID(M4U_LARB1_ID, 3)
+#define M4U_PORT_HW_VDEC_VLD2_EXT MTK_M4U_ID(M4U_LARB1_ID, 4)
+#define M4U_PORT_HW_VDEC_AVC_MV_EXT MTK_M4U_ID(M4U_LARB1_ID, 5)
+#define M4U_PORT_HW_VDEC_PRED_RD_EXT MTK_M4U_ID(M4U_LARB1_ID, 6)
+#define M4U_PORT_HW_VDEC_PRED_WR_EXT MTK_M4U_ID(M4U_LARB1_ID, 7)
+#define M4U_PORT_HW_VDEC_PPWRAP_EXT MTK_M4U_ID(M4U_LARB1_ID, 8)
+#define M4U_PORT_HW_VDEC_TILE MTK_M4U_ID(M4U_LARB1_ID, 9)
+
+/* larb2 */
+#define M4U_PORT_IMGO MTK_M4U_ID(M4U_LARB2_ID, 0)
+#define M4U_PORT_RRZO MTK_M4U_ID(M4U_LARB2_ID, 1)
+#define M4U_PORT_AAO MTK_M4U_ID(M4U_LARB2_ID, 2)
+#define M4U_PORT_LCSO MTK_M4U_ID(M4U_LARB2_ID, 3)
+#define M4U_PORT_ESFKO MTK_M4U_ID(M4U_LARB2_ID, 4)
+#define M4U_PORT_IMGO_D MTK_M4U_ID(M4U_LARB2_ID, 5)
+#define M4U_PORT_LSCI MTK_M4U_ID(M4U_LARB2_ID, 6)
+#define M4U_PORT_LSCI_D MTK_M4U_ID(M4U_LARB2_ID, 7)
+#define M4U_PORT_BPCI MTK_M4U_ID(M4U_LARB2_ID, 8)
+#define M4U_PORT_BPCI_D MTK_M4U_ID(M4U_LARB2_ID, 9)
+#define M4U_PORT_UFDI MTK_M4U_ID(M4U_LARB2_ID, 10)
+#define M4U_PORT_IMGI MTK_M4U_ID(M4U_LARB2_ID, 11)
+#define M4U_PORT_IMG2O MTK_M4U_ID(M4U_LARB2_ID, 12)
+#define M4U_PORT_IMG3O MTK_M4U_ID(M4U_LARB2_ID, 13)
+#define M4U_PORT_VIPI MTK_M4U_ID(M4U_LARB2_ID, 14)
+#define M4U_PORT_VIP2I MTK_M4U_ID(M4U_LARB2_ID, 15)
+#define M4U_PORT_VIP3I MTK_M4U_ID(M4U_LARB2_ID, 16)
+#define M4U_PORT_LCEI MTK_M4U_ID(M4U_LARB2_ID, 17)
+#define M4U_PORT_RB MTK_M4U_ID(M4U_LARB2_ID, 18)
+#define M4U_PORT_RP MTK_M4U_ID(M4U_LARB2_ID, 19)
+#define M4U_PORT_WR MTK_M4U_ID(M4U_LARB2_ID, 20)
+
+/* larb3 */
+#define M4U_PORT_VENC_RCPU MTK_M4U_ID(M4U_LARB3_ID, 0)
+#define M4U_PORT_VENC_REC MTK_M4U_ID(M4U_LARB3_ID, 1)
+#define M4U_PORT_VENC_BSDMA MTK_M4U_ID(M4U_LARB3_ID, 2)
+#define M4U_PORT_VENC_SV_COMV MTK_M4U_ID(M4U_LARB3_ID, 3)
+#define M4U_PORT_VENC_RD_COMV MTK_M4U_ID(M4U_LARB3_ID, 4)
+#define M4U_PORT_JPGENC_RDMA MTK_M4U_ID(M4U_LARB3_ID, 5)
+#define M4U_PORT_JPGENC_BSDMA MTK_M4U_ID(M4U_LARB3_ID, 6)
+#define M4U_PORT_JPGDEC_WDMA MTK_M4U_ID(M4U_LARB3_ID, 7)
+#define M4U_PORT_JPGDEC_BSDMA MTK_M4U_ID(M4U_LARB3_ID, 8)
+#define M4U_PORT_VENC_CUR_LUMA MTK_M4U_ID(M4U_LARB3_ID, 9)
+#define M4U_PORT_VENC_CUR_CHROMA MTK_M4U_ID(M4U_LARB3_ID, 10)
+#define M4U_PORT_VENC_REF_LUMA MTK_M4U_ID(M4U_LARB3_ID, 11)
+#define M4U_PORT_VENC_REF_CHROMA MTK_M4U_ID(M4U_LARB3_ID, 12)
+#define M4U_PORT_VENC_NBM_RDMA MTK_M4U_ID(M4U_LARB3_ID, 13)
+#define M4U_PORT_VENC_NBM_WDMA MTK_M4U_ID(M4U_LARB3_ID, 14)
+
+/* larb4 */
+#define M4U_PORT_DISP_OVL1 MTK_M4U_ID(M4U_LARB4_ID, 0)
+#define M4U_PORT_DISP_RDMA1 MTK_M4U_ID(M4U_LARB4_ID, 1)
+#define M4U_PORT_DISP_RDMA2 MTK_M4U_ID(M4U_LARB4_ID, 2)
+#define M4U_PORT_DISP_WDMA1 MTK_M4U_ID(M4U_LARB4_ID, 3)
+#define M4U_PORT_MDP_RDMA1 MTK_M4U_ID(M4U_LARB4_ID, 4)
+#define M4U_PORT_MDP_WROT1 MTK_M4U_ID(M4U_LARB4_ID, 5)
+
+/* larb5 */
+#define M4U_PORT_VENC_RCPU_SET2 MTK_M4U_ID(M4U_LARB5_ID, 0)
+#define M4U_PORT_VENC_REC_FRM_SET2 MTK_M4U_ID(M4U_LARB5_ID, 1)
+#define M4U_PORT_VENC_REF_LUMA_SET2 MTK_M4U_ID(M4U_LARB5_ID, 2)
+#define M4U_PORT_VENC_REC_CHROMA_SET2 MTK_M4U_ID(M4U_LARB5_ID, 3)
+#define M4U_PORT_VENC_BSDMA_SET2 MTK_M4U_ID(M4U_LARB5_ID, 4)
+#define M4U_PORT_VENC_CUR_LUMA_SET2 MTK_M4U_ID(M4U_LARB5_ID, 5)
+#define M4U_PORT_VENC_CUR_CHROMA_SET2 MTK_M4U_ID(M4U_LARB5_ID, 6)
+#define M4U_PORT_VENC_RD_COMA_SET2 MTK_M4U_ID(M4U_LARB5_ID, 7)
+#define M4U_PORT_VENC_SV_COMA_SET2 MTK_M4U_ID(M4U_LARB5_ID, 8)
+
+#endif
--
1.8.1.1.dirty
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v6 2/5] dt-bindings: mediatek: Add smi dts binding
2015-12-08 9:49 [PATCH v6 0/5] MT8173 IOMMU SUPPORT Yong Wu
2015-12-08 9:49 ` [PATCH v6 1/5] dt-bindings: iommu: Add binding for mediatek IOMMU Yong Wu
@ 2015-12-08 9:49 ` Yong Wu
[not found] ` <1449568153-15643-3-git-send-email-yong.wu-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
2015-12-08 9:49 ` [PATCH v6 3/5] memory: mediatek: Add SMI driver Yong Wu
` (2 subsequent siblings)
4 siblings, 1 reply; 24+ messages in thread
From: Yong Wu @ 2015-12-08 9:49 UTC (permalink / raw)
To: Joerg Roedel, Thierry Reding, Mark Rutland, Matthias Brugger
Cc: Robin Murphy, Will Deacon, Daniel Kurtz, Tomasz Figa, Lucas Stach,
Rob Herring, Catalin Marinas, linux-mediatek, Sasha Hauer,
srv_heupstream, devicetree, linux-kernel, linux-arm-kernel, iommu,
pebolle, arnd, mitchelh, p.zabel, yingjoe.chen, Yong Wu
This patch add smi binding document.
Signed-off-by: Yong Wu <yong.wu@mediatek.com>
---
.../memory-controllers/mediatek,smi-common.txt | 24 +++++++++++++++++++++
.../memory-controllers/mediatek,smi-larb.txt | 25 ++++++++++++++++++++++
2 files changed, 49 insertions(+)
create mode 100644 Documentation/devicetree/bindings/memory-controllers/mediatek,smi-common.txt
create mode 100644 Documentation/devicetree/bindings/memory-controllers/mediatek,smi-larb.txt
diff --git a/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-common.txt b/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-common.txt
new file mode 100644
index 0000000..06a83ce
--- /dev/null
+++ b/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-common.txt
@@ -0,0 +1,24 @@
+SMI (Smart Multimedia Interface) Common
+
+The hardware block diagram please check bindings/iommu/mediatek,iommu.txt
+
+Required properties:
+- compatible : must be "mediatek,mt8173-smi-common"
+- reg : the register and size of the SMI block.
+- power-domains : a phandle to the power domain of this local arbiter.
+- clocks : Must contain an entry for each entry in clock-names.
+- clock-names : must contain 2 entries, as follows:
+ - "apb" : Advanced Peripheral Bus clock, It's the clock for setting
+ the register.
+ - "smi" : It's the clock for transfer data and command.
+ They may be the same if both source clocks are the same.
+
+Example:
+ smi_common: smi@14022000 {
+ compatible = "mediatek,mt8173-smi-common";
+ reg = <0 0x14022000 0 0x1000>;
+ power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+ clocks = <&mmsys CLK_MM_SMI_COMMON>,
+ <&mmsys CLK_MM_SMI_COMMON>;
+ clock-names = "apb", "smi";
+ };
diff --git a/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-larb.txt b/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-larb.txt
new file mode 100644
index 0000000..55ff3b7
--- /dev/null
+++ b/Documentation/devicetree/bindings/memory-controllers/mediatek,smi-larb.txt
@@ -0,0 +1,25 @@
+SMI (Smart Multimedia Interface) Local Arbiter
+
+The hardware block diagram please check bindings/iommu/mediatek,iommu.txt
+
+Required properties:
+- compatible : must be "mediatek,mt8173-smi-larb"
+- reg : the register and size of this local arbiter.
+- mediatek,smi : a phandle to the smi_common node.
+- power-domains : a phandle to the power domain of this local arbiter.
+- clocks : Must contain an entry for each entry in clock-names.
+- clock-names: must contain 2 entries, as follows:
+ - "apb" : Advanced Peripheral Bus clock, It's the clock for setting
+ the register.
+ - "smi" : It's the clock for transfer data and command.
+
+Example:
+ larb1: larb@16010000 {
+ compatible = "mediatek,mt8173-smi-larb";
+ reg = <0 0x16010000 0 0x1000>;
+ mediatek,smi = <&smi_common>;
+ power-domains = <&scpsys MT8173_POWER_DOMAIN_VDEC>;
+ clocks = <&vdecsys CLK_VDEC_CKEN>,
+ <&vdecsys CLK_VDEC_LARB_CKEN>;
+ clock-names = "apb", "smi";
+ };
--
1.8.1.1.dirty
^ permalink raw reply related [flat|nested] 24+ messages in thread
* [PATCH v6 3/5] memory: mediatek: Add SMI driver
2015-12-08 9:49 [PATCH v6 0/5] MT8173 IOMMU SUPPORT Yong Wu
2015-12-08 9:49 ` [PATCH v6 1/5] dt-bindings: iommu: Add binding for mediatek IOMMU Yong Wu
2015-12-08 9:49 ` [PATCH v6 2/5] dt-bindings: mediatek: Add smi dts binding Yong Wu
@ 2015-12-08 9:49 ` Yong Wu
[not found] ` <1449568153-15643-4-git-send-email-yong.wu-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
2015-12-14 18:18 ` Matthias Brugger
2015-12-08 9:49 ` [PATCH v6 4/5] iommu/mediatek: Add mt8173 IOMMU driver Yong Wu
[not found] ` <1449568153-15643-1-git-send-email-yong.wu-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
4 siblings, 2 replies; 24+ messages in thread
From: Yong Wu @ 2015-12-08 9:49 UTC (permalink / raw)
To: Joerg Roedel, Thierry Reding, Mark Rutland, Matthias Brugger
Cc: Robin Murphy, Will Deacon, Daniel Kurtz, Tomasz Figa, Lucas Stach,
Rob Herring, Catalin Marinas, linux-mediatek, Sasha Hauer,
srv_heupstream, devicetree, linux-kernel, linux-arm-kernel, iommu,
pebolle, arnd, mitchelh, p.zabel, yingjoe.chen, Yong Wu
This patch add SMI(Smart Multimedia Interface) driver. This driver
is responsible to enable/disable iommu and control the power domain
and clocks of each local arbiter.
Signed-off-by: Yong Wu <yong.wu@mediatek.com>
---
Currently SMI offer mtk_smi_larb_get/put to enable the power-domain
,clocks and initialize the iommu configuration register for each a local
arbiter, The reason is:
a) If a device would like to disable iommu, it also need call
mtk_smi_larb_get/put to enable its power and clocks.
b) The iommu core don't support attach/detach a device within a iommu-group.
So we cann't use iommu_attach_device(iommu_detach_device) instead
of mtk_smi_larb_get/put.
drivers/memory/Kconfig | 8 ++
drivers/memory/Makefile | 1 +
drivers/memory/mtk-smi.c | 297 +++++++++++++++++++++++++++++++++++++++++++++
include/soc/mediatek/smi.h | 53 ++++++++
4 files changed, 359 insertions(+)
create mode 100644 drivers/memory/mtk-smi.c
create mode 100644 include/soc/mediatek/smi.h
diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
index 6f31546..51d5cd2 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -114,6 +114,14 @@ config JZ4780_NEMC
the Ingenic JZ4780. This controller is used to handle external
memory devices such as NAND and SRAM.
+config MTK_SMI
+ bool
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ help
+ This driver is for the Memory Controller module in MediaTek SoCs,
+ mainly help enable/disable iommu and control the power domain and
+ clocks for each local arbiter.
+
source "drivers/memory/tegra/Kconfig"
endif
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index 1c46af5..890bdf4 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -15,5 +15,6 @@ obj-$(CONFIG_FSL_IFC) += fsl_ifc.o
obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o
obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o
obj-$(CONFIG_JZ4780_NEMC) += jz4780-nemc.o
+obj-$(CONFIG_MTK_SMI) += mtk-smi.o
obj-$(CONFIG_TEGRA_MC) += tegra/
diff --git a/drivers/memory/mtk-smi.c b/drivers/memory/mtk-smi.c
new file mode 100644
index 0000000..3562081
--- /dev/null
+++ b/drivers/memory/mtk-smi.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2014-2015 MediaTek Inc.
+ * Author: Yong Wu <yong.wu@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <soc/mediatek/smi.h>
+
+#define SMI_LARB_MMU_EN 0xf00
+#define F_SMI_MMU_EN(port) BIT(port)
+
+struct mtk_smi_common {
+ struct device *dev;
+ struct clk *clk_apb, *clk_smi;
+};
+
+struct mtk_smi_larb { /* larb: local arbiter */
+ struct device *dev;
+ void __iomem *base;
+ struct clk *clk_apb, *clk_smi;
+ struct device *smi_common_dev;
+ u32 mmu;
+};
+
+static int
+mtk_smi_enable(struct device *dev, struct clk *apb, struct clk *smi)
+{
+ int ret;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_prepare_enable(apb);
+ if (ret)
+ goto err_put_pm;
+
+ ret = clk_prepare_enable(smi);
+ if (ret)
+ goto err_disable_apb;
+
+ return 0;
+
+err_disable_apb:
+ clk_disable_unprepare(apb);
+err_put_pm:
+ pm_runtime_put_sync(dev);
+ return ret;
+}
+
+static void
+mtk_smi_disable(struct device *dev, struct clk *apb, struct clk *smi)
+{
+ clk_disable_unprepare(smi);
+ clk_disable_unprepare(apb);
+ pm_runtime_put_sync(dev);
+}
+
+static int mtk_smi_common_enable(struct mtk_smi_common *common)
+{
+ return mtk_smi_enable(common->dev, common->clk_apb, common->clk_smi);
+}
+
+static void mtk_smi_common_disable(struct mtk_smi_common *common)
+{
+ mtk_smi_disable(common->dev, common->clk_apb, common->clk_smi);
+}
+
+static int mtk_smi_larb_enable(struct mtk_smi_larb *larb)
+{
+ return mtk_smi_enable(larb->dev, larb->clk_apb, larb->clk_smi);
+}
+
+static void mtk_smi_larb_disable(struct mtk_smi_larb *larb)
+{
+ mtk_smi_disable(larb->dev, larb->clk_apb, larb->clk_smi);
+}
+
+int mtk_smi_larb_get(struct device *larbdev)
+{
+ struct mtk_smi_larb *larb = dev_get_drvdata(larbdev);
+ struct mtk_smi_common *common = dev_get_drvdata(larb->smi_common_dev);
+ int ret;
+
+ ret = mtk_smi_common_enable(common);
+ if (ret)
+ return ret;
+
+ ret = mtk_smi_larb_enable(larb);
+ if (ret)
+ goto err_put_smi;
+
+ /* Configure the iommu info */
+ writel_relaxed(larb->mmu, larb->base + SMI_LARB_MMU_EN);
+
+ return 0;
+
+err_put_smi:
+ mtk_smi_common_disable(common);
+ return ret;
+}
+
+void mtk_smi_larb_put(struct device *larbdev)
+{
+ struct mtk_smi_larb *larb = dev_get_drvdata(larbdev);
+ struct mtk_smi_common *common = dev_get_drvdata(larb->smi_common_dev);
+
+ writel_relaxed(0, larb->base + SMI_LARB_MMU_EN);
+ mtk_smi_larb_disable(larb);
+ mtk_smi_common_disable(common);
+}
+
+void mtk_smi_config_port(struct device *larbdev, unsigned int larbportid,
+ bool enable)
+{
+ struct mtk_smi_larb *larb = dev_get_drvdata(larbdev);
+
+ dev_dbg(larbdev, "%s iommu port: %d\n",
+ enable ? "enable" : "disable", larbportid);
+
+ /*
+ * Only record the iommu info here,
+ * and it will work after its power and clocks is enabled.
+ */
+ if (enable)
+ larb->mmu |= F_SMI_MMU_EN(larbportid);
+ else
+ larb->mmu &= ~F_SMI_MMU_EN(larbportid);
+}
+
+static int
+mtk_smi_larb_bind(struct device *dev, struct device *master, void *data)
+{
+ return 0;
+}
+
+static void
+mtk_smi_larb_unbind(struct device *dev, struct device *master, void *data)
+{
+}
+
+static const struct component_ops mtk_smi_larb_component_ops = {
+ .bind = mtk_smi_larb_bind,
+ .unbind = mtk_smi_larb_unbind,
+};
+
+static int mtk_smi_larb_probe(struct platform_device *pdev)
+{
+ struct mtk_smi_larb *larb;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ struct device_node *smi_node;
+ struct platform_device *smi_pdev;
+
+ if (!dev->pm_domain)
+ return -EPROBE_DEFER;
+
+ larb = devm_kzalloc(dev, sizeof(*larb), GFP_KERNEL);
+ if (!larb)
+ return -ENOMEM;
+ larb->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ larb->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(larb->base))
+ return PTR_ERR(larb->base);
+
+ larb->clk_apb = devm_clk_get(dev, "apb");
+ if (IS_ERR(larb->clk_apb))
+ return PTR_ERR(larb->clk_apb);
+
+ larb->clk_smi = devm_clk_get(dev, "smi");
+ if (IS_ERR(larb->clk_smi))
+ return PTR_ERR(larb->clk_smi);
+
+ smi_node = of_parse_phandle(dev->of_node, "mediatek,smi", 0);
+ if (!smi_node)
+ return -EINVAL;
+
+ smi_pdev = of_find_device_by_node(smi_node);
+ of_node_put(smi_node);
+ if (smi_pdev) {
+ larb->smi_common_dev = &smi_pdev->dev;
+ } else {
+ dev_err(dev, "Failed to get the smi_common device\n");
+ return -EINVAL;
+ }
+
+ pm_runtime_enable(dev);
+ dev_set_drvdata(dev, larb);
+ return component_add(dev, &mtk_smi_larb_component_ops);
+}
+
+static int mtk_smi_larb_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+ component_del(&pdev->dev, &mtk_smi_larb_component_ops);
+ return 0;
+}
+
+static const struct of_device_id mtk_smi_larb_of_ids[] = {
+ { .compatible = "mediatek,mt8173-smi-larb",},
+ {}
+};
+
+static struct platform_driver mtk_smi_larb_driver = {
+ .probe = mtk_smi_larb_probe,
+ .remove = mtk_smi_larb_remove,
+ .driver = {
+ .name = "mtk-smi-larb",
+ .of_match_table = mtk_smi_larb_of_ids,
+ }
+};
+
+static int mtk_smi_common_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mtk_smi_common *common;
+
+ if (!dev->pm_domain)
+ return -EPROBE_DEFER;
+
+ common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL);
+ if (!common)
+ return -ENOMEM;
+ common->dev = dev;
+
+ common->clk_apb = devm_clk_get(dev, "apb");
+ if (IS_ERR(common->clk_apb))
+ return PTR_ERR(common->clk_apb);
+
+ common->clk_smi = devm_clk_get(dev, "smi");
+ if (IS_ERR(common->clk_smi))
+ return PTR_ERR(common->clk_smi);
+
+ pm_runtime_enable(dev);
+ dev_set_drvdata(dev, common);
+ return 0;
+}
+
+static int mtk_smi_common_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id mtk_smi_common_of_ids[] = {
+ { .compatible = "mediatek,mt8173-smi-common", },
+ {}
+};
+
+static struct platform_driver mtk_smi_common_driver = {
+ .probe = mtk_smi_common_probe,
+ .remove = mtk_smi_common_remove,
+ .driver = {
+ .name = "mtk-smi-common",
+ .of_match_table = mtk_smi_common_of_ids,
+ }
+};
+
+static int __init mtk_smi_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&mtk_smi_common_driver);
+ if (ret != 0) {
+ pr_err("Failed to register SMI driver\n");
+ return ret;
+ }
+
+ ret = platform_driver_register(&mtk_smi_larb_driver);
+ if (ret != 0) {
+ pr_err("Failed to register SMI-LARB driver\n");
+ goto err_unreg_smi;
+ }
+ return ret;
+
+err_unreg_smi:
+ platform_driver_unregister(&mtk_smi_common_driver);
+ return ret;
+}
+subsys_initcall(mtk_smi_init);
diff --git a/include/soc/mediatek/smi.h b/include/soc/mediatek/smi.h
new file mode 100644
index 0000000..6f41b2e
--- /dev/null
+++ b/include/soc/mediatek/smi.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2014-2015 MediaTek Inc.
+ * Author: Yong Wu <yong.wu@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#ifndef MTK_IOMMU_SMI_H
+#define MTK_IOMMU_SMI_H
+
+#include <linux/device.h>
+
+#ifdef CONFIG_MTK_SMI
+
+/*
+ * Record the iommu info for each port in the local arbiter.
+ * It's only for iommu.
+ */
+void mtk_smi_config_port(struct device *larbdev, unsigned int larbportid,
+ bool enable);
+/*
+ * mtk_smi_larb_get: Enable the power domain and clocks for this local arbiter.
+ * It also initialize some basic setting(like iommu).
+ * mtk_smi_larb_put: Disable the power domain and clocks for this local arbiter.
+ * Both should be called in non-atomic context.
+ *
+ * Returns 0 if successful, negative on failure.
+ */
+int mtk_smi_larb_get(struct device *larbdev);
+void mtk_smi_larb_put(struct device *larbdev);
+
+#else
+
+static inline void
+mtk_smi_config_port(struct device *larbdev, unsigned int larbportid,
+ bool enable) { }
+
+static inline int mtk_smi_larb_get(struct device *larbdev)
+{
+ return 0;
+}
+
+static inline void mtk_smi_larb_put(struct device *larbdev) { }
+
+#endif
+
+#endif
--
1.8.1.1.dirty
^ permalink raw reply related [flat|nested] 24+ messages in thread
[parent not found: <1449568153-15643-4-git-send-email-yong.wu-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v6 3/5] memory: mediatek: Add SMI driver
[not found] ` <1449568153-15643-4-git-send-email-yong.wu-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
@ 2015-12-11 7:22 ` Yong Wu
0 siblings, 0 replies; 24+ messages in thread
From: Yong Wu @ 2015-12-11 7:22 UTC (permalink / raw)
To: Matthias Brugger
Cc: Joerg Roedel, Thierry Reding, Mark Rutland, Robin Murphy,
Will Deacon, Daniel Kurtz, Tomasz Figa, Lucas Stach, Rob Herring,
Catalin Marinas, linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
Sasha Hauer, srv_heupstream-NuS5LvNUpcJWk0Htik3J/w,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
pebolle-IWqWACnzNjzz+pZb47iToQ, arnd-r2nGTMty4D4,
mitchelh-sgV2jX0FEOL9JmXXK+q4OQ, p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ,
yingjoe.chen-NuS5LvNUpcJWk0Htik3J/w
On Tue, 2015-12-08 at 17:49 +0800, Yong Wu wrote:
> This patch add SMI(Smart Multimedia Interface) driver. This driver
> is responsible to enable/disable iommu and control the power domain
> and clocks of each local arbiter.
>
> Signed-off-by: Yong Wu <yong.wu-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> ---
Hi Matthias,
Because drivers/memory/ don't have the maintainer, and our IOMMU,
V4L2 and DRM rely on the SMI. From Joerg and Thierry[1], we need your
help. Could you have a loot at our SMI while free?
Look forward to any comment from you.
Thanks.
[1]http://lists.linuxfoundation.org/pipermail/iommu/2015-November/014981.html
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v6 3/5] memory: mediatek: Add SMI driver
2015-12-08 9:49 ` [PATCH v6 3/5] memory: mediatek: Add SMI driver Yong Wu
[not found] ` <1449568153-15643-4-git-send-email-yong.wu-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
@ 2015-12-14 18:18 ` Matthias Brugger
[not found] ` <24171857.0SPpBlzoZl-FL1YVkE3Js+HtQ08XqlRiA@public.gmane.org>
1 sibling, 1 reply; 24+ messages in thread
From: Matthias Brugger @ 2015-12-14 18:18 UTC (permalink / raw)
To: Yong Wu
Cc: Joerg Roedel, Thierry Reding, Mark Rutland, Robin Murphy,
Will Deacon, Daniel Kurtz, Tomasz Figa, Lucas Stach, Rob Herring,
Catalin Marinas, linux-mediatek, Sasha Hauer, srv_heupstream,
devicetree, linux-kernel, linux-arm-kernel, iommu, pebolle, arnd,
mitchelh, p.zabel, yingjoe.chen
On Tuesday 08 Dec 2015 17:49:11 Yong Wu wrote:
> This patch add SMI(Smart Multimedia Interface) driver. This driver
> is responsible to enable/disable iommu and control the power domain
> and clocks of each local arbiter.
>
> Signed-off-by: Yong Wu <yong.wu@mediatek.com>
> ---
> Currently SMI offer mtk_smi_larb_get/put to enable the power-domain
> ,clocks and initialize the iommu configuration register for each a local
> arbiter, The reason is:
> a) If a device would like to disable iommu, it also need call
> mtk_smi_larb_get/put to enable its power and clocks.
> b) The iommu core don't support attach/detach a device within a
> iommu-group. So we cann't use iommu_attach_device(iommu_detach_device)
> instead
> of mtk_smi_larb_get/put.
>
> drivers/memory/Kconfig | 8 ++
> drivers/memory/Makefile | 1 +
> drivers/memory/mtk-smi.c | 297
> +++++++++++++++++++++++++++++++++++++++++++++ include/soc/mediatek/smi.h |
> 53 ++++++++
> 4 files changed, 359 insertions(+)
> create mode 100644 drivers/memory/mtk-smi.c
> create mode 100644 include/soc/mediatek/smi.h
>
> diff --git a/drivers/memory/Kconfig b/drivers/memory/Kconfig
> index 6f31546..51d5cd2 100644
> --- a/drivers/memory/Kconfig
> +++ b/drivers/memory/Kconfig
> @@ -114,6 +114,14 @@ config JZ4780_NEMC
> the Ingenic JZ4780. This controller is used to handle external
> memory devices such as NAND and SRAM.
>
> +config MTK_SMI
> + bool
> + depends on ARCH_MEDIATEK || COMPILE_TEST
> + help
> + This driver is for the Memory Controller module in MediaTek SoCs,
> + mainly help enable/disable iommu and control the power domain and
> + clocks for each local arbiter.
> +
> source "drivers/memory/tegra/Kconfig"
>
> endif
> diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
> index 1c46af5..890bdf4 100644
> --- a/drivers/memory/Makefile
> +++ b/drivers/memory/Makefile
> @@ -15,5 +15,6 @@ obj-$(CONFIG_FSL_IFC) += fsl_ifc.o
> obj-$(CONFIG_MVEBU_DEVBUS) += mvebu-devbus.o
> obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o
> obj-$(CONFIG_JZ4780_NEMC) += jz4780-nemc.o
> +obj-$(CONFIG_MTK_SMI) += mtk-smi.o
>
> obj-$(CONFIG_TEGRA_MC) += tegra/
> diff --git a/drivers/memory/mtk-smi.c b/drivers/memory/mtk-smi.c
> new file mode 100644
> index 0000000..3562081
> --- /dev/null
> +++ b/drivers/memory/mtk-smi.c
> @@ -0,0 +1,297 @@
> +/*
> + * Copyright (c) 2014-2015 MediaTek Inc.
> + * Author: Yong Wu <yong.wu@mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/of_address.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <soc/mediatek/smi.h>
> +
> +#define SMI_LARB_MMU_EN 0xf00
> +#define F_SMI_MMU_EN(port) BIT(port)
> +
> +struct mtk_smi_common {
> + struct device *dev;
> + struct clk *clk_apb, *clk_smi;
> +};
> +
> +struct mtk_smi_larb { /* larb: local arbiter */
> + struct device *dev;
> + void __iomem *base;
> + struct clk *clk_apb, *clk_smi;
> + struct device *smi_common_dev;
> + u32 mmu;
> +};
> +
> +static int
> +mtk_smi_enable(struct device *dev, struct clk *apb, struct clk *smi)
> +{
> + int ret;
> +
> + ret = pm_runtime_get_sync(dev);
> + if (ret < 0)
> + return ret;
> +
> + ret = clk_prepare_enable(apb);
> + if (ret)
> + goto err_put_pm;
> +
> + ret = clk_prepare_enable(smi);
> + if (ret)
> + goto err_disable_apb;
> +
> + return 0;
> +
> +err_disable_apb:
> + clk_disable_unprepare(apb);
> +err_put_pm:
> + pm_runtime_put_sync(dev);
> + return ret;
> +}
> +
> +static void
> +mtk_smi_disable(struct device *dev, struct clk *apb, struct clk *smi)
> +{
> + clk_disable_unprepare(smi);
> + clk_disable_unprepare(apb);
> + pm_runtime_put_sync(dev);
> +}
> +
> +static int mtk_smi_common_enable(struct mtk_smi_common *common)
> +{
> + return mtk_smi_enable(common->dev, common->clk_apb, common->clk_smi);
> +}
> +
> +static void mtk_smi_common_disable(struct mtk_smi_common *common)
> +{
> + mtk_smi_disable(common->dev, common->clk_apb, common->clk_smi);
> +}
> +
> +static int mtk_smi_larb_enable(struct mtk_smi_larb *larb)
> +{
> + return mtk_smi_enable(larb->dev, larb->clk_apb, larb->clk_smi);
> +}
> +
> +static void mtk_smi_larb_disable(struct mtk_smi_larb *larb)
> +{
> + mtk_smi_disable(larb->dev, larb->clk_apb, larb->clk_smi);
> +}
> +
This is somehow over-engineered. Just use mtk_smi_enable and mtk_smi_disable
instead of adding an extra indirection.
> +int mtk_smi_larb_get(struct device *larbdev)
> +{
> + struct mtk_smi_larb *larb = dev_get_drvdata(larbdev);
> + struct mtk_smi_common *common = dev_get_drvdata(larb->smi_common_dev);
> + int ret;
> +
> + ret = mtk_smi_common_enable(common);
> + if (ret)
> + return ret;
> +
> + ret = mtk_smi_larb_enable(larb);
> + if (ret)
> + goto err_put_smi;
> +
> + /* Configure the iommu info */
> + writel_relaxed(larb->mmu, larb->base + SMI_LARB_MMU_EN);
> +
> + return 0;
> +
> +err_put_smi:
> + mtk_smi_common_disable(common);
> + return ret;
> +}
> +
> +void mtk_smi_larb_put(struct device *larbdev)
> +{
> + struct mtk_smi_larb *larb = dev_get_drvdata(larbdev);
> + struct mtk_smi_common *common = dev_get_drvdata(larb->smi_common_dev);
> +
> + writel_relaxed(0, larb->base + SMI_LARB_MMU_EN);
> + mtk_smi_larb_disable(larb);
> + mtk_smi_common_disable(common);
> +}
> +
Looks strange that you just disable all MMUs while you only enable some of
them at runtime. Unfortunately the datasheet I have lacks the SMI part, so I
can just guess how the HW is working.
>From the DTS it looks like as if a larb can be used by two different
components (e.g. larb0 from ovl0 and rdma0). Wouldn't that produce a conflict?
Regards,
Matthias
> +void mtk_smi_config_port(struct device *larbdev, unsigned int larbportid,
> + bool enable)
> +{
> + struct mtk_smi_larb *larb = dev_get_drvdata(larbdev);
> +
> + dev_dbg(larbdev, "%s iommu port: %d\n",
> + enable ? "enable" : "disable", larbportid);
> +
> + /*
> + * Only record the iommu info here,
> + * and it will work after its power and clocks is enabled.
> + */
> + if (enable)
> + larb->mmu |= F_SMI_MMU_EN(larbportid);
> + else
> + larb->mmu &= ~F_SMI_MMU_EN(larbportid);
> +}
> +
> +static int
> +mtk_smi_larb_bind(struct device *dev, struct device *master, void *data)
> +{
> + return 0;
> +}
> +
> +static void
> +mtk_smi_larb_unbind(struct device *dev, struct device *master, void *data)
> +{
> +}
> +
> +static const struct component_ops mtk_smi_larb_component_ops = {
> + .bind = mtk_smi_larb_bind,
> + .unbind = mtk_smi_larb_unbind,
> +};
> +
> +static int mtk_smi_larb_probe(struct platform_device *pdev)
> +{
> + struct mtk_smi_larb *larb;
> + struct resource *res;
> + struct device *dev = &pdev->dev;
> + struct device_node *smi_node;
> + struct platform_device *smi_pdev;
> +
> + if (!dev->pm_domain)
> + return -EPROBE_DEFER;
> +
> + larb = devm_kzalloc(dev, sizeof(*larb), GFP_KERNEL);
> + if (!larb)
> + return -ENOMEM;
> + larb->dev = dev;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + larb->base = devm_ioremap_resource(dev, res);
> + if (IS_ERR(larb->base))
> + return PTR_ERR(larb->base);
> +
> + larb->clk_apb = devm_clk_get(dev, "apb");
> + if (IS_ERR(larb->clk_apb))
> + return PTR_ERR(larb->clk_apb);
> +
> + larb->clk_smi = devm_clk_get(dev, "smi");
> + if (IS_ERR(larb->clk_smi))
> + return PTR_ERR(larb->clk_smi);
> +
> + smi_node = of_parse_phandle(dev->of_node, "mediatek,smi", 0);
> + if (!smi_node)
> + return -EINVAL;
> +
> + smi_pdev = of_find_device_by_node(smi_node);
> + of_node_put(smi_node);
> + if (smi_pdev) {
> + larb->smi_common_dev = &smi_pdev->dev;
> + } else {
> + dev_err(dev, "Failed to get the smi_common device\n");
> + return -EINVAL;
> + }
> +
> + pm_runtime_enable(dev);
> + dev_set_drvdata(dev, larb);
> + return component_add(dev, &mtk_smi_larb_component_ops);
> +}
> +
> +static int mtk_smi_larb_remove(struct platform_device *pdev)
> +{
> + pm_runtime_disable(&pdev->dev);
> + component_del(&pdev->dev, &mtk_smi_larb_component_ops);
> + return 0;
> +}
> +
> +static const struct of_device_id mtk_smi_larb_of_ids[] = {
> + { .compatible = "mediatek,mt8173-smi-larb",},
> + {}
> +};
> +
> +static struct platform_driver mtk_smi_larb_driver = {
> + .probe = mtk_smi_larb_probe,
> + .remove = mtk_smi_larb_remove,
> + .driver = {
> + .name = "mtk-smi-larb",
> + .of_match_table = mtk_smi_larb_of_ids,
> + }
> +};
> +
> +static int mtk_smi_common_probe(struct platform_device *pdev)
> +{
> + struct device *dev = &pdev->dev;
> + struct mtk_smi_common *common;
> +
> + if (!dev->pm_domain)
> + return -EPROBE_DEFER;
> +
> + common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL);
> + if (!common)
> + return -ENOMEM;
> + common->dev = dev;
> +
> + common->clk_apb = devm_clk_get(dev, "apb");
> + if (IS_ERR(common->clk_apb))
> + return PTR_ERR(common->clk_apb);
> +
> + common->clk_smi = devm_clk_get(dev, "smi");
> + if (IS_ERR(common->clk_smi))
> + return PTR_ERR(common->clk_smi);
> +
> + pm_runtime_enable(dev);
> + dev_set_drvdata(dev, common);
> + return 0;
> +}
> +
> +static int mtk_smi_common_remove(struct platform_device *pdev)
> +{
> + pm_runtime_disable(&pdev->dev);
> + return 0;
> +}
> +
> +static const struct of_device_id mtk_smi_common_of_ids[] = {
> + { .compatible = "mediatek,mt8173-smi-common", },
> + {}
> +};
> +
> +static struct platform_driver mtk_smi_common_driver = {
> + .probe = mtk_smi_common_probe,
> + .remove = mtk_smi_common_remove,
> + .driver = {
> + .name = "mtk-smi-common",
> + .of_match_table = mtk_smi_common_of_ids,
> + }
> +};
> +
> +static int __init mtk_smi_init(void)
> +{
> + int ret;
> +
> + ret = platform_driver_register(&mtk_smi_common_driver);
> + if (ret != 0) {
> + pr_err("Failed to register SMI driver\n");
> + return ret;
> + }
> +
> + ret = platform_driver_register(&mtk_smi_larb_driver);
> + if (ret != 0) {
> + pr_err("Failed to register SMI-LARB driver\n");
> + goto err_unreg_smi;
> + }
> + return ret;
> +
> +err_unreg_smi:
> + platform_driver_unregister(&mtk_smi_common_driver);
> + return ret;
> +}
> +subsys_initcall(mtk_smi_init);
> diff --git a/include/soc/mediatek/smi.h b/include/soc/mediatek/smi.h
> new file mode 100644
> index 0000000..6f41b2e
> --- /dev/null
> +++ b/include/soc/mediatek/smi.h
> @@ -0,0 +1,53 @@
> +/*
> + * Copyright (c) 2014-2015 MediaTek Inc.
> + * Author: Yong Wu <yong.wu@mediatek.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +#ifndef MTK_IOMMU_SMI_H
> +#define MTK_IOMMU_SMI_H
> +
> +#include <linux/device.h>
> +
> +#ifdef CONFIG_MTK_SMI
> +
> +/*
> + * Record the iommu info for each port in the local arbiter.
> + * It's only for iommu.
> + */
> +void mtk_smi_config_port(struct device *larbdev, unsigned int larbportid,
> + bool enable);
> +/*
> + * mtk_smi_larb_get: Enable the power domain and clocks for this local
> arbiter. + * It also initialize some basic setting(like
> iommu). + * mtk_smi_larb_put: Disable the power domain and clocks for this
> local arbiter. + * Both should be called in non-atomic context.
> + *
> + * Returns 0 if successful, negative on failure.
> + */
> +int mtk_smi_larb_get(struct device *larbdev);
> +void mtk_smi_larb_put(struct device *larbdev);
> +
> +#else
> +
> +static inline void
> +mtk_smi_config_port(struct device *larbdev, unsigned int larbportid,
> + bool enable) { }
> +
> +static inline int mtk_smi_larb_get(struct device *larbdev)
> +{
> + return 0;
> +}
> +
> +static inline void mtk_smi_larb_put(struct device *larbdev) { }
> +
> +#endif
> +
> +#endif
^ permalink raw reply [flat|nested] 24+ messages in thread
* [PATCH v6 4/5] iommu/mediatek: Add mt8173 IOMMU driver
2015-12-08 9:49 [PATCH v6 0/5] MT8173 IOMMU SUPPORT Yong Wu
` (2 preceding siblings ...)
2015-12-08 9:49 ` [PATCH v6 3/5] memory: mediatek: Add SMI driver Yong Wu
@ 2015-12-08 9:49 ` Yong Wu
2015-12-08 10:32 ` kbuild test robot
[not found] ` <1449568153-15643-5-git-send-email-yong.wu-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
[not found] ` <1449568153-15643-1-git-send-email-yong.wu-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
4 siblings, 2 replies; 24+ messages in thread
From: Yong Wu @ 2015-12-08 9:49 UTC (permalink / raw)
To: Joerg Roedel, Thierry Reding, Mark Rutland, Matthias Brugger
Cc: Robin Murphy, Will Deacon, Daniel Kurtz, Tomasz Figa, Lucas Stach,
Rob Herring, Catalin Marinas, linux-mediatek, Sasha Hauer,
srv_heupstream, devicetree, linux-kernel, linux-arm-kernel, iommu,
pebolle, arnd, mitchelh, p.zabel, yingjoe.chen, Yong Wu
This patch adds support for mediatek m4u (MultiMedia Memory Management
Unit).
Signed-off-by: Yong Wu <yong.wu@mediatek.com>
---
drivers/iommu/Kconfig | 15 +
drivers/iommu/Makefile | 1 +
drivers/iommu/mtk_iommu.c | 752 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 768 insertions(+)
create mode 100644 drivers/iommu/mtk_iommu.c
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index b9094e9..aab942f 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -393,4 +393,19 @@ config S390_IOMMU
help
Support for the IOMMU API for s390 PCI devices.
+config MTK_IOMMU
+ bool "MTK IOMMU Support"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ select IOMMU_API
+ select IOMMU_DMA
+ select IOMMU_IO_PGTABLE_ARMV7S
+ select MEMORY
+ select MTK_SMI
+ help
+ Support for the M4U on certain Mediatek SOCs. M4U is MultiMedia
+ Memory Management Unit. This option enables remapping of DMA memory
+ accesses for the multimedia subsystem.
+
+ If unsure, say N here.
+
endif # IOMMU_SUPPORT
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index 68faca02..02887bc 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_ROCKCHIP_IOMMU) += rockchip-iommu.o
obj-$(CONFIG_TEGRA_IOMMU_GART) += tegra-gart.o
obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o
obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o
+obj-$(CONFIG_MTK_IOMMU) += mtk_iommu.o
obj-$(CONFIG_SHMOBILE_IOMMU) += shmobile-iommu.o
obj-$(CONFIG_SHMOBILE_IPMMU) += shmobile-ipmmu.o
obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o
diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c
new file mode 100644
index 0000000..6a21e70
--- /dev/null
+++ b/drivers/iommu/mtk_iommu.c
@@ -0,0 +1,752 @@
+/*
+ * Copyright (c) 2014-2015 MediaTek Inc.
+ * Author: Yong Wu <yong.wu@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+#include <linux/clk.h>
+#include <linux/component.h>
+#include <linux/dma-iommu.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/iommu.h>
+#include <linux/iopoll.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of_address.h>
+#include <linux/of_iommu.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <soc/mediatek/smi.h>
+#include <dt-bindings/memory/mt8173-larb-port.h>
+
+#include "io-pgtable.h"
+
+#define REG_MMU_PT_BASE_ADDR 0x000
+
+#define REG_MMU_INVALIDATE 0x020
+#define F_ALL_INVLD 0x2
+#define F_MMU_INV_RANGE 0x1
+
+#define REG_MMU_INVLD_START_A 0x024
+#define REG_MMU_INVLD_END_A 0x028
+
+#define REG_MMU_INV_SEL 0x038
+#define F_INVLD_EN0 BIT(0)
+#define F_INVLD_EN1 BIT(1)
+
+#define REG_MMU_STANDARD_AXI_MODE 0x048
+#define REG_MMU_DCM_DIS 0x050
+
+#define REG_MMU_CTRL_REG 0x110
+#define F_MMU_PREFETCH_RT_REPLACE_MOD BIT(4)
+#define F_MMU_TF_PROTECT_SEL(prot) (((prot) & 0x3) << 5)
+#define F_COHERENCE_EN BIT(8)
+
+#define REG_MMU_IVRP_PADDR 0x114
+#define F_MMU_IVRP_PA_SET(pa) ((pa) >> 1)
+
+#define REG_MMU_INT_CONTROL0 0x120
+#define F_L2_MULIT_HIT_EN BIT(0)
+#define F_TABLE_WALK_FAULT_INT_EN BIT(1)
+#define F_PREETCH_FIFO_OVERFLOW_INT_EN BIT(2)
+#define F_MISS_FIFO_OVERFLOW_INT_EN BIT(3)
+#define F_PREFETCH_FIFO_ERR_INT_EN BIT(5)
+#define F_MISS_FIFO_ERR_INT_EN BIT(6)
+#define F_INT_CLR_BIT BIT(12)
+
+#define REG_MMU_INT_MAIN_CONTROL 0x124
+#define F_INT_TRANSLATION_FAULT BIT(0)
+#define F_INT_MAIN_MULTI_HIT_FAULT BIT(1)
+#define F_INT_INVALID_PA_FAULT BIT(2)
+#define F_INT_ENTRY_REPLACEMENT_FAULT BIT(3)
+#define F_INT_TLB_MISS_FAULT BIT(4)
+#define F_INT_MISS_TRANSATION_FIFO_FAULT BIT(5)
+#define F_INT_PRETETCH_TRANSATION_FIFO_FAULT BIT(6)
+
+#define REG_MMU_CPE_DONE 0x12C
+
+#define REG_MMU_FAULT_ST1 0x134
+
+#define REG_MMU_FAULT_VA 0x13c
+#define F_MMU_FAULT_VA_MSK 0xfffff000
+#define F_MMU_FAULT_VA_WRITE_BIT BIT(1)
+#define F_MMU_FAULT_VA_LAYER_BIT BIT(0)
+
+#define REG_MMU_INVLD_PA 0x140
+#define REG_MMU_INT_ID 0x150
+#define F_MMU0_INT_ID_LARB_ID(a) (((a) >> 7) & 0x7)
+#define F_MMU0_INT_ID_PORT_ID(a) (((a) >> 2) & 0x1f)
+
+#define MTK_PROTECT_PA_ALIGN 128
+#define MTK_IOMMU_LARB_MAX_NR 8
+
+struct mtk_iommu_suspend_reg {
+ u32 standard_axi_mode;
+ u32 dcm_dis;
+ u32 ctrl_reg;
+ u32 int_control0;
+ u32 int_main_control;
+};
+
+struct mtk_iommu_client_priv {
+ struct list_head client;
+ unsigned int mtk_m4u_id;
+ struct device *m4udev;
+};
+
+struct mtk_iommu_domain {
+ spinlock_t pgtlock; /* lock for page table */
+
+ struct io_pgtable_cfg cfg;
+ struct io_pgtable_ops *iop;
+
+ struct iommu_domain domain;
+};
+
+struct mtk_iommu_data {
+ void __iomem *base;
+ int irq;
+ struct device *dev;
+ struct device *larbdev[MTK_IOMMU_LARB_MAX_NR];
+ struct clk *bclk;
+ phys_addr_t protect_base; /* protect memory base */
+ int larb_nr; /* local arbiter number */
+ struct mtk_iommu_suspend_reg reg;
+ struct mtk_iommu_domain *m4u_dom;
+ struct iommu_group *m4u_group;
+};
+
+static struct iommu_ops mtk_iommu_ops;
+
+static struct mtk_iommu_domain *to_mtk_domain(struct iommu_domain *dom)
+{
+ return container_of(dom, struct mtk_iommu_domain, domain);
+}
+
+static void mtk_iommu_tlb_flush_all(void *cookie)
+{
+ struct mtk_iommu_data *data = cookie;
+
+ writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, data->base + REG_MMU_INV_SEL);
+ writel_relaxed(F_ALL_INVLD, data->base + REG_MMU_INVALIDATE);
+ wmb(); /* Make sure the tlb flush all done */
+}
+
+static void mtk_iommu_tlb_add_flush_nosync(unsigned long iova, size_t size,
+ size_t granule, bool leaf,
+ void *cookie)
+{
+ struct mtk_iommu_data *data = cookie;
+
+ writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, data->base + REG_MMU_INV_SEL);
+
+ writel_relaxed(iova, data->base + REG_MMU_INVLD_START_A);
+ writel_relaxed(iova + size - 1, data->base + REG_MMU_INVLD_END_A);
+ writel_relaxed(F_MMU_INV_RANGE, data->base + REG_MMU_INVALIDATE);
+}
+
+static void mtk_iommu_tlb_sync(void *cookie)
+{
+ struct mtk_iommu_data *data = cookie;
+ int ret;
+ u32 tmp;
+
+ ret = readl_poll_timeout_atomic(data->base + REG_MMU_CPE_DONE, tmp,
+ tmp != 0, 10, 100000);
+ if (ret) {
+ dev_warn(data->dev,
+ "Partial TLB flush timed out, falling back to full flush\n");
+ mtk_iommu_tlb_flush_all(cookie);
+ }
+ /* Clear the CPE status */
+ writel_relaxed(0, data->base + REG_MMU_CPE_DONE);
+}
+
+static const struct iommu_gather_ops mtk_iommu_gather_ops = {
+ .tlb_flush_all = mtk_iommu_tlb_flush_all,
+ .tlb_add_flush = mtk_iommu_tlb_add_flush_nosync,
+ .tlb_sync = mtk_iommu_tlb_sync,
+};
+
+static irqreturn_t mtk_iommu_isr(int irq, void *dev_id)
+{
+ struct mtk_iommu_data *data = dev_id;
+ struct mtk_iommu_domain *dom = data->m4u_dom;
+ u32 int_state, regval, fault_iova, fault_pa;
+ unsigned int fault_larb, fault_port;
+ bool layer, write;
+
+ /* Read error info from registers */
+ int_state = readl_relaxed(data->base + REG_MMU_FAULT_ST1);
+ fault_iova = readl_relaxed(data->base + REG_MMU_FAULT_VA);
+ layer = fault_iova & F_MMU_FAULT_VA_LAYER_BIT;
+ write = fault_iova & F_MMU_FAULT_VA_WRITE_BIT;
+ fault_iova &= F_MMU_FAULT_VA_MSK;
+ fault_pa = readl_relaxed(data->base + REG_MMU_INVLD_PA);
+ regval = readl_relaxed(data->base + REG_MMU_INT_ID);
+ fault_larb = F_MMU0_INT_ID_LARB_ID(regval);
+ fault_port = F_MMU0_INT_ID_PORT_ID(regval);
+
+ if (report_iommu_fault(&dom->domain, data->dev, fault_iova,
+ write ? IOMMU_FAULT_WRITE : IOMMU_FAULT_READ)) {
+ dev_err_ratelimited(
+ data->dev,
+ "fault type=0x%x iova=0x%x pa=0x%x larb=%d port=%d layer=%d %s\n",
+ int_state, fault_iova, fault_pa, fault_larb, fault_port,
+ layer, write ? "write" : "read");
+ }
+
+ /* Interrupt clear */
+ regval = readl_relaxed(data->base + REG_MMU_INT_CONTROL0);
+ regval |= F_INT_CLR_BIT;
+ writel_relaxed(regval, data->base + REG_MMU_INT_CONTROL0);
+
+ mtk_iommu_tlb_flush_all(data);
+
+ return IRQ_HANDLED;
+}
+
+static void mtk_iommu_config(struct mtk_iommu_data *data,
+ struct device *dev, bool enable)
+{
+ struct mtk_iommu_client_priv *head, *cur, *next;
+
+ head = dev->archdata.iommu;
+ list_for_each_entry_safe(cur, next, &head->client, client) {
+ mtk_smi_config_port(
+ data->larbdev[MTK_M4U_TO_LARB(cur->mtk_m4u_id)],
+ MTK_M4U_TO_PORT(cur->mtk_m4u_id), enable);
+ }
+}
+
+static int mtk_iommu_domain_finalise(struct mtk_iommu_data *data)
+{
+ struct mtk_iommu_domain *dom = data->m4u_dom;
+
+ spin_lock_init(&dom->pgtlock);
+
+ dom->cfg = (struct io_pgtable_cfg) {
+ .quirks = IO_PGTABLE_QUIRK_ARM_NS |
+ IO_PGTABLE_QUIRK_NO_PERMS |
+ IO_PGTABLE_QUIRK_TLBI_ON_MAP,
+ .pgsize_bitmap = mtk_iommu_ops.pgsize_bitmap,
+ .ias = 32,
+ .oas = 32,
+ .tlb = &mtk_iommu_gather_ops,
+ .iommu_dev = data->dev,
+ };
+
+ dom->iop = alloc_io_pgtable_ops(ARM_V7S, &dom->cfg, data);
+ if (!dom->iop) {
+ dev_err(data->dev, "Failed to alloc io pgtable\n");
+ return -EINVAL;
+ }
+
+ /* Update our support page sizes bitmap */
+ mtk_iommu_ops.pgsize_bitmap = dom->cfg.pgsize_bitmap;
+
+ writel_relaxed(data->m4u_dom->cfg.arm_v7s_cfg.ttbr[0],
+ data->base + REG_MMU_PT_BASE_ADDR);
+ return 0;
+}
+
+static struct iommu_domain *mtk_iommu_domain_alloc(unsigned type)
+{
+ struct mtk_iommu_domain *dom;
+
+ if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_DMA)
+ return NULL;
+
+ dom = kzalloc(sizeof(*dom), GFP_KERNEL);
+ if (!dom)
+ return NULL;
+
+ if (type == IOMMU_DOMAIN_DMA && iommu_get_dma_cookie(&dom->domain)) {
+ kfree(dom);
+ return NULL;
+ }
+
+ dom->domain.geometry.aperture_start = 0;
+ dom->domain.geometry.aperture_end = DMA_BIT_MASK(32);
+ dom->domain.geometry.force_aperture = true;
+
+ return &dom->domain;
+}
+
+static void mtk_iommu_domain_free(struct iommu_domain *domain)
+{
+ if (domain->type == IOMMU_DOMAIN_DMA)
+ iommu_put_dma_cookie(domain);
+ kfree(to_mtk_domain(domain));
+}
+
+static int mtk_iommu_attach_device(struct iommu_domain *domain,
+ struct device *dev)
+{
+ struct mtk_iommu_domain *dom = to_mtk_domain(domain);
+ struct mtk_iommu_client_priv *priv = dev->archdata.iommu;
+ struct mtk_iommu_data *data;
+ int ret;
+
+ if (!priv)
+ return -ENODEV;
+
+ data = dev_get_drvdata(priv->m4udev);
+ if (!data) {
+ /*
+ * The DMA core will run earlier than this probe, and it will
+ * create a default iommu domain for each a iommu device.
+ * But here there is only one domain called the m4u domain
+ * which all the multimedia HW share.
+ * The default domain isn't needed here.
+ */
+ iommu_domain_free(domain);
+
+ /* Return 0 for DMA to update dev->archdata.dma_ops */
+ return 0;
+ } else if (!data->m4u_dom) {
+ /*
+ * While a device is added into a iommu group, the iommu core
+ * will create a default domain for each a iommu group.
+ * This default domain is reserved as the m4u domain and is
+ * initiated here.
+ */
+ data->m4u_dom = dom;
+
+ if (domain->type == IOMMU_DOMAIN_DMA) {
+ ret = iommu_dma_init_domain(domain, 0,
+ DMA_BIT_MASK(32));
+ if (ret)
+ goto err_uninit_dom;
+ }
+
+ ret = mtk_iommu_domain_finalise(data);
+ if (ret)
+ goto err_uninit_dom;
+ } else if (data->m4u_dom != dom) {
+ /* All the client devices should be in the same m4u domain */
+ dev_err(dev, "try to attach into the error iommu domain\n");
+ return -EPERM;
+ }
+
+ mtk_iommu_config(data, dev, true);
+ return 0;
+
+err_uninit_dom:
+ data->m4u_dom = NULL;
+ return ret;
+}
+
+static void mtk_iommu_detach_device(struct iommu_domain *domain,
+ struct device *dev)
+{
+ struct mtk_iommu_client_priv *priv = dev->archdata.iommu;
+ struct mtk_iommu_data *data;
+
+ if (!priv)
+ return;
+
+ data = dev_get_drvdata(priv->m4udev);
+ if (data)
+ mtk_iommu_config(data, dev, false);
+}
+
+static int mtk_iommu_map(struct iommu_domain *domain, unsigned long iova,
+ phys_addr_t paddr, size_t size, int prot)
+{
+ struct mtk_iommu_domain *dom = to_mtk_domain(domain);
+ unsigned long flags;
+ int ret;
+
+ spin_lock_irqsave(&dom->pgtlock, flags);
+ ret = dom->iop->map(dom->iop, iova, paddr, size, prot);
+ spin_unlock_irqrestore(&dom->pgtlock, flags);
+
+ return ret;
+}
+
+static size_t mtk_iommu_unmap(struct iommu_domain *domain,
+ unsigned long iova, size_t size)
+{
+ struct mtk_iommu_domain *dom = to_mtk_domain(domain);
+ unsigned long flags;
+ size_t unmapsz;
+
+ spin_lock_irqsave(&dom->pgtlock, flags);
+ unmapsz = dom->iop->unmap(dom->iop, iova, size);
+ spin_unlock_irqrestore(&dom->pgtlock, flags);
+
+ return unmapsz;
+}
+
+static phys_addr_t mtk_iommu_iova_to_phys(struct iommu_domain *domain,
+ dma_addr_t iova)
+{
+ struct mtk_iommu_domain *dom = to_mtk_domain(domain);
+ unsigned long flags;
+ phys_addr_t pa;
+
+ spin_lock_irqsave(&dom->pgtlock, flags);
+ pa = dom->iop->iova_to_phys(dom->iop, iova);
+ spin_unlock_irqrestore(&dom->pgtlock, flags);
+
+ return pa;
+}
+
+static int mtk_iommu_add_device(struct device *dev)
+{
+ struct iommu_group *group;
+
+ if (!dev->archdata.iommu) /* Not a iommu client device */
+ return -ENODEV;
+
+ group = iommu_group_get_for_dev(dev);
+ if (IS_ERR(group))
+ return PTR_ERR(group);
+
+ iommu_group_put(group);
+ return 0;
+}
+
+static void mtk_iommu_remove_device(struct device *dev)
+{
+ struct mtk_iommu_client_priv *head, *cur, *next;
+
+ head = dev->archdata.iommu;
+ if (!head)
+ return;
+
+ list_for_each_entry_safe(cur, next, &head->client, client) {
+ list_del(&cur->client);
+ kfree(cur);
+ }
+ kfree(head);
+ dev->archdata.iommu = NULL;
+
+ iommu_group_remove_device(dev);
+}
+
+static struct iommu_group *mtk_iommu_device_group(struct device *dev)
+{
+ struct mtk_iommu_data *data;
+ struct mtk_iommu_client_priv *priv;
+
+ priv = dev->archdata.iommu;
+ if (!priv)
+ return ERR_PTR(-ENODEV);
+
+ /* All the client devices are in the same m4u iommu-group */
+ data = dev_get_drvdata(priv->m4udev);
+ if (!data->m4u_group) {
+ data->m4u_group = iommu_group_alloc();
+ if (IS_ERR(data->m4u_group))
+ dev_err(dev, "Failed to allocate M4U IOMMU group\n");
+ }
+ return data->m4u_group;
+}
+
+static int mtk_iommu_of_xlate(struct device *dev, struct of_phandle_args *args)
+{
+ struct mtk_iommu_client_priv *head, *priv, *next;
+ struct platform_device *m4updev;
+
+ if (args->args_count != 1) {
+ dev_err(dev, "invalid #iommu-cells(%d) property for IOMMU\n",
+ args->args_count);
+ return -EINVAL;
+ }
+
+ if (!dev->archdata.iommu) {
+ /* Get the m4u device */
+ m4updev = of_find_device_by_node(args->np);
+ of_node_put(args->np);
+ if (WARN_ON(!m4updev))
+ return -EINVAL;
+
+ head = kzalloc(sizeof(*head), GFP_KERNEL);
+ if (!head)
+ return -ENOMEM;
+
+ dev->archdata.iommu = head;
+ INIT_LIST_HEAD(&head->client);
+ head->m4udev = &m4updev->dev;
+ } else {
+ head = dev->archdata.iommu;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ goto err_free_mem;
+
+ priv->mtk_m4u_id = args->args[0];
+ list_add_tail(&priv->client, &head->client);
+
+ return 0;
+
+err_free_mem:
+ list_for_each_entry_safe(priv, next, &head->client, client)
+ kfree(priv);
+ kfree(head);
+ dev->archdata.iommu = NULL;
+ return -ENOMEM;
+}
+
+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,
+ .remove_device = mtk_iommu_remove_device,
+ .device_group = mtk_iommu_device_group,
+ .of_xlate = mtk_iommu_of_xlate,
+ .pgsize_bitmap = SZ_4K | SZ_64K | SZ_1M | SZ_16M,
+};
+
+static int mtk_iommu_hw_init(const struct mtk_iommu_data *data)
+{
+ u32 regval;
+ int ret;
+
+ ret = clk_prepare_enable(data->bclk);
+ if (ret) {
+ dev_err(data->dev, "Failed to enable iommu bclk(%d)\n", ret);
+ return ret;
+ }
+
+ regval = F_MMU_PREFETCH_RT_REPLACE_MOD |
+ F_MMU_TF_PROTECT_SEL(2) |
+ F_COHERENCE_EN;
+ writel_relaxed(regval, data->base + REG_MMU_CTRL_REG);
+
+ regval = F_L2_MULIT_HIT_EN |
+ F_TABLE_WALK_FAULT_INT_EN |
+ F_PREETCH_FIFO_OVERFLOW_INT_EN |
+ F_MISS_FIFO_OVERFLOW_INT_EN |
+ F_PREFETCH_FIFO_ERR_INT_EN |
+ F_MISS_FIFO_ERR_INT_EN;
+ writel_relaxed(regval, data->base + REG_MMU_INT_CONTROL0);
+
+ regval = F_INT_TRANSLATION_FAULT |
+ F_INT_MAIN_MULTI_HIT_FAULT |
+ F_INT_INVALID_PA_FAULT |
+ F_INT_ENTRY_REPLACEMENT_FAULT |
+ F_INT_TLB_MISS_FAULT |
+ F_INT_MISS_TRANSATION_FIFO_FAULT |
+ F_INT_PRETETCH_TRANSATION_FIFO_FAULT;
+ writel_relaxed(regval, data->base + REG_MMU_INT_MAIN_CONTROL);
+
+ regval = F_MMU_IVRP_PA_SET(data->protect_base);
+ writel_relaxed(regval, data->base + REG_MMU_IVRP_PADDR);
+
+ writel_relaxed(0, data->base + REG_MMU_DCM_DIS);
+ writel_relaxed(0, data->base + REG_MMU_STANDARD_AXI_MODE);
+
+ if (devm_request_irq(data->dev, data->irq, mtk_iommu_isr, 0,
+ dev_name(data->dev), (void *)data)) {
+ writel_relaxed(0, data->base + REG_MMU_PT_BASE_ADDR);
+ clk_disable_unprepare(data->bclk);
+ dev_err(data->dev, "Failed @ IRQ-%d Request\n", data->irq);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int compare_of(struct device *dev, void *data)
+{
+ return dev->of_node == data;
+}
+
+static int mtk_iommu_bind(struct device *dev)
+{
+ struct mtk_iommu_data *data = dev_get_drvdata(dev);
+ void *protect;
+ int ret;
+
+ /* Protect memory. HW will access here while translation fault.*/
+ protect = devm_kzalloc(dev, MTK_PROTECT_PA_ALIGN * 2, GFP_KERNEL);
+ if (!protect)
+ return -ENOMEM;
+ data->protect_base = ALIGN(virt_to_phys(protect), MTK_PROTECT_PA_ALIGN);
+
+ ret = component_bind_all(dev, NULL);
+ if (ret)
+ return ret;
+
+ ret = mtk_iommu_hw_init(data);
+ if (ret) {
+ component_unbind_all(dev, NULL);
+ return ret;
+ }
+
+ if (!iommu_present(&platform_bus_type))
+ bus_set_iommu(&platform_bus_type, &mtk_iommu_ops);
+
+ return 0;
+}
+
+static void mtk_iommu_unbind(struct device *dev)
+{
+ struct mtk_iommu_data *data = dev_get_drvdata(dev);
+
+ if (iommu_present(&platform_bus_type))
+ bus_set_iommu(&platform_bus_type, NULL);
+
+ component_unbind_all(dev, NULL);
+ free_io_pgtable_ops(data->m4u_dom->iop);
+ clk_disable_unprepare(data->bclk);
+ devm_free_irq(dev, data->irq, data);
+}
+
+static const struct component_master_ops mtk_iommu_com_ops = {
+ .bind = mtk_iommu_bind,
+ .unbind = mtk_iommu_unbind,
+};
+
+static int mtk_iommu_probe(struct platform_device *pdev)
+{
+ struct mtk_iommu_data *data;
+ struct device *dev = &pdev->dev;
+ struct resource *res;
+ struct component_match *match = NULL;
+ int i;
+
+ data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ data->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ data->base = devm_ioremap_resource(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(
+ dev->of_node, "mediatek,larbs", 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(dev->of_node, "mediatek,larbs", i);
+ if (!larbnode)
+ return -EINVAL;
+
+ if (!of_device_is_available(larbnode))
+ continue;
+
+ plarbdev = of_find_device_by_node(larbnode);
+ of_node_put(larbnode);
+ if (!plarbdev)
+ return -EPROBE_DEFER;
+ data->larbdev[i] = &plarbdev->dev;
+
+ component_match_add(dev, &match, compare_of, larbnode);
+ }
+
+ dev_set_drvdata(dev, data);
+ return component_master_add_with_match(dev, &mtk_iommu_com_ops, match);
+}
+
+static int mtk_iommu_remove(struct platform_device *pdev)
+{
+ component_master_del(&pdev->dev, &mtk_iommu_com_ops);
+ return 0;
+}
+
+static int mtk_iommu_suspend(struct device *dev)
+{
+ struct mtk_iommu_data *data = dev_get_drvdata(dev);
+ struct mtk_iommu_suspend_reg *reg = &data->reg;
+ void __iomem *base = data->base;
+
+ reg->standard_axi_mode = readl_relaxed(base +
+ REG_MMU_STANDARD_AXI_MODE);
+ reg->dcm_dis = readl_relaxed(base + REG_MMU_DCM_DIS);
+ reg->ctrl_reg = readl_relaxed(base + REG_MMU_CTRL_REG);
+ reg->int_control0 = readl_relaxed(base + REG_MMU_INT_CONTROL0);
+ reg->int_main_control = readl_relaxed(base + REG_MMU_INT_MAIN_CONTROL);
+ return 0;
+}
+
+static int mtk_iommu_resume(struct device *dev)
+{
+ struct mtk_iommu_data *data = dev_get_drvdata(dev);
+ struct mtk_iommu_suspend_reg *reg = &data->reg;
+ void __iomem *base = data->base;
+
+ writel_relaxed(data->m4u_dom->cfg.arm_v7s_cfg.ttbr[0],
+ base + REG_MMU_PT_BASE_ADDR);
+ writel_relaxed(reg->standard_axi_mode,
+ base + REG_MMU_STANDARD_AXI_MODE);
+ writel_relaxed(reg->dcm_dis, base + REG_MMU_DCM_DIS);
+ writel_relaxed(reg->ctrl_reg, base + REG_MMU_CTRL_REG);
+ writel_relaxed(reg->int_control0, base + REG_MMU_INT_CONTROL0);
+ writel_relaxed(reg->int_main_control, base + REG_MMU_INT_MAIN_CONTROL);
+ writel_relaxed(F_MMU_IVRP_PA_SET(data->protect_base),
+ base + REG_MMU_IVRP_PADDR);
+ return 0;
+}
+
+const struct dev_pm_ops mtk_iommu_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(mtk_iommu_suspend, mtk_iommu_resume)
+};
+
+static const struct of_device_id mtk_iommu_of_ids[] = {
+ { .compatible = "mediatek,mt8173-m4u", },
+ {}
+};
+
+static struct platform_driver mtk_iommu_driver = {
+ .probe = mtk_iommu_probe,
+ .remove = mtk_iommu_remove,
+ .driver = {
+ .name = "mtk-iommu",
+ .of_match_table = mtk_iommu_of_ids,
+ .pm = &mtk_iommu_pm_ops,
+ }
+};
+
+static int __init mtk_iommu_init(void)
+{
+ return platform_driver_register(&mtk_iommu_driver);
+}
+subsys_initcall(mtk_iommu_init);
+
+static int mtk_iommu_init_fn(struct device_node *np)
+{
+ struct platform_device *pdev;
+
+ pdev = of_platform_device_create(np, NULL, platform_bus_type.dev_root);
+ if (IS_ERR(pdev))
+ return PTR_ERR(pdev);
+
+ of_iommu_set_ops(np, &mtk_iommu_ops);
+ return 0;
+}
+
+IOMMU_OF_DECLARE(mtkm4u, "mediatek,mt8173-m4u", mtk_iommu_init_fn);
--
1.8.1.1.dirty
^ permalink raw reply related [flat|nested] 24+ messages in thread
* Re: [PATCH v6 4/5] iommu/mediatek: Add mt8173 IOMMU driver
2015-12-08 9:49 ` [PATCH v6 4/5] iommu/mediatek: Add mt8173 IOMMU driver Yong Wu
@ 2015-12-08 10:32 ` kbuild test robot
[not found] ` <1449568153-15643-5-git-send-email-yong.wu-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
1 sibling, 0 replies; 24+ messages in thread
From: kbuild test robot @ 2015-12-08 10:32 UTC (permalink / raw)
Cc: kbuild-all, Joerg Roedel, Thierry Reding, Mark Rutland,
Matthias Brugger, Robin Murphy, Will Deacon, Daniel Kurtz,
Tomasz Figa, Lucas Stach, Rob Herring, Catalin Marinas,
linux-mediatek, Sasha Hauer, srv_heupstream, devicetree,
linux-kernel, linux-arm-kernel, iommu, pebolle, arnd, mitchelh,
p.zabel, yingjoe.chen, Yong Wu
[-- Attachment #1: Type: text/plain, Size: 7078 bytes --]
Hi Yong,
[auto build test ERROR on tegra/for-next]
[also build test ERROR on v4.4-rc4 next-20151208]
url: https://github.com/0day-ci/linux/commits/Yong-Wu/MT8173-IOMMU-SUPPORT/20151208-175252
base: https://git.kernel.org/pub/scm/linux/kernel/git/tegra/linux for-next
config: x86_64-allmodconfig (attached as .config)
reproduce:
# save the attached .config to linux build tree
make ARCH=x86_64
All error/warnings (new ones prefixed by >>):
drivers/iommu/dma-iommu.c: In function '__iommu_dma_alloc_pages':
>> drivers/iommu/dma-iommu.c:198:11: error: implicit declaration of function 'vzalloc' [-Werror=implicit-function-declaration]
pages = vzalloc(array_size);
^
>> drivers/iommu/dma-iommu.c:198:9: warning: assignment makes pointer from integer without a cast [-Wint-conversion]
pages = vzalloc(array_size);
^
cc1: some warnings being treated as errors
--
>> drivers/iommu/mtk_iommu.c:176:19: warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]
.tlb_add_flush = mtk_iommu_tlb_add_flush_nosync,
^
drivers/iommu/mtk_iommu.c:176:19: note: (near initialization for 'mtk_iommu_gather_ops.tlb_add_flush')
drivers/iommu/mtk_iommu.c: In function 'mtk_iommu_domain_finalise':
>> drivers/iommu/mtk_iommu.c:239:4: error: 'IO_PGTABLE_QUIRK_NO_PERMS' undeclared (first use in this function)
IO_PGTABLE_QUIRK_NO_PERMS |
^
drivers/iommu/mtk_iommu.c:239:4: note: each undeclared identifier is reported only once for each function it appears in
>> drivers/iommu/mtk_iommu.c:240:4: error: 'IO_PGTABLE_QUIRK_TLBI_ON_MAP' undeclared (first use in this function)
IO_PGTABLE_QUIRK_TLBI_ON_MAP,
^
>> drivers/iommu/mtk_iommu.c:248:34: error: 'ARM_V7S' undeclared (first use in this function)
dom->iop = alloc_io_pgtable_ops(ARM_V7S, &dom->cfg, data);
^
In file included from arch/x86/include/asm/realmode.h:5:0,
from arch/x86/include/asm/acpi.h:33,
from arch/x86/include/asm/fixmap.h:19,
from arch/x86/include/asm/apic.h:12,
from arch/x86/include/asm/smp.h:12,
from arch/x86/include/asm/mmzone_64.h:10,
from arch/x86/include/asm/mmzone.h:4,
from include/linux/mmzone.h:856,
from include/linux/topology.h:32,
from include/linux/of.h:24,
from include/linux/iommu.h:24,
from include/linux/dma-iommu.h:23,
from drivers/iommu/mtk_iommu.c:16:
>> drivers/iommu/mtk_iommu.c:257:35: error: 'struct io_pgtable_cfg' has no member named 'arm_v7s_cfg'
writel_relaxed(data->m4u_dom->cfg.arm_v7s_cfg.ttbr[0],
^
arch/x86/include/asm/io.h:81:39: note: in definition of macro 'writel_relaxed'
#define writel_relaxed(v, a) __writel(v, a)
^
drivers/iommu/mtk_iommu.c: In function 'mtk_iommu_resume':
drivers/iommu/mtk_iommu.c:702:35: error: 'struct io_pgtable_cfg' has no member named 'arm_v7s_cfg'
writel_relaxed(data->m4u_dom->cfg.arm_v7s_cfg.ttbr[0],
^
arch/x86/include/asm/io.h:81:39: note: in definition of macro 'writel_relaxed'
#define writel_relaxed(v, a) __writel(v, a)
^
vim +/IO_PGTABLE_QUIRK_NO_PERMS +239 drivers/iommu/mtk_iommu.c
170 /* Clear the CPE status */
171 writel_relaxed(0, data->base + REG_MMU_CPE_DONE);
172 }
173
174 static const struct iommu_gather_ops mtk_iommu_gather_ops = {
175 .tlb_flush_all = mtk_iommu_tlb_flush_all,
> 176 .tlb_add_flush = mtk_iommu_tlb_add_flush_nosync,
177 .tlb_sync = mtk_iommu_tlb_sync,
178 };
179
180 static irqreturn_t mtk_iommu_isr(int irq, void *dev_id)
181 {
182 struct mtk_iommu_data *data = dev_id;
183 struct mtk_iommu_domain *dom = data->m4u_dom;
184 u32 int_state, regval, fault_iova, fault_pa;
185 unsigned int fault_larb, fault_port;
186 bool layer, write;
187
188 /* Read error info from registers */
189 int_state = readl_relaxed(data->base + REG_MMU_FAULT_ST1);
190 fault_iova = readl_relaxed(data->base + REG_MMU_FAULT_VA);
191 layer = fault_iova & F_MMU_FAULT_VA_LAYER_BIT;
192 write = fault_iova & F_MMU_FAULT_VA_WRITE_BIT;
193 fault_iova &= F_MMU_FAULT_VA_MSK;
194 fault_pa = readl_relaxed(data->base + REG_MMU_INVLD_PA);
195 regval = readl_relaxed(data->base + REG_MMU_INT_ID);
196 fault_larb = F_MMU0_INT_ID_LARB_ID(regval);
197 fault_port = F_MMU0_INT_ID_PORT_ID(regval);
198
199 if (report_iommu_fault(&dom->domain, data->dev, fault_iova,
200 write ? IOMMU_FAULT_WRITE : IOMMU_FAULT_READ)) {
201 dev_err_ratelimited(
202 data->dev,
203 "fault type=0x%x iova=0x%x pa=0x%x larb=%d port=%d layer=%d %s\n",
204 int_state, fault_iova, fault_pa, fault_larb, fault_port,
205 layer, write ? "write" : "read");
206 }
207
208 /* Interrupt clear */
209 regval = readl_relaxed(data->base + REG_MMU_INT_CONTROL0);
210 regval |= F_INT_CLR_BIT;
211 writel_relaxed(regval, data->base + REG_MMU_INT_CONTROL0);
212
213 mtk_iommu_tlb_flush_all(data);
214
215 return IRQ_HANDLED;
216 }
217
218 static void mtk_iommu_config(struct mtk_iommu_data *data,
219 struct device *dev, bool enable)
220 {
221 struct mtk_iommu_client_priv *head, *cur, *next;
222
223 head = dev->archdata.iommu;
224 list_for_each_entry_safe(cur, next, &head->client, client) {
225 mtk_smi_config_port(
226 data->larbdev[MTK_M4U_TO_LARB(cur->mtk_m4u_id)],
227 MTK_M4U_TO_PORT(cur->mtk_m4u_id), enable);
228 }
229 }
230
231 static int mtk_iommu_domain_finalise(struct mtk_iommu_data *data)
232 {
233 struct mtk_iommu_domain *dom = data->m4u_dom;
234
235 spin_lock_init(&dom->pgtlock);
236
237 dom->cfg = (struct io_pgtable_cfg) {
238 .quirks = IO_PGTABLE_QUIRK_ARM_NS |
> 239 IO_PGTABLE_QUIRK_NO_PERMS |
> 240 IO_PGTABLE_QUIRK_TLBI_ON_MAP,
241 .pgsize_bitmap = mtk_iommu_ops.pgsize_bitmap,
242 .ias = 32,
243 .oas = 32,
244 .tlb = &mtk_iommu_gather_ops,
245 .iommu_dev = data->dev,
246 };
247
> 248 dom->iop = alloc_io_pgtable_ops(ARM_V7S, &dom->cfg, data);
249 if (!dom->iop) {
250 dev_err(data->dev, "Failed to alloc io pgtable\n");
251 return -EINVAL;
252 }
253
254 /* Update our support page sizes bitmap */
255 mtk_iommu_ops.pgsize_bitmap = dom->cfg.pgsize_bitmap;
256
> 257 writel_relaxed(data->m4u_dom->cfg.arm_v7s_cfg.ttbr[0],
258 data->base + REG_MMU_PT_BASE_ADDR);
259 return 0;
260 }
---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation
[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 51073 bytes --]
^ permalink raw reply [flat|nested] 24+ messages in thread
[parent not found: <1449568153-15643-5-git-send-email-yong.wu-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>]
* Re: [PATCH v6 4/5] iommu/mediatek: Add mt8173 IOMMU driver
[not found] ` <1449568153-15643-5-git-send-email-yong.wu-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
@ 2015-12-14 14:16 ` Joerg Roedel
[not found] ` <20151214141656.GG18805-zLv9SwRftAIdnm+yROfE0A@public.gmane.org>
2015-12-14 18:19 ` Matthias Brugger
1 sibling, 1 reply; 24+ messages in thread
From: Joerg Roedel @ 2015-12-14 14:16 UTC (permalink / raw)
To: Yong Wu
Cc: Mark Rutland, Catalin Marinas, Will Deacon,
yingjoe.chen-NuS5LvNUpcJWk0Htik3J/w, Thierry Reding,
devicetree-u79uwXL29TY76Z2rM5mHXA, Sasha Hauer, arnd-r2nGTMty4D4,
Tomasz Figa, Rob Herring,
linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Matthias Brugger,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
pebolle-IWqWACnzNjzz+pZb47iToQ,
srv_heupstream-NuS5LvNUpcJWk0Htik3J/w,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA, Daniel Kurtz,
p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ, Lucas Stach
On Tue, Dec 08, 2015 at 05:49:12PM +0800, Yong Wu wrote:
> +static int mtk_iommu_attach_device(struct iommu_domain *domain,
> + struct device *dev)
> +{
> + struct mtk_iommu_domain *dom = to_mtk_domain(domain);
> + struct mtk_iommu_client_priv *priv = dev->archdata.iommu;
> + struct mtk_iommu_data *data;
> + int ret;
> +
> + if (!priv)
> + return -ENODEV;
> +
> + data = dev_get_drvdata(priv->m4udev);
> + if (!data) {
> + /*
> + * The DMA core will run earlier than this probe, and it will
> + * create a default iommu domain for each a iommu device.
> + * But here there is only one domain called the m4u domain
> + * which all the multimedia HW share.
> + * The default domain isn't needed here.
> + */
The iommu core creates one domain per iommu-group. In your case this
means one default domain per iommu in the system.
> + iommu_domain_free(domain);
This function is not supposed to free the domain passed to it.
> +static int mtk_iommu_add_device(struct device *dev)
> +{
> + struct iommu_group *group;
> +
> + if (!dev->archdata.iommu) /* Not a iommu client device */
> + return -ENODEV;
> +
> + group = iommu_group_get_for_dev(dev);
> + if (IS_ERR(group))
> + return PTR_ERR(group);
> +
> + iommu_group_put(group);
> + return 0;
> +}
[...]
> +static struct iommu_group *mtk_iommu_device_group(struct device *dev)
> +{
> + struct mtk_iommu_data *data;
> + struct mtk_iommu_client_priv *priv;
> +
> + priv = dev->archdata.iommu;
> + if (!priv)
> + return ERR_PTR(-ENODEV);
> +
> + /* All the client devices are in the same m4u iommu-group */
> + data = dev_get_drvdata(priv->m4udev);
> + if (!data->m4u_group) {
> + data->m4u_group = iommu_group_alloc();
> + if (IS_ERR(data->m4u_group))
> + dev_err(dev, "Failed to allocate M4U IOMMU group\n");
> + }
> + return data->m4u_group;
> +}
This looks much better than before, thanks.
^ permalink raw reply [flat|nested] 24+ messages in thread
* Re: [PATCH v6 4/5] iommu/mediatek: Add mt8173 IOMMU driver
[not found] ` <1449568153-15643-5-git-send-email-yong.wu-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
2015-12-14 14:16 ` Joerg Roedel
@ 2015-12-14 18:19 ` Matthias Brugger
[not found] ` <566F0821.4080507-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
1 sibling, 1 reply; 24+ messages in thread
From: Matthias Brugger @ 2015-12-14 18:19 UTC (permalink / raw)
To: Yong Wu, Joerg Roedel, Thierry Reding, Mark Rutland
Cc: p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ, devicetree-u79uwXL29TY76Z2rM5mHXA,
pebolle-IWqWACnzNjzz+pZb47iToQ, arnd-r2nGTMty4D4,
srv_heupstream-NuS5LvNUpcJWk0Htik3J/w, Catalin Marinas,
Will Deacon, linux-kernel-u79uwXL29TY76Z2rM5mHXA, Tomasz Figa,
iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA, Rob Herring,
Daniel Kurtz, Sasha Hauer, yingjoe.chen-NuS5LvNUpcJWk0Htik3J/w,
linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Lucas Stach
On 08/12/15 10:49, Yong Wu wrote:
> This patch adds support for mediatek m4u (MultiMedia Memory Management
> Unit).
>
> Signed-off-by: Yong Wu <yong.wu-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> ---
> drivers/iommu/Kconfig | 15 +
> drivers/iommu/Makefile | 1 +
> drivers/iommu/mtk_iommu.c | 752 ++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 768 insertions(+)
> create mode 100644 drivers/iommu/mtk_iommu.c
>
> diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
> index b9094e9..aab942f 100644
> --- a/drivers/iommu/Kconfig
> +++ b/drivers/iommu/Kconfig
> @@ -393,4 +393,19 @@ config S390_IOMMU
> help
> Support for the IOMMU API for s390 PCI devices.
>
> +config MTK_IOMMU
> + bool "MTK IOMMU Support"
> + depends on ARCH_MEDIATEK || COMPILE_TEST
> + select IOMMU_API
> + select IOMMU_DMA
> + select IOMMU_IO_PGTABLE_ARMV7S
> + select MEMORY
> + select MTK_SMI
> + help
> + Support for the M4U on certain Mediatek SOCs. M4U is MultiMedia
> + Memory Management Unit. This option enables remapping of DMA memory
> + accesses for the multimedia subsystem.
> +
> + If unsure, say N here.
> +
> endif # IOMMU_SUPPORT
> diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
> index 68faca02..02887bc 100644
> --- a/drivers/iommu/Makefile
> +++ b/drivers/iommu/Makefile
> @@ -22,6 +22,7 @@ obj-$(CONFIG_ROCKCHIP_IOMMU) += rockchip-iommu.o
> obj-$(CONFIG_TEGRA_IOMMU_GART) += tegra-gart.o
> obj-$(CONFIG_TEGRA_IOMMU_SMMU) += tegra-smmu.o
> obj-$(CONFIG_EXYNOS_IOMMU) += exynos-iommu.o
> +obj-$(CONFIG_MTK_IOMMU) += mtk_iommu.o
> obj-$(CONFIG_SHMOBILE_IOMMU) += shmobile-iommu.o
> obj-$(CONFIG_SHMOBILE_IPMMU) += shmobile-ipmmu.o
> obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o
> diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c
> new file mode 100644
> index 0000000..6a21e70
> --- /dev/null
> +++ b/drivers/iommu/mtk_iommu.c
> @@ -0,0 +1,752 @@
> +/*
> + * Copyright (c) 2014-2015 MediaTek Inc.
> + * Author: Yong Wu <yong.wu-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +#include <linux/clk.h>
> +#include <linux/component.h>
> +#include <linux/dma-iommu.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/iommu.h>
> +#include <linux/iopoll.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/of_address.h>
> +#include <linux/of_iommu.h>
> +#include <linux/of_irq.h>
> +#include <linux/of_platform.h>
> +#include <linux/platform_device.h>
> +#include <soc/mediatek/smi.h>
> +#include <dt-bindings/memory/mt8173-larb-port.h>
> +
> +#include "io-pgtable.h"
> +
> +#define REG_MMU_PT_BASE_ADDR 0x000
> +
> +#define REG_MMU_INVALIDATE 0x020
> +#define F_ALL_INVLD 0x2
> +#define F_MMU_INV_RANGE 0x1
> +
> +#define REG_MMU_INVLD_START_A 0x024
> +#define REG_MMU_INVLD_END_A 0x028
> +
> +#define REG_MMU_INV_SEL 0x038
> +#define F_INVLD_EN0 BIT(0)
> +#define F_INVLD_EN1 BIT(1)
> +
> +#define REG_MMU_STANDARD_AXI_MODE 0x048
> +#define REG_MMU_DCM_DIS 0x050
> +
> +#define REG_MMU_CTRL_REG 0x110
> +#define F_MMU_PREFETCH_RT_REPLACE_MOD BIT(4)
> +#define F_MMU_TF_PROTECT_SEL(prot) (((prot) & 0x3) << 5)
> +#define F_COHERENCE_EN BIT(8)
> +
> +#define REG_MMU_IVRP_PADDR 0x114
> +#define F_MMU_IVRP_PA_SET(pa) ((pa) >> 1)
> +
> +#define REG_MMU_INT_CONTROL0 0x120
> +#define F_L2_MULIT_HIT_EN BIT(0)
> +#define F_TABLE_WALK_FAULT_INT_EN BIT(1)
> +#define F_PREETCH_FIFO_OVERFLOW_INT_EN BIT(2)
> +#define F_MISS_FIFO_OVERFLOW_INT_EN BIT(3)
> +#define F_PREFETCH_FIFO_ERR_INT_EN BIT(5)
> +#define F_MISS_FIFO_ERR_INT_EN BIT(6)
> +#define F_INT_CLR_BIT BIT(12)
> +
> +#define REG_MMU_INT_MAIN_CONTROL 0x124
> +#define F_INT_TRANSLATION_FAULT BIT(0)
> +#define F_INT_MAIN_MULTI_HIT_FAULT BIT(1)
> +#define F_INT_INVALID_PA_FAULT BIT(2)
> +#define F_INT_ENTRY_REPLACEMENT_FAULT BIT(3)
> +#define F_INT_TLB_MISS_FAULT BIT(4)
> +#define F_INT_MISS_TRANSATION_FIFO_FAULT BIT(5)
> +#define F_INT_PRETETCH_TRANSATION_FIFO_FAULT BIT(6)
> +
> +#define REG_MMU_CPE_DONE 0x12C
> +
> +#define REG_MMU_FAULT_ST1 0x134
> +
> +#define REG_MMU_FAULT_VA 0x13c
> +#define F_MMU_FAULT_VA_MSK 0xfffff000
> +#define F_MMU_FAULT_VA_WRITE_BIT BIT(1)
> +#define F_MMU_FAULT_VA_LAYER_BIT BIT(0)
> +
> +#define REG_MMU_INVLD_PA 0x140
> +#define REG_MMU_INT_ID 0x150
> +#define F_MMU0_INT_ID_LARB_ID(a) (((a) >> 7) & 0x7)
> +#define F_MMU0_INT_ID_PORT_ID(a) (((a) >> 2) & 0x1f)
> +
> +#define MTK_PROTECT_PA_ALIGN 128
> +#define MTK_IOMMU_LARB_MAX_NR 8
> +
> +struct mtk_iommu_suspend_reg {
> + u32 standard_axi_mode;
> + u32 dcm_dis;
> + u32 ctrl_reg;
> + u32 int_control0;
> + u32 int_main_control;
> +};
> +
> +struct mtk_iommu_client_priv {
> + struct list_head client;
> + unsigned int mtk_m4u_id;
> + struct device *m4udev;
> +};
> +
> +struct mtk_iommu_domain {
> + spinlock_t pgtlock; /* lock for page table */
> +
> + struct io_pgtable_cfg cfg;
> + struct io_pgtable_ops *iop;
> +
> + struct iommu_domain domain;
> +};
> +
> +struct mtk_iommu_data {
> + void __iomem *base;
> + int irq;
> + struct device *dev;
> + struct device *larbdev[MTK_IOMMU_LARB_MAX_NR];
> + struct clk *bclk;
> + phys_addr_t protect_base; /* protect memory base */
> + int larb_nr; /* local arbiter number */
> + struct mtk_iommu_suspend_reg reg;
> + struct mtk_iommu_domain *m4u_dom;
> + struct iommu_group *m4u_group;
> +};
> +
> +static struct iommu_ops mtk_iommu_ops;
> +
> +static struct mtk_iommu_domain *to_mtk_domain(struct iommu_domain *dom)
> +{
> + return container_of(dom, struct mtk_iommu_domain, domain);
> +}
> +
> +static void mtk_iommu_tlb_flush_all(void *cookie)
> +{
> + struct mtk_iommu_data *data = cookie;
> +
> + writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, data->base + REG_MMU_INV_SEL);
> + writel_relaxed(F_ALL_INVLD, data->base + REG_MMU_INVALIDATE);
> + wmb(); /* Make sure the tlb flush all done */
> +}
> +
> +static void mtk_iommu_tlb_add_flush_nosync(unsigned long iova, size_t size,
> + size_t granule, bool leaf,
> + void *cookie)
> +{
> + struct mtk_iommu_data *data = cookie;
> +
> + writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0, data->base + REG_MMU_INV_SEL);
> +
> + writel_relaxed(iova, data->base + REG_MMU_INVLD_START_A);
> + writel_relaxed(iova + size - 1, data->base + REG_MMU_INVLD_END_A);
> + writel_relaxed(F_MMU_INV_RANGE, data->base + REG_MMU_INVALIDATE);
> +}
> +
> +static void mtk_iommu_tlb_sync(void *cookie)
> +{
> + struct mtk_iommu_data *data = cookie;
> + int ret;
> + u32 tmp;
> +
> + ret = readl_poll_timeout_atomic(data->base + REG_MMU_CPE_DONE, tmp,
> + tmp != 0, 10, 100000);
> + if (ret) {
> + dev_warn(data->dev,
> + "Partial TLB flush timed out, falling back to full flush\n");
> + mtk_iommu_tlb_flush_all(cookie);
> + }
> + /* Clear the CPE status */
> + writel_relaxed(0, data->base + REG_MMU_CPE_DONE);
> +}
> +
> +static const struct iommu_gather_ops mtk_iommu_gather_ops = {
> + .tlb_flush_all = mtk_iommu_tlb_flush_all,
> + .tlb_add_flush = mtk_iommu_tlb_add_flush_nosync,
> + .tlb_sync = mtk_iommu_tlb_sync,
> +};
> +
> +static irqreturn_t mtk_iommu_isr(int irq, void *dev_id)
> +{
> + struct mtk_iommu_data *data = dev_id;
> + struct mtk_iommu_domain *dom = data->m4u_dom;
> + u32 int_state, regval, fault_iova, fault_pa;
> + unsigned int fault_larb, fault_port;
> + bool layer, write;
> +
> + /* Read error info from registers */
> + int_state = readl_relaxed(data->base + REG_MMU_FAULT_ST1);
> + fault_iova = readl_relaxed(data->base + REG_MMU_FAULT_VA);
> + layer = fault_iova & F_MMU_FAULT_VA_LAYER_BIT;
> + write = fault_iova & F_MMU_FAULT_VA_WRITE_BIT;
> + fault_iova &= F_MMU_FAULT_VA_MSK;
> + fault_pa = readl_relaxed(data->base + REG_MMU_INVLD_PA);
> + regval = readl_relaxed(data->base + REG_MMU_INT_ID);
> + fault_larb = F_MMU0_INT_ID_LARB_ID(regval);
> + fault_port = F_MMU0_INT_ID_PORT_ID(regval);
> +
> + if (report_iommu_fault(&dom->domain, data->dev, fault_iova,
> + write ? IOMMU_FAULT_WRITE : IOMMU_FAULT_READ)) {
> + dev_err_ratelimited(
> + data->dev,
> + "fault type=0x%x iova=0x%x pa=0x%x larb=%d port=%d layer=%d %s\n",
> + int_state, fault_iova, fault_pa, fault_larb, fault_port,
> + layer, write ? "write" : "read");
> + }
> +
> + /* Interrupt clear */
> + regval = readl_relaxed(data->base + REG_MMU_INT_CONTROL0);
> + regval |= F_INT_CLR_BIT;
> + writel_relaxed(regval, data->base + REG_MMU_INT_CONTROL0);
> +
> + mtk_iommu_tlb_flush_all(data);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void mtk_iommu_config(struct mtk_iommu_data *data,
> + struct device *dev, bool enable)
> +{
> + struct mtk_iommu_client_priv *head, *cur, *next;
> +
> + head = dev->archdata.iommu;
> + list_for_each_entry_safe(cur, next, &head->client, client) {
> + mtk_smi_config_port(
> + data->larbdev[MTK_M4U_TO_LARB(cur->mtk_m4u_id)],
> + MTK_M4U_TO_PORT(cur->mtk_m4u_id), enable);
Use an extra variable for MTK_M4U_TO_LARB(cur->mtk-m4u_id), this makes
the code easier to read.
Regards,
Matthias
> + }
> +}
> +
> +static int mtk_iommu_domain_finalise(struct mtk_iommu_data *data)
> +{
> + struct mtk_iommu_domain *dom = data->m4u_dom;
> +
> + spin_lock_init(&dom->pgtlock);
> +
> + dom->cfg = (struct io_pgtable_cfg) {
> + .quirks = IO_PGTABLE_QUIRK_ARM_NS |
> + IO_PGTABLE_QUIRK_NO_PERMS |
> + IO_PGTABLE_QUIRK_TLBI_ON_MAP,
> + .pgsize_bitmap = mtk_iommu_ops.pgsize_bitmap,
> + .ias = 32,
> + .oas = 32,
> + .tlb = &mtk_iommu_gather_ops,
> + .iommu_dev = data->dev,
> + };
> +
> + dom->iop = alloc_io_pgtable_ops(ARM_V7S, &dom->cfg, data);
> + if (!dom->iop) {
> + dev_err(data->dev, "Failed to alloc io pgtable\n");
> + return -EINVAL;
> + }
> +
> + /* Update our support page sizes bitmap */
> + mtk_iommu_ops.pgsize_bitmap = dom->cfg.pgsize_bitmap;
> +
> + writel_relaxed(data->m4u_dom->cfg.arm_v7s_cfg.ttbr[0],
> + data->base + REG_MMU_PT_BASE_ADDR);
> + return 0;
> +}
> +
> +static struct iommu_domain *mtk_iommu_domain_alloc(unsigned type)
> +{
> + struct mtk_iommu_domain *dom;
> +
> + if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_DMA)
> + return NULL;
> +
> + dom = kzalloc(sizeof(*dom), GFP_KERNEL);
> + if (!dom)
> + return NULL;
> +
> + if (type == IOMMU_DOMAIN_DMA && iommu_get_dma_cookie(&dom->domain)) {
> + kfree(dom);
> + return NULL;
> + }
> +
> + dom->domain.geometry.aperture_start = 0;
> + dom->domain.geometry.aperture_end = DMA_BIT_MASK(32);
> + dom->domain.geometry.force_aperture = true;
> +
> + return &dom->domain;
> +}
> +
> +static void mtk_iommu_domain_free(struct iommu_domain *domain)
> +{
> + if (domain->type == IOMMU_DOMAIN_DMA)
> + iommu_put_dma_cookie(domain);
> + kfree(to_mtk_domain(domain));
> +}
> +
> +static int mtk_iommu_attach_device(struct iommu_domain *domain,
> + struct device *dev)
> +{
> + struct mtk_iommu_domain *dom = to_mtk_domain(domain);
> + struct mtk_iommu_client_priv *priv = dev->archdata.iommu;
> + struct mtk_iommu_data *data;
> + int ret;
> +
> + if (!priv)
> + return -ENODEV;
> +
> + data = dev_get_drvdata(priv->m4udev);
> + if (!data) {
> + /*
> + * The DMA core will run earlier than this probe, and it will
> + * create a default iommu domain for each a iommu device.
> + * But here there is only one domain called the m4u domain
> + * which all the multimedia HW share.
> + * The default domain isn't needed here.
> + */
> + iommu_domain_free(domain);
> +
> + /* Return 0 for DMA to update dev->archdata.dma_ops */
> + return 0;
> + } else if (!data->m4u_dom) {
> + /*
> + * While a device is added into a iommu group, the iommu core
> + * will create a default domain for each a iommu group.
> + * This default domain is reserved as the m4u domain and is
> + * initiated here.
> + */
> + data->m4u_dom = dom;
> +
> + if (domain->type == IOMMU_DOMAIN_DMA) {
> + ret = iommu_dma_init_domain(domain, 0,
> + DMA_BIT_MASK(32));
> + if (ret)
> + goto err_uninit_dom;
> + }
> +
> + ret = mtk_iommu_domain_finalise(data);
> + if (ret)
> + goto err_uninit_dom;
> + } else if (data->m4u_dom != dom) {
> + /* All the client devices should be in the same m4u domain */
> + dev_err(dev, "try to attach into the error iommu domain\n");
> + return -EPERM;
> + }
> +
> + mtk_iommu_config(data, dev, true);
> + return 0;
> +
> +err_uninit_dom:
> + data->m4u_dom = NULL;
> + return ret;
> +}
> +
> +static void mtk_iommu_detach_device(struct iommu_domain *domain,
> + struct device *dev)
> +{
> + struct mtk_iommu_client_priv *priv = dev->archdata.iommu;
> + struct mtk_iommu_data *data;
> +
> + if (!priv)
> + return;
> +
> + data = dev_get_drvdata(priv->m4udev);
> + if (data)
> + mtk_iommu_config(data, dev, false);
> +}
> +
> +static int mtk_iommu_map(struct iommu_domain *domain, unsigned long iova,
> + phys_addr_t paddr, size_t size, int prot)
> +{
> + struct mtk_iommu_domain *dom = to_mtk_domain(domain);
> + unsigned long flags;
> + int ret;
> +
> + spin_lock_irqsave(&dom->pgtlock, flags);
> + ret = dom->iop->map(dom->iop, iova, paddr, size, prot);
> + spin_unlock_irqrestore(&dom->pgtlock, flags);
> +
> + return ret;
> +}
> +
> +static size_t mtk_iommu_unmap(struct iommu_domain *domain,
> + unsigned long iova, size_t size)
> +{
> + struct mtk_iommu_domain *dom = to_mtk_domain(domain);
> + unsigned long flags;
> + size_t unmapsz;
> +
> + spin_lock_irqsave(&dom->pgtlock, flags);
> + unmapsz = dom->iop->unmap(dom->iop, iova, size);
> + spin_unlock_irqrestore(&dom->pgtlock, flags);
> +
> + return unmapsz;
> +}
> +
> +static phys_addr_t mtk_iommu_iova_to_phys(struct iommu_domain *domain,
> + dma_addr_t iova)
> +{
> + struct mtk_iommu_domain *dom = to_mtk_domain(domain);
> + unsigned long flags;
> + phys_addr_t pa;
> +
> + spin_lock_irqsave(&dom->pgtlock, flags);
> + pa = dom->iop->iova_to_phys(dom->iop, iova);
> + spin_unlock_irqrestore(&dom->pgtlock, flags);
> +
> + return pa;
> +}
> +
> +static int mtk_iommu_add_device(struct device *dev)
> +{
> + struct iommu_group *group;
> +
> + if (!dev->archdata.iommu) /* Not a iommu client device */
> + return -ENODEV;
> +
> + group = iommu_group_get_for_dev(dev);
> + if (IS_ERR(group))
> + return PTR_ERR(group);
> +
> + iommu_group_put(group);
> + return 0;
> +}
> +
> +static void mtk_iommu_remove_device(struct device *dev)
> +{
> + struct mtk_iommu_client_priv *head, *cur, *next;
> +
> + head = dev->archdata.iommu;
> + if (!head)
> + return;
> +
> + list_for_each_entry_safe(cur, next, &head->client, client) {
> + list_del(&cur->client);
> + kfree(cur);
> + }
> + kfree(head);
> + dev->archdata.iommu = NULL;
> +
> + iommu_group_remove_device(dev);
> +}
> +
> +static struct iommu_group *mtk_iommu_device_group(struct device *dev)
> +{
> + struct mtk_iommu_data *data;
> + struct mtk_iommu_client_priv *priv;
> +
> + priv = dev->archdata.iommu;
> + if (!priv)
> + return ERR_PTR(-ENODEV);
> +
> + /* All the client devices are in the same m4u iommu-group */
> + data = dev_get_drvdata(priv->m4udev);
> + if (!data->m4u_group) {
> + data->m4u_group = iommu_group_alloc();
> + if (IS_ERR(data->m4u_group))
> + dev_err(dev, "Failed to allocate M4U IOMMU group\n");
> + }
> + return data->m4u_group;
> +}
> +
> +static int mtk_iommu_of_xlate(struct device *dev, struct of_phandle_args *args)
> +{
> + struct mtk_iommu_client_priv *head, *priv, *next;
> + struct platform_device *m4updev;
> +
> + if (args->args_count != 1) {
> + dev_err(dev, "invalid #iommu-cells(%d) property for IOMMU\n",
> + args->args_count);
> + return -EINVAL;
> + }
> +
> + if (!dev->archdata.iommu) {
> + /* Get the m4u device */
> + m4updev = of_find_device_by_node(args->np);
> + of_node_put(args->np);
> + if (WARN_ON(!m4updev))
> + return -EINVAL;
> +
> + head = kzalloc(sizeof(*head), GFP_KERNEL);
> + if (!head)
> + return -ENOMEM;
> +
> + dev->archdata.iommu = head;
> + INIT_LIST_HEAD(&head->client);
> + head->m4udev = &m4updev->dev;
> + } else {
> + head = dev->archdata.iommu;
> + }
> +
> + priv = kzalloc(sizeof(*priv), GFP_KERNEL);
> + if (!priv)
> + goto err_free_mem;
> +
> + priv->mtk_m4u_id = args->args[0];
> + list_add_tail(&priv->client, &head->client);
> +
> + return 0;
> +
> +err_free_mem:
> + list_for_each_entry_safe(priv, next, &head->client, client)
> + kfree(priv);
> + kfree(head);
> + dev->archdata.iommu = NULL;
> + return -ENOMEM;
> +}
> +
> +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,
> + .remove_device = mtk_iommu_remove_device,
> + .device_group = mtk_iommu_device_group,
> + .of_xlate = mtk_iommu_of_xlate,
> + .pgsize_bitmap = SZ_4K | SZ_64K | SZ_1M | SZ_16M,
> +};
> +
> +static int mtk_iommu_hw_init(const struct mtk_iommu_data *data)
> +{
> + u32 regval;
> + int ret;
> +
> + ret = clk_prepare_enable(data->bclk);
> + if (ret) {
> + dev_err(data->dev, "Failed to enable iommu bclk(%d)\n", ret);
> + return ret;
> + }
> +
> + regval = F_MMU_PREFETCH_RT_REPLACE_MOD |
> + F_MMU_TF_PROTECT_SEL(2) |
> + F_COHERENCE_EN;
> + writel_relaxed(regval, data->base + REG_MMU_CTRL_REG);
> +
> + regval = F_L2_MULIT_HIT_EN |
> + F_TABLE_WALK_FAULT_INT_EN |
> + F_PREETCH_FIFO_OVERFLOW_INT_EN |
> + F_MISS_FIFO_OVERFLOW_INT_EN |
> + F_PREFETCH_FIFO_ERR_INT_EN |
> + F_MISS_FIFO_ERR_INT_EN;
> + writel_relaxed(regval, data->base + REG_MMU_INT_CONTROL0);
> +
> + regval = F_INT_TRANSLATION_FAULT |
> + F_INT_MAIN_MULTI_HIT_FAULT |
> + F_INT_INVALID_PA_FAULT |
> + F_INT_ENTRY_REPLACEMENT_FAULT |
> + F_INT_TLB_MISS_FAULT |
> + F_INT_MISS_TRANSATION_FIFO_FAULT |
> + F_INT_PRETETCH_TRANSATION_FIFO_FAULT;
> + writel_relaxed(regval, data->base + REG_MMU_INT_MAIN_CONTROL);
> +
> + regval = F_MMU_IVRP_PA_SET(data->protect_base);
> + writel_relaxed(regval, data->base + REG_MMU_IVRP_PADDR);
> +
> + writel_relaxed(0, data->base + REG_MMU_DCM_DIS);
> + writel_relaxed(0, data->base + REG_MMU_STANDARD_AXI_MODE);
> +
> + if (devm_request_irq(data->dev, data->irq, mtk_iommu_isr, 0,
> + dev_name(data->dev), (void *)data)) {
> + writel_relaxed(0, data->base + REG_MMU_PT_BASE_ADDR);
> + clk_disable_unprepare(data->bclk);
> + dev_err(data->dev, "Failed @ IRQ-%d Request\n", data->irq);
> + return -ENODEV;
> + }
> +
> + return 0;
> +}
> +
> +static int compare_of(struct device *dev, void *data)
> +{
> + return dev->of_node == data;
> +}
> +
> +static int mtk_iommu_bind(struct device *dev)
> +{
> + struct mtk_iommu_data *data = dev_get_drvdata(dev);
> + void *protect;
> + int ret;
> +
> + /* Protect memory. HW will access here while translation fault.*/
> + protect = devm_kzalloc(dev, MTK_PROTECT_PA_ALIGN * 2, GFP_KERNEL);
> + if (!protect)
> + return -ENOMEM;
> + data->protect_base = ALIGN(virt_to_phys(protect), MTK_PROTECT_PA_ALIGN);
> +
> + ret = component_bind_all(dev, NULL);
> + if (ret)
> + return ret;
> +
> + ret = mtk_iommu_hw_init(data);
> + if (ret) {
> + component_unbind_all(dev, NULL);
> + return ret;
> + }
> +
> + if (!iommu_present(&platform_bus_type))
> + bus_set_iommu(&platform_bus_type, &mtk_iommu_ops);
> +
> + return 0;
> +}
> +
> +static void mtk_iommu_unbind(struct device *dev)
> +{
> + struct mtk_iommu_data *data = dev_get_drvdata(dev);
> +
> + if (iommu_present(&platform_bus_type))
> + bus_set_iommu(&platform_bus_type, NULL);
> +
> + component_unbind_all(dev, NULL);
> + free_io_pgtable_ops(data->m4u_dom->iop);
> + clk_disable_unprepare(data->bclk);
> + devm_free_irq(dev, data->irq, data);
> +}
> +
> +static const struct component_master_ops mtk_iommu_com_ops = {
> + .bind = mtk_iommu_bind,
> + .unbind = mtk_iommu_unbind,
> +};
> +
> +static int mtk_iommu_probe(struct platform_device *pdev)
> +{
> + struct mtk_iommu_data *data;
> + struct device *dev = &pdev->dev;
> + struct resource *res;
> + struct component_match *match = NULL;
> + int i;
> +
> + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
> + if (!data)
> + return -ENOMEM;
> + data->dev = dev;
> +
> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> + data->base = devm_ioremap_resource(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(
> + dev->of_node, "mediatek,larbs", 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(dev->of_node, "mediatek,larbs", i);
> + if (!larbnode)
> + return -EINVAL;
> +
> + if (!of_device_is_available(larbnode))
> + continue;
> +
> + plarbdev = of_find_device_by_node(larbnode);
> + of_node_put(larbnode);
> + if (!plarbdev)
> + return -EPROBE_DEFER;
> + data->larbdev[i] = &plarbdev->dev;
> +
> + component_match_add(dev, &match, compare_of, larbnode);
> + }
> +
> + dev_set_drvdata(dev, data);
> + return component_master_add_with_match(dev, &mtk_iommu_com_ops, match);
> +}
> +
> +static int mtk_iommu_remove(struct platform_device *pdev)
> +{
> + component_master_del(&pdev->dev, &mtk_iommu_com_ops);
> + return 0;
> +}
> +
> +static int mtk_iommu_suspend(struct device *dev)
> +{
> + struct mtk_iommu_data *data = dev_get_drvdata(dev);
> + struct mtk_iommu_suspend_reg *reg = &data->reg;
> + void __iomem *base = data->base;
> +
> + reg->standard_axi_mode = readl_relaxed(base +
> + REG_MMU_STANDARD_AXI_MODE);
> + reg->dcm_dis = readl_relaxed(base + REG_MMU_DCM_DIS);
> + reg->ctrl_reg = readl_relaxed(base + REG_MMU_CTRL_REG);
> + reg->int_control0 = readl_relaxed(base + REG_MMU_INT_CONTROL0);
> + reg->int_main_control = readl_relaxed(base + REG_MMU_INT_MAIN_CONTROL);
> + return 0;
> +}
> +
> +static int mtk_iommu_resume(struct device *dev)
> +{
> + struct mtk_iommu_data *data = dev_get_drvdata(dev);
> + struct mtk_iommu_suspend_reg *reg = &data->reg;
> + void __iomem *base = data->base;
> +
> + writel_relaxed(data->m4u_dom->cfg.arm_v7s_cfg.ttbr[0],
> + base + REG_MMU_PT_BASE_ADDR);
> + writel_relaxed(reg->standard_axi_mode,
> + base + REG_MMU_STANDARD_AXI_MODE);
> + writel_relaxed(reg->dcm_dis, base + REG_MMU_DCM_DIS);
> + writel_relaxed(reg->ctrl_reg, base + REG_MMU_CTRL_REG);
> + writel_relaxed(reg->int_control0, base + REG_MMU_INT_CONTROL0);
> + writel_relaxed(reg->int_main_control, base + REG_MMU_INT_MAIN_CONTROL);
> + writel_relaxed(F_MMU_IVRP_PA_SET(data->protect_base),
> + base + REG_MMU_IVRP_PADDR);
> + return 0;
> +}
> +
> +const struct dev_pm_ops mtk_iommu_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(mtk_iommu_suspend, mtk_iommu_resume)
> +};
> +
> +static const struct of_device_id mtk_iommu_of_ids[] = {
> + { .compatible = "mediatek,mt8173-m4u", },
> + {}
> +};
> +
> +static struct platform_driver mtk_iommu_driver = {
> + .probe = mtk_iommu_probe,
> + .remove = mtk_iommu_remove,
> + .driver = {
> + .name = "mtk-iommu",
> + .of_match_table = mtk_iommu_of_ids,
> + .pm = &mtk_iommu_pm_ops,
> + }
> +};
> +
> +static int __init mtk_iommu_init(void)
> +{
> + return platform_driver_register(&mtk_iommu_driver);
> +}
> +subsys_initcall(mtk_iommu_init);
> +
> +static int mtk_iommu_init_fn(struct device_node *np)
> +{
> + struct platform_device *pdev;
> +
> + pdev = of_platform_device_create(np, NULL, platform_bus_type.dev_root);
> + if (IS_ERR(pdev))
> + return PTR_ERR(pdev);
> +
> + of_iommu_set_ops(np, &mtk_iommu_ops);
> + return 0;
> +}
> +
> +IOMMU_OF_DECLARE(mtkm4u, "mediatek,mt8173-m4u", mtk_iommu_init_fn);
>
^ permalink raw reply [flat|nested] 24+ messages in thread
[parent not found: <1449568153-15643-1-git-send-email-yong.wu-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>]
* [PATCH v6 5/5] dts: mt8173: Add iommu/smi nodes for mt8173
[not found] ` <1449568153-15643-1-git-send-email-yong.wu-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
@ 2015-12-08 9:49 ` Yong Wu
0 siblings, 0 replies; 24+ messages in thread
From: Yong Wu @ 2015-12-08 9:49 UTC (permalink / raw)
To: Joerg Roedel, Thierry Reding, Mark Rutland, Matthias Brugger
Cc: Robin Murphy, Will Deacon, Daniel Kurtz, Tomasz Figa, Lucas Stach,
Rob Herring, Catalin Marinas,
linux-mediatek-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Sasha Hauer,
srv_heupstream-NuS5LvNUpcJWk0Htik3J/w,
devicetree-u79uwXL29TY76Z2rM5mHXA,
linux-kernel-u79uwXL29TY76Z2rM5mHXA,
linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
iommu-cunTk1MwBs9QetFLy7KEm3xJsTq8ys+cHZ5vskTnxNA,
pebolle-IWqWACnzNjzz+pZb47iToQ, arnd-r2nGTMty4D4,
mitchelh-sgV2jX0FEOL9JmXXK+q4OQ, p.zabel-bIcnvbaLZ9MEGnE8C9+IrQ,
yingjoe.chen-NuS5LvNUpcJWk0Htik3J/w, Yong Wu
This patch add the iommu/larbs nodes for mt8173
Signed-off-by: Yong Wu <yong.wu-NuS5LvNUpcJWk0Htik3J/w@public.gmane.org>
---
arch/arm64/boot/dts/mediatek/mt8173.dtsi | 81 ++++++++++++++++++++++++++++++++
1 file changed, 81 insertions(+)
diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
index 4dd5f93..8b50951 100644
--- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi
@@ -14,6 +14,7 @@
#include <dt-bindings/clock/mt8173-clk.h>
#include <dt-bindings/interrupt-controller/irq.h>
#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/memory/mt8173-larb-port.h>
#include <dt-bindings/power/mt8173-power.h>
#include <dt-bindings/reset-controller/mt8173-resets.h>
#include "mt8173-pinfunc.h"
@@ -267,6 +268,17 @@
reg = <0 0x10200620 0 0x20>;
};
+ iommu: iommu@10205000 {
+ compatible = "mediatek,mt8173-m4u";
+ reg = <0 0x10205000 0 0x1000>;
+ interrupts = <GIC_SPI 139 IRQ_TYPE_LEVEL_LOW>;
+ clocks = <&infracfg CLK_INFRA_M4U>;
+ clock-names = "bclk";
+ mediatek,larbs = <&larb0 &larb1 &larb2
+ &larb3 &larb4 &larb5>;
+ #iommu-cells = <1>;
+ };
+
apmixedsys: clock-controller@10209000 {
compatible = "mediatek,mt8173-apmixedsys";
reg = <0 0x10209000 0 0x1000>;
@@ -516,29 +528,98 @@
#clock-cells = <1>;
};
+ larb0: larb@14021000 {
+ compatible = "mediatek,mt8173-smi-larb";
+ reg = <0 0x14021000 0 0x1000>;
+ mediatek,smi = <&smi_common>;
+ power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+ clocks = <&mmsys CLK_MM_SMI_LARB0>,
+ <&mmsys CLK_MM_SMI_LARB0>;
+ clock-names = "apb", "smi";
+ };
+
+ smi_common: smi@14022000 {
+ compatible = "mediatek,mt8173-smi-common";
+ reg = <0 0x14022000 0 0x1000>;
+ power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+ clocks = <&mmsys CLK_MM_SMI_COMMON>,
+ <&mmsys CLK_MM_SMI_COMMON>;
+ clock-names = "apb", "smi";
+ };
+
+ larb4: larb@14027000 {
+ compatible = "mediatek,mt8173-smi-larb";
+ reg = <0 0x14027000 0 0x1000>;
+ mediatek,smi = <&smi_common>;
+ power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
+ clocks = <&mmsys CLK_MM_SMI_LARB4>,
+ <&mmsys CLK_MM_SMI_LARB4>;
+ clock-names = "apb", "smi";
+ };
+
imgsys: clock-controller@15000000 {
compatible = "mediatek,mt8173-imgsys", "syscon";
reg = <0 0x15000000 0 0x1000>;
#clock-cells = <1>;
};
+ larb2: larb@15001000 {
+ compatible = "mediatek,mt8173-smi-larb";
+ reg = <0 0x15001000 0 0x1000>;
+ mediatek,smi = <&smi_common>;
+ power-domains = <&scpsys MT8173_POWER_DOMAIN_ISP>;
+ clocks = <&imgsys CLK_IMG_LARB2_SMI>,
+ <&imgsys CLK_IMG_LARB2_SMI>;
+ clock-names = "apb", "smi";
+ };
+
vdecsys: clock-controller@16000000 {
compatible = "mediatek,mt8173-vdecsys", "syscon";
reg = <0 0x16000000 0 0x1000>;
#clock-cells = <1>;
};
+ larb1: larb@16010000 {
+ compatible = "mediatek,mt8173-smi-larb";
+ reg = <0 0x16010000 0 0x1000>;
+ mediatek,smi = <&smi_common>;
+ power-domains = <&scpsys MT8173_POWER_DOMAIN_VDEC>;
+ clocks = <&vdecsys CLK_VDEC_CKEN>,
+ <&vdecsys CLK_VDEC_LARB_CKEN>;
+ clock-names = "apb", "smi";
+ };
+
vencsys: clock-controller@18000000 {
compatible = "mediatek,mt8173-vencsys", "syscon";
reg = <0 0x18000000 0 0x1000>;
#clock-cells = <1>;
};
+ larb3: larb@18001000 {
+ compatible = "mediatek,mt8173-smi-larb";
+ reg = <0 0x18001000 0 0x1000>;
+ mediatek,smi = <&smi_common>;
+ power-domains = <&scpsys MT8173_POWER_DOMAIN_VENC>;
+ clocks = <&vencsys CLK_VENC_CKE1>,
+ <&vencsys CLK_VENC_CKE0>;
+ clock-names = "apb", "smi";
+ };
+
vencltsys: clock-controller@19000000 {
compatible = "mediatek,mt8173-vencltsys", "syscon";
reg = <0 0x19000000 0 0x1000>;
#clock-cells = <1>;
};
+
+ larb5: larb@19001000 {
+ compatible = "mediatek,mt8173-smi-larb";
+ reg = <0 0x19001000 0 0x1000>;
+ mediatek,smi = <&smi_common>;
+ power-domains = <&scpsys MT8173_POWER_DOMAIN_VENC_LT>;
+ clocks = <&vencltsys CLK_VENCLT_CKE1>,
+ <&vencltsys CLK_VENCLT_CKE0>;
+ clock-names = "apb", "smi";
+ };
};
};
--
1.8.1.1.dirty
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply related [flat|nested] 24+ messages in thread