Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > linux.kernel > #1198710 > unrolled thread
| Started by | Yong Wu <yong.wu@mediatek.com> |
|---|---|
| First post | 2015-08-03 12:30 +0200 |
| Last post | 2015-08-03 12:30 +0200 |
| Articles | 6 — 1 participant |
Back to article view | Back to linux.kernel
[PATCH v4 0/6] MT8173 IOMMU SUPPORT Yong Wu <yong.wu@mediatek.com> - 2015-08-03 12:30 +0200
[PATCH v4 2/6] dt-bindings: mediatek: Add smi dts binding Yong Wu <yong.wu@mediatek.com> - 2015-08-03 12:30 +0200
[PATCH v4 3/6] iommu: add ARM short descriptor page table allocator. Yong Wu <yong.wu@mediatek.com> - 2015-08-03 12:30 +0200
[PATCH v4 6/6] dts: mt8173: Add iommu/smi nodes for mt8173 Yong Wu <yong.wu@mediatek.com> - 2015-08-03 12:30 +0200
[PATCH v4 1/6] dt-bindings: iommu: Add binding for mediatek IOMMU Yong Wu <yong.wu@mediatek.com> - 2015-08-03 12:30 +0200
[PATCH v4 4/6] memory: mediatek: Add SMI driver Yong Wu <yong.wu@mediatek.com> - 2015-08-03 12:30 +0200
| From | Yong Wu <yong.wu@mediatek.com> |
|---|---|
| Date | 2015-08-03 12:30 +0200 |
| Subject | [PATCH v4 0/6] MT8173 IOMMU SUPPORT |
| Message-ID | <pTlah-fp-5@gated-at.bofh.it> |
This patch set adds support for m4u(Multimedia Memory Management Unit),
Currently it only support the m4u with 2 levels of pagetable on mt8173.
It's based on Robin Murphy's arm64 DMA-v5[1] and Robin's allow DMA API use in
io-pgtable-arm[2]. The dsti is based on MTK clock patch[3].
Please check the hardware block diagram of Mediatek IOMMU.
EMI (External Memory Interface)
|
m4u (Multimedia Memory Management Unit)
|
smi (Smart Multimedia Interface)
|
+---------------+-------
| |
| |
vdec larb disp larb ... SoCs have different local arbiter(larb).
| |
| |
+----+----+ +-----+-----+
| | | | | | ...
| | | | | | ...
| | | | | | ...
MC PP VLD OVL0 RDMA0 WDMA0 ... There are different ports in each larb.
Normally we specify a local arbiter(larb) for each multimedia hardware like
display, video decode, video encode and camera. And there are different ports in
each larb. Take a example, there are some ports like MC, PP, UFO, VLD, AVC_MV,
PRED_RD in video larb, all the ports are according to the video hardware.
From the diagram, all the multimedia module connect with m4u via smi.
SMI is responsible to enable/disable iommu and control the clocks for each local
arbiter. If we should enable the iommu of video decode, it should config the
video's ports. And if the video hardware work whether enable/disable iommu,
it should enable the clock of its larb's clock. And smi also help bandwidth
control for each local. So we add a special driver for smi and locate
it drivers/memory.
v4:
-use only one iommu domain here based on the Robin's DMA-v5.
-remove flush_pgtable.
-change writel to writel_relaxed.
-about Short-descriptor: move dma_map_single into io-pgtable-arm-short.
Improve the flow of free pgtable and add NO_XN+NO_PERMS quirk following
Will's suggestion.
-Change two sytle issues in dtsi according to Daniel's suggestion.
v3: http://lists.linuxfoundation.org/pipermail/iommu/2015-July/013632.html
-rebased onto v4.2-rc1
-improve iommu flow based on the Robin's DMA v3[4].
-change mtk iommu-cells from 1 to 2.
-about Short-descriptor: add split function; add self-test; add some other bits like nG,
XN according to the spec; add SUPERSECTION and MTK quirk; move io_pgtable_ops_to_pgtable
out from LPAE to the header file.
-about SMI: move from driver/soc/mediatek to driver/memory; change the clocks from
clk[2] to clk_apb and clk_smi; add pm.
-add iommu suspend/resume to backup/restore register.
v2: http://lists.linuxfoundation.org/pipermail/iommu/2015-May/013028.html
-add arm short descriptor support.
-seperate smi common from smi and change the clock-names according
to smi HW.
-delete the hardcode of the port-names in mt8173.
replace this with larb-portes-nr in dtsi.
-fix some coding style issues.
v1: http://lists.infradead.org/pipermail/linux-mediatek/2015-March/000058.html
-initial version.
[1]: http://lists.linuxfoundation.org/pipermail/iommu/2015-July/013900.html
[2]: http://lists.linuxfoundation.org/pipermail/iommu/2015-July/013876.html
[3]: http://lists.infradead.org/pipermail/linux-mediatek/2015-July/001800.html
[4]: http://lists.linuxfoundation.org/pipermail/iommu/2015-July/013597.html
Yong Wu (6):
dt-bindings: iommu: Add binding for mediatek IOMMU
dt-bindings: mediatek: Add smi dts binding
iommu: add ARM short descriptor page table allocator.
memory: mediatek: Add SMI driver
iommu/mediatek: Add mt8173 IOMMU driver
dts: mt8173: Add iommu/smi nodes for mt8173
.../devicetree/bindings/iommu/mediatek,iommu.txt | 61 ++
.../memory-controllers/mediatek,smi-larb.txt | 25 +
.../bindings/memory-controllers/mediatek,smi.txt | 24 +
arch/arm64/boot/dts/mediatek/mt8173.dtsi | 81 ++
drivers/iommu/Kconfig | 31 +
drivers/iommu/Makefile | 2 +
drivers/iommu/io-pgtable-arm-short.c | 811 +++++++++++++++++++++
drivers/iommu/io-pgtable-arm.c | 3 -
drivers/iommu/io-pgtable.c | 4 +
drivers/iommu/io-pgtable.h | 14 +
drivers/iommu/mtk_iommu.c | 714 ++++++++++++++++++
drivers/memory/Kconfig | 8 +
drivers/memory/Makefile | 1 +
drivers/memory/mtk-smi.c | 285 ++++++++
include/dt-bindings/memory/mt8173-larb-port.h | 105 +++
include/soc/mediatek/smi.h | 60 ++
16 files changed, 2226 insertions(+), 3 deletions(-)
create mode 100644 Documentation/devicetree/bindings/iommu/mediatek,iommu.txt
create mode 100644 Documentation/devicetree/bindings/memory-controllers/mediatek,smi-larb.txt
create mode 100644 Documentation/devicetree/bindings/memory-controllers/mediatek,smi.txt
create mode 100644 drivers/iommu/io-pgtable-arm-short.c
create mode 100644 drivers/iommu/mtk_iommu.c
create mode 100644 drivers/memory/mtk-smi.c
create mode 100644 include/dt-bindings/memory/mt8173-larb-port.h
create mode 100644 include/soc/mediatek/smi.h
--
1.8.1.1.dirty
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [next] | [standalone]
| From | Yong Wu <yong.wu@mediatek.com> |
|---|---|
| Date | 2015-08-03 12:30 +0200 |
| Subject | [PATCH v4 2/6] dt-bindings: mediatek: Add smi dts binding |
| Message-ID | <pTlah-fp-11@gated-at.bofh.it> |
| In reply to | #1198710 |
This patch add smi binding document.
Signed-off-by: Yong Wu <yong.wu@mediatek.com>
---
.../memory-controllers/mediatek,smi-larb.txt | 25 ++++++++++++++++++++++
.../bindings/memory-controllers/mediatek,smi.txt | 24 +++++++++++++++++++++
2 files changed, 49 insertions(+)
create mode 100644 Documentation/devicetree/bindings/memory-controllers/mediatek,smi-larb.txt
create mode 100644 Documentation/devicetree/bindings/memory-controllers/mediatek,smi.txt
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";
+ };
diff --git a/Documentation/devicetree/bindings/memory-controllers/mediatek,smi.txt b/Documentation/devicetree/bindings/memory-controllers/mediatek,smi.txt
new file mode 100644
index 0000000..f54e91c
--- /dev/null
+++ b/Documentation/devicetree/bindings/memory-controllers/mediatek,smi.txt
@@ -0,0 +1,24 @@
+SMI (Smart Multimedia Interface)
+
+The hardware block diagram please check bindings/iommu/mediatek,iommu.txt
+
+Required properties:
+- compatible : must be "mediatek,mt8173-smi"
+- 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 clock are the same.
+
+Example:
+ smi_common: smi@14022000 {
+ compatible = "mediatek,mt8173-smi";
+ 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";
+ };
--
1.8.1.1.dirty
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Yong Wu <yong.wu@mediatek.com> |
|---|---|
| Date | 2015-08-03 12:30 +0200 |
| Subject | [PATCH v4 3/6] iommu: add ARM short descriptor page table allocator. |
| Message-ID | <pTlai-fp-25@gated-at.bofh.it> |
| In reply to | #1198710 |
This patch is for ARM Short Descriptor Format.
Signed-off-by: Yong Wu <yong.wu@mediatek.com>
---
drivers/iommu/Kconfig | 18 +
drivers/iommu/Makefile | 1 +
drivers/iommu/io-pgtable-arm-short.c | 813 +++++++++++++++++++++++++++++++++++
drivers/iommu/io-pgtable-arm.c | 3 -
drivers/iommu/io-pgtable.c | 4 +
drivers/iommu/io-pgtable.h | 14 +
6 files changed, 850 insertions(+), 3 deletions(-)
create mode 100644 drivers/iommu/io-pgtable-arm-short.c
diff --git a/drivers/iommu/Kconfig b/drivers/iommu/Kconfig
index f1fb1d3..3abd066 100644
--- a/drivers/iommu/Kconfig
+++ b/drivers/iommu/Kconfig
@@ -39,6 +39,24 @@ config IOMMU_IO_PGTABLE_LPAE_SELFTEST
If unsure, say N here.
+config IOMMU_IO_PGTABLE_SHORT
+ bool "ARMv7/v8 Short Descriptor Format"
+ select IOMMU_IO_PGTABLE
+ depends on ARM || ARM64 || COMPILE_TEST
+ help
+ Enable support for the ARM Short-descriptor pagetable format.
+ This allocator supports 2 levels translation tables which supports
+ a memory map based on memory sections or pages.
+
+config IOMMU_IO_PGTABLE_SHORT_SELFTEST
+ bool "Short Descriptor selftests"
+ depends on IOMMU_IO_PGTABLE_SHORT
+ help
+ Enable self-tests for Short-descriptor page table allocator.
+ This performs a series of page-table consistency checks during boot.
+
+ If unsure, say N here.
+
endmenu
config IOMMU_IOVA
diff --git a/drivers/iommu/Makefile b/drivers/iommu/Makefile
index c6dcc51..06df3e6 100644
--- a/drivers/iommu/Makefile
+++ b/drivers/iommu/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_IOMMU_API) += iommu-traces.o
obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o
obj-$(CONFIG_IOMMU_IO_PGTABLE) += io-pgtable.o
obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o
+obj-$(CONFIG_IOMMU_IO_PGTABLE_SHORT) += io-pgtable-arm-short.o
obj-$(CONFIG_IOMMU_IOVA) += iova.o
obj-$(CONFIG_OF_IOMMU) += of_iommu.o
obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o msm_iommu_dev.o
diff --git a/drivers/iommu/io-pgtable-arm-short.c b/drivers/iommu/io-pgtable-arm-short.c
new file mode 100644
index 0000000..56f5480
--- /dev/null
+++ b/drivers/iommu/io-pgtable-arm-short.c
@@ -0,0 +1,813 @@
+/*
+ * 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.
+ */
+#define pr_fmt(fmt) "arm-short-desc io-pgtable: "fmt
+
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/iommu.h>
+#include <linux/errno.h>
+#include "io-pgtable.h"
+
+typedef u32 arm_short_iopte;
+
+struct arm_short_io_pgtable {
+ struct io_pgtable iop;
+ struct kmem_cache *pgtable_cached;
+ size_t pgd_size;
+ void *pgd;
+};
+
+#define io_pgtable_to_data(x) \
+ container_of((x), struct arm_short_io_pgtable, iop)
+
+#define io_pgtable_ops_to_data(x) \
+ io_pgtable_to_data(io_pgtable_ops_to_pgtable(x))
+
+#define io_pgtable_cfg_to_pgtable(x) \
+ container_of((x), struct io_pgtable, cfg)
+
+#define io_pgtable_cfg_to_data(x) \
+ io_pgtable_to_data(io_pgtable_cfg_to_pgtable(x))
+
+#define ARM_SHORT_PGDIR_SHIFT 20
+#define ARM_SHORT_PAGE_SHIFT 12
+#define ARM_SHORT_PTRS_PER_PTE \
+ (1 << (ARM_SHORT_PGDIR_SHIFT - ARM_SHORT_PAGE_SHIFT))
+#define ARM_SHORT_BYTES_PER_PTE \
+ (ARM_SHORT_PTRS_PER_PTE * sizeof(arm_short_iopte))
+
+/* level 1 pagetable */
+#define ARM_SHORT_PGD_TYPE_PGTABLE BIT(0)
+#define ARM_SHORT_PGD_TYPE_SECTION BIT(1)
+#define ARM_SHORT_PGD_B BIT(2)
+#define ARM_SHORT_PGD_C BIT(3)
+#define ARM_SHORT_PGD_PGTABLE_NS BIT(3)
+#define ARM_SHORT_PGD_SECTION_XN BIT(4)
+#define ARM_SHORT_PGD_IMPLE BIT(9)
+#define ARM_SHORT_PGD_RD_WR (3 << 10)
+#define ARM_SHORT_PGD_RDONLY BIT(15)
+#define ARM_SHORT_PGD_S BIT(16)
+#define ARM_SHORT_PGD_nG BIT(17)
+#define ARM_SHORT_PGD_SUPERSECTION BIT(18)
+#define ARM_SHORT_PGD_SECTION_NS BIT(19)
+
+#define ARM_SHORT_PGD_TYPE_SUPERSECTION \
+ (ARM_SHORT_PGD_TYPE_SECTION | ARM_SHORT_PGD_SUPERSECTION)
+#define ARM_SHORT_PGD_SECTION_TYPE_MSK \
+ (ARM_SHORT_PGD_TYPE_SECTION | ARM_SHORT_PGD_SUPERSECTION)
+#define ARM_SHORT_PGD_PGTABLE_TYPE_MSK \
+ (ARM_SHORT_PGD_TYPE_SECTION | ARM_SHORT_PGD_TYPE_PGTABLE)
+#define ARM_SHORT_PGD_TYPE_IS_PGTABLE(pgd) \
+ (((pgd) & ARM_SHORT_PGD_PGTABLE_TYPE_MSK) == ARM_SHORT_PGD_TYPE_PGTABLE)
+#define ARM_SHORT_PGD_TYPE_IS_SECTION(pgd) \
+ (((pgd) & ARM_SHORT_PGD_SECTION_TYPE_MSK) == ARM_SHORT_PGD_TYPE_SECTION)
+#define ARM_SHORT_PGD_TYPE_IS_SUPERSECTION(pgd) \
+ (((pgd) & ARM_SHORT_PGD_SECTION_TYPE_MSK) == \
+ ARM_SHORT_PGD_TYPE_SUPERSECTION)
+#define ARM_SHORT_PGD_PGTABLE_MSK 0xfffffc00
+#define ARM_SHORT_PGD_SECTION_MSK (~(SZ_1M - 1))
+#define ARM_SHORT_PGD_SUPERSECTION_MSK (~(SZ_16M - 1))
+
+/* level 2 pagetable */
+#define ARM_SHORT_PTE_TYPE_LARGE BIT(0)
+#define ARM_SHORT_PTE_SMALL_XN BIT(0)
+#define ARM_SHORT_PTE_TYPE_SMALL BIT(1)
+#define ARM_SHORT_PTE_B BIT(2)
+#define ARM_SHORT_PTE_C BIT(3)
+#define ARM_SHORT_PTE_RD_WR (3 << 4)
+#define ARM_SHORT_PTE_RDONLY BIT(9)
+#define ARM_SHORT_PTE_S BIT(10)
+#define ARM_SHORT_PTE_nG BIT(11)
+#define ARM_SHORT_PTE_LARGE_XN BIT(15)
+#define ARM_SHORT_PTE_LARGE_MSK (~(SZ_64K - 1))
+#define ARM_SHORT_PTE_SMALL_MSK (~(SZ_4K - 1))
+#define ARM_SHORT_PTE_TYPE_MSK \
+ (ARM_SHORT_PTE_TYPE_LARGE | ARM_SHORT_PTE_TYPE_SMALL)
+#define ARM_SHORT_PTE_TYPE_IS_SMALLPAGE(pte) \
+ (((pte) & ARM_SHORT_PTE_TYPE_SMALL) == ARM_SHORT_PTE_TYPE_SMALL)
+#define ARM_SHORT_PTE_TYPE_IS_LARGEPAGE(pte) \
+ (((pte) & ARM_SHORT_PTE_TYPE_MSK) == ARM_SHORT_PTE_TYPE_LARGE)
+
+#define ARM_SHORT_PGD_IDX(a) ((a) >> ARM_SHORT_PGDIR_SHIFT)
+#define ARM_SHORT_PTE_IDX(a) \
+ (((a) >> ARM_SHORT_PAGE_SHIFT) & (ARM_SHORT_PTRS_PER_PTE - 1))
+
+#define ARM_SHORT_GET_PGTABLE_VA(pgd) \
+ (phys_to_virt((unsigned long)pgd & ARM_SHORT_PGD_PGTABLE_MSK))
+
+#define ARM_SHORT_PTE_LARGE_GET_PROT(pte) \
+ (((pte) & (~ARM_SHORT_PTE_LARGE_MSK)) & ~ARM_SHORT_PTE_TYPE_MSK)
+
+#define ARM_SHORT_PGD_GET_PROT(pgd) \
+ (((pgd) & (~ARM_SHORT_PGD_SECTION_MSK)) & ~ARM_SHORT_PGD_SUPERSECTION)
+
+static bool selftest_running;
+
+static arm_short_iopte *
+arm_short_get_pte_in_pgd(arm_short_iopte pgd, unsigned int iova)
+{
+ arm_short_iopte *pte;
+
+ pte = ARM_SHORT_GET_PGTABLE_VA(pgd);
+ pte += ARM_SHORT_PTE_IDX(iova);
+ return pte;
+}
+
+static dma_addr_t
+__arm_short_dma_addr(struct device *dev, void *va)
+{
+ return phys_to_dma(dev, virt_to_phys(va));
+}
+
+static int
+__arm_short_set_pte(arm_short_iopte *ptep, arm_short_iopte pte,
+ unsigned int ptenr, struct io_pgtable_cfg *cfg)
+{
+ struct device *dev = cfg->iommu_dev;
+ int i;
+
+ for (i = 0; i < ptenr; i++) {
+ if (ptep[i] && pte) {
+ /* Someone else may have allocated for this pte */
+ WARN_ON(!selftest_running);
+ goto err_exist_pte;
+ }
+ ptep[i] = pte;
+ }
+
+ if (selftest_running)
+ return 0;
+
+ dma_sync_single_for_device(dev, __arm_short_dma_addr(dev, ptep),
+ sizeof(*ptep) * ptenr, DMA_TO_DEVICE);
+ return 0;
+
+err_exist_pte:
+ while (i--)
+ ptep[i] = 0;
+ return -EEXIST;
+}
+
+static void *
+__arm_short_alloc_pgtable(size_t size, gfp_t gfp, bool pgd,
+ struct io_pgtable_cfg *cfg)
+{
+ struct arm_short_io_pgtable *data;
+ struct device *dev = cfg->iommu_dev;
+ dma_addr_t dma;
+ void *va;
+
+ if (pgd) {/* lvl1 pagetable */
+ va = alloc_pages_exact(size, gfp);
+ } else { /* lvl2 pagetable */
+ data = io_pgtable_cfg_to_data(cfg);
+ va = kmem_cache_zalloc(data->pgtable_cached, gfp);
+ }
+
+ if (!va)
+ return NULL;
+
+ if (selftest_running)
+ return va;
+
+ dma = dma_map_single(dev, va, size, DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, dma))
+ goto out_free;
+
+ if (dma != __arm_short_dma_addr(dev, va))
+ goto out_unmap;
+
+ if (!pgd) {
+ kmemleak_ignore(va);
+ dma_sync_single_for_device(dev, __arm_short_dma_addr(dev, va),
+ size, DMA_TO_DEVICE);
+ }
+
+ return va;
+
+out_unmap:
+ dev_err_ratelimited(dev, "Cannot accommodate DMA translation for IOMMU page tables\n");
+ dma_unmap_single(dev, dma, size, DMA_TO_DEVICE);
+out_free:
+ if (pgd)
+ free_pages_exact(va, size);
+ else
+ kmem_cache_free(data->pgtable_cached, va);
+ return NULL;
+}
+
+static void
+__arm_short_free_pgtable(void *va, size_t size, bool pgd,
+ struct io_pgtable_cfg *cfg)
+{
+ struct arm_short_io_pgtable *data = io_pgtable_cfg_to_data(cfg);
+ struct device *dev = cfg->iommu_dev;
+
+ if (!selftest_running)
+ dma_unmap_single(dev, __arm_short_dma_addr(dev, va),
+ size, DMA_TO_DEVICE);
+
+ if (pgd)
+ free_pages_exact(va, size);
+ else
+ kmem_cache_free(data->pgtable_cached, va);
+}
+
+static arm_short_iopte
+__arm_short_pte_prot(struct arm_short_io_pgtable *data, int prot, bool large)
+{
+ arm_short_iopte pteprot;
+ int quirk = data->iop.cfg.quirks;
+
+ pteprot = ARM_SHORT_PTE_S | ARM_SHORT_PTE_nG;
+ pteprot |= large ? ARM_SHORT_PTE_TYPE_LARGE :
+ ARM_SHORT_PTE_TYPE_SMALL;
+ if (prot & IOMMU_CACHE)
+ pteprot |= ARM_SHORT_PTE_B | ARM_SHORT_PTE_C;
+ if (!(quirk & IO_PGTABLE_QUIRK_SHORT_NO_XN) && (prot & IOMMU_NOEXEC)) {
+ pteprot |= large ? ARM_SHORT_PTE_LARGE_XN :
+ ARM_SHORT_PTE_SMALL_XN;
+ }
+ if (!(quirk & IO_PGTABLE_QUIRK_SHORT_NO_PERMS)) {
+ pteprot |= ARM_SHORT_PTE_RD_WR;
+ if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ))
+ pteprot |= ARM_SHORT_PTE_RDONLY;
+ }
+ return pteprot;
+}
+
+static arm_short_iopte
+__arm_short_pgd_prot(struct arm_short_io_pgtable *data, int prot, bool super)
+{
+ arm_short_iopte pgdprot;
+ int quirk = data->iop.cfg.quirks;
+
+ pgdprot = ARM_SHORT_PGD_S | ARM_SHORT_PGD_nG;
+ pgdprot |= super ? ARM_SHORT_PGD_TYPE_SUPERSECTION :
+ ARM_SHORT_PGD_TYPE_SECTION;
+ if (prot & IOMMU_CACHE)
+ pgdprot |= ARM_SHORT_PGD_C | ARM_SHORT_PGD_B;
+ if (quirk & IO_PGTABLE_QUIRK_ARM_NS)
+ pgdprot |= ARM_SHORT_PGD_SECTION_NS;
+
+ if (!(quirk & IO_PGTABLE_QUIRK_SHORT_NO_XN) && (prot & IOMMU_NOEXEC))
+ pgdprot |= ARM_SHORT_PGD_SECTION_XN;
+
+ if (!(quirk & IO_PGTABLE_QUIRK_SHORT_NO_PERMS)) {
+ pgdprot |= ARM_SHORT_PGD_RD_WR;
+ if (!(prot & IOMMU_WRITE) && (prot & IOMMU_READ))
+ pgdprot |= ARM_SHORT_PGD_RDONLY;
+ }
+ return pgdprot;
+}
+
+static arm_short_iopte
+__arm_short_pte_prot_split(struct arm_short_io_pgtable *data,
+ arm_short_iopte pgdprot,
+ arm_short_iopte pteprot_large,
+ bool large)
+{
+ arm_short_iopte pteprot = 0;
+
+ pteprot = ARM_SHORT_PTE_S | ARM_SHORT_PTE_nG | ARM_SHORT_PTE_RD_WR;
+ pteprot |= large ? ARM_SHORT_PTE_TYPE_LARGE :
+ ARM_SHORT_PTE_TYPE_SMALL;
+
+ /* large page to small page pte prot. Only large page may split */
+ if (!pgdprot && !large) {
+ pteprot |= pteprot_large & ~ARM_SHORT_PTE_SMALL_MSK;
+ if (pteprot_large & ARM_SHORT_PTE_LARGE_XN)
+ pteprot |= ARM_SHORT_PTE_SMALL_XN;
+ }
+
+ /* section to pte prot */
+ if (pgdprot & ARM_SHORT_PGD_C)
+ pteprot |= ARM_SHORT_PTE_C;
+ if (pgdprot & ARM_SHORT_PGD_B)
+ pteprot |= ARM_SHORT_PTE_B;
+ if (pgdprot & ARM_SHORT_PGD_nG)
+ pteprot |= ARM_SHORT_PTE_nG;
+ if (pgdprot & ARM_SHORT_PGD_SECTION_XN)
+ pteprot |= large ? ARM_SHORT_PTE_LARGE_XN :
+ ARM_SHORT_PTE_SMALL_XN;
+ if (pgdprot & ARM_SHORT_PGD_RD_WR)
+ pteprot |= ARM_SHORT_PTE_RD_WR;
+ if (pgdprot & ARM_SHORT_PGD_RDONLY)
+ pteprot |= ARM_SHORT_PTE_RDONLY;
+
+ return pteprot;
+}
+
+static arm_short_iopte
+__arm_short_pgtable_prot(struct arm_short_io_pgtable *data)
+{
+ arm_short_iopte pgdprot = 0;
+
+ pgdprot = ARM_SHORT_PGD_TYPE_PGTABLE;
+ if (data->iop.cfg.quirks & IO_PGTABLE_QUIRK_ARM_NS)
+ pgdprot |= ARM_SHORT_PGD_PGTABLE_NS;
+ return pgdprot;
+}
+
+static int
+_arm_short_map(struct arm_short_io_pgtable *data,
+ unsigned int iova, phys_addr_t paddr,
+ arm_short_iopte pgdprot, arm_short_iopte pteprot,
+ bool large)
+{
+ struct io_pgtable_cfg *cfg = &data->iop.cfg;
+ arm_short_iopte *pgd = data->pgd, *pte;
+ void *pte_new = NULL;
+ int ret;
+
+ pgd += ARM_SHORT_PGD_IDX(iova);
+
+ if (!pteprot) { /* section or supersection */
+ pte = pgd;
+ pteprot = pgdprot;
+ } else { /* page or largepage */
+ if (!(*pgd)) {
+ pte_new = __arm_short_alloc_pgtable(
+ ARM_SHORT_BYTES_PER_PTE,
+ GFP_ATOMIC, false, cfg);
+ if (unlikely(!pte_new))
+ return -ENOMEM;
+
+ pgdprot |= virt_to_phys(pte_new);
+ __arm_short_set_pte(pgd, pgdprot, 1, cfg);
+ }
+ pte = arm_short_get_pte_in_pgd(*pgd, iova);
+ }
+
+ pteprot |= (arm_short_iopte)paddr;
+ ret = __arm_short_set_pte(pte, pteprot, large ? 16 : 1, cfg);
+ if (ret && pte_new)
+ __arm_short_free_pgtable(pte_new, ARM_SHORT_BYTES_PER_PTE,
+ false, cfg);
+ return ret;
+}
+
+static int arm_short_map(struct io_pgtable_ops *ops, unsigned long iova,
+ phys_addr_t paddr, size_t size, int prot)
+{
+ struct arm_short_io_pgtable *data = io_pgtable_ops_to_data(ops);
+ arm_short_iopte pgdprot = 0, pteprot = 0;
+ bool large;
+
+ /* If no access, then nothing to do */
+ if (!(prot & (IOMMU_READ | IOMMU_WRITE)))
+ return 0;
+
+ if (WARN_ON((iova | paddr) & (size - 1)))
+ return -EINVAL;
+
+ switch (size) {
+ case SZ_4K:
+ case SZ_64K:
+ large = (size == SZ_64K) ? true : false;
+ pteprot = __arm_short_pte_prot(data, prot, large);
+ pgdprot = __arm_short_pgtable_prot(data);
+ break;
+
+ case SZ_1M:
+ case SZ_16M:
+ large = (size == SZ_16M) ? true : false;
+ pgdprot = __arm_short_pgd_prot(data, prot, large);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return _arm_short_map(data, iova, paddr, pgdprot, pteprot, large);
+}
+
+static phys_addr_t arm_short_iova_to_phys(struct io_pgtable_ops *ops,
+ unsigned long iova)
+{
+ struct arm_short_io_pgtable *data = io_pgtable_ops_to_data(ops);
+ arm_short_iopte *pte, *pgd = data->pgd;
+ phys_addr_t pa = 0;
+
+ pgd += ARM_SHORT_PGD_IDX(iova);
+
+ if (ARM_SHORT_PGD_TYPE_IS_PGTABLE(*pgd)) {
+ pte = arm_short_get_pte_in_pgd(*pgd, iova);
+
+ if (ARM_SHORT_PTE_TYPE_IS_LARGEPAGE(*pte)) {
+ pa = (*pte) & ARM_SHORT_PTE_LARGE_MSK;
+ pa |= iova & ~ARM_SHORT_PTE_LARGE_MSK;
+ } else if (ARM_SHORT_PTE_TYPE_IS_SMALLPAGE(*pte)) {
+ pa = (*pte) & ARM_SHORT_PTE_SMALL_MSK;
+ pa |= iova & ~ARM_SHORT_PTE_SMALL_MSK;
+ }
+ } else if (ARM_SHORT_PGD_TYPE_IS_SECTION(*pgd)) {
+ pa = (*pgd) & ARM_SHORT_PGD_SECTION_MSK;
+ pa |= iova & ~ARM_SHORT_PGD_SECTION_MSK;
+ } else if (ARM_SHORT_PGD_TYPE_IS_SUPERSECTION(*pgd)) {
+ pa = (*pgd) & ARM_SHORT_PGD_SUPERSECTION_MSK;
+ pa |= iova & ~ARM_SHORT_PGD_SUPERSECTION_MSK;
+ }
+
+ return pa;
+}
+
+static bool _arm_short_whether_free_pgtable(arm_short_iopte *pgd)
+{
+ arm_short_iopte *pte;
+ int i;
+
+ pte = ARM_SHORT_GET_PGTABLE_VA(*pgd);
+ for (i = 0; i < ARM_SHORT_PTRS_PER_PTE; i++) {
+ if (pte[i] != 0)
+ return false;
+ }
+
+ return true;
+}
+
+static int
+arm_short_split_blk_unmap(struct io_pgtable_ops *ops, unsigned int iova,
+ phys_addr_t paddr, size_t size,
+ arm_short_iopte pgdprotup, arm_short_iopte pteprotup,
+ size_t blk_size)
+{
+ struct arm_short_io_pgtable *data = io_pgtable_ops_to_data(ops);
+ const struct iommu_gather_ops *tlb = data->iop.cfg.tlb;
+ struct io_pgtable_cfg *cfg = &data->iop.cfg;
+ unsigned long *pgbitmap = &cfg->pgsize_bitmap;
+ unsigned int blk_base, blk_start, blk_end, i;
+ arm_short_iopte pgdprot, pteprot;
+ phys_addr_t blk_paddr;
+ size_t mapsize = 0, nextmapsize;
+ int ret;
+
+ /* find the nearest mapsize */
+ for (i = find_first_bit(pgbitmap, BITS_PER_LONG);
+ i < BITS_PER_LONG && ((1 << i) < blk_size) &&
+ IS_ALIGNED(size, 1 << i);
+ i = find_next_bit(pgbitmap, BITS_PER_LONG, i + 1))
+ mapsize = 1 << i;
+
+ if (WARN_ON(!mapsize))
+ return 0; /* Bytes unmapped */
+ nextmapsize = 1 << i;
+
+ blk_base = iova & ~(blk_size - 1);
+ blk_start = blk_base;
+ blk_end = blk_start + blk_size;
+ blk_paddr = paddr;
+
+ for (; blk_start < blk_end;
+ blk_start += mapsize, blk_paddr += mapsize) {
+ /* Unmap! */
+ if (blk_start == iova)
+ continue;
+
+ /* Try to upper map */
+ if (blk_base != blk_start &&
+ IS_ALIGNED(blk_start | blk_paddr, nextmapsize) &&
+ mapsize != nextmapsize) {
+ mapsize = nextmapsize;
+ i = find_next_bit(pgbitmap, BITS_PER_LONG, i + 1);
+ if (i < BITS_PER_LONG)
+ nextmapsize = 1 << i;
+ }
+
+ if (mapsize == SZ_1M) {
+ pgdprot = pgdprotup;
+ pgdprot |= __arm_short_pgd_prot(data, 0, false);
+ pteprot = 0;
+ } else { /* small or large page */
+ pgdprot = (blk_size == SZ_64K) ? 0 : pgdprotup;
+ pteprot = __arm_short_pte_prot_split(
+ data, pgdprot, pteprotup,
+ mapsize == SZ_64K);
+ pgdprot = __arm_short_pgtable_prot(data);
+ }
+
+ ret = _arm_short_map(data, blk_start, blk_paddr, pgdprot,
+ pteprot, mapsize == SZ_64K);
+ if (ret < 0) {
+ /* Free the table we allocated */
+ arm_short_iopte *pgd = data->pgd, *pte;
+
+ pgd += ARM_SHORT_PGD_IDX(blk_base);
+ if (*pgd) {
+ pte = ARM_SHORT_GET_PGTABLE_VA(*pgd);
+ __arm_short_set_pte(pgd, 0, 1, cfg);
+ tlb->tlb_add_flush(blk_base, blk_size, true,
+ data->iop.cookie);
+ tlb->tlb_sync(data->iop.cookie);
+ __arm_short_free_pgtable(
+ pte, ARM_SHORT_BYTES_PER_PTE,
+ false, cfg);
+ }
+ return 0;/* Bytes unmapped */
+ }
+ }
+
+ tlb->tlb_add_flush(blk_base, blk_size, true, data->iop.cookie);
+ tlb->tlb_sync(data->iop.cookie);
+ return size;
+}
+
+static int arm_short_unmap(struct io_pgtable_ops *ops,
+ unsigned long iova,
+ size_t size)
+{
+ struct arm_short_io_pgtable *data = io_pgtable_ops_to_data(ops);
+ struct io_pgtable_cfg *cfg = &data->iop.cfg;
+ arm_short_iopte *pgd, *pte = NULL;
+ arm_short_iopte curpgd, curpte = 0;
+ phys_addr_t paddr;
+ unsigned int iova_base, blk_size = 0;
+ void *cookie = data->iop.cookie;
+ bool pgtablefree = false;
+
+ pgd = (arm_short_iopte *)data->pgd + ARM_SHORT_PGD_IDX(iova);
+
+ /* Get block size */
+ if (ARM_SHORT_PGD_TYPE_IS_PGTABLE(*pgd)) {
+ pte = arm_short_get_pte_in_pgd(*pgd, iova);
+
+ if (ARM_SHORT_PTE_TYPE_IS_SMALLPAGE(*pte))
+ blk_size = SZ_4K;
+ else if (ARM_SHORT_PTE_TYPE_IS_LARGEPAGE(*pte))
+ blk_size = SZ_64K;
+ else
+ WARN_ON(1);
+ } else if (ARM_SHORT_PGD_TYPE_IS_SECTION(*pgd)) {
+ blk_size = SZ_1M;
+ } else if (ARM_SHORT_PGD_TYPE_IS_SUPERSECTION(*pgd)) {
+ blk_size = SZ_16M;
+ } else {
+ WARN_ON(1);
+ }
+
+ iova_base = iova & ~(blk_size - 1);
+ pgd = (arm_short_iopte *)data->pgd + ARM_SHORT_PGD_IDX(iova_base);
+ paddr = arm_short_iova_to_phys(ops, iova_base);
+ curpgd = *pgd;
+
+ if (blk_size == SZ_4K || blk_size == SZ_64K) {
+ pte = arm_short_get_pte_in_pgd(*pgd, iova_base);
+ curpte = *pte;
+ __arm_short_set_pte(pte, 0, blk_size / SZ_4K, cfg);
+
+ pgtablefree = _arm_short_whether_free_pgtable(pgd);
+ if (pgtablefree)
+ __arm_short_set_pte(pgd, 0, 1, cfg);
+ } else if (blk_size == SZ_1M || blk_size == SZ_16M) {
+ __arm_short_set_pte(pgd, 0, blk_size / SZ_1M, cfg);
+ }
+
+ cfg->tlb->tlb_add_flush(iova_base, blk_size, true, cookie);
+ cfg->tlb->tlb_sync(cookie);
+
+ if (pgtablefree)/* Free pgtable after tlb-flush */
+ __arm_short_free_pgtable(ARM_SHORT_GET_PGTABLE_VA(curpgd),
+ ARM_SHORT_BYTES_PER_PTE, false, cfg);
+
+ if (blk_size > size) { /* Split the block */
+ return arm_short_split_blk_unmap(
+ ops, iova, paddr, size,
+ ARM_SHORT_PGD_GET_PROT(curpgd),
+ ARM_SHORT_PTE_LARGE_GET_PROT(curpte),
+ blk_size);
+ } else if (blk_size < size) {
+ /* Unmap the block while remap partial again after split */
+ return blk_size +
+ arm_short_unmap(ops, iova + blk_size, size - blk_size);
+ }
+
+ return size;
+}
+
+static struct io_pgtable *
+arm_short_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
+{
+ struct arm_short_io_pgtable *data;
+
+ if (cfg->ias > 32 || cfg->oas > 32)
+ return NULL;
+
+ cfg->pgsize_bitmap &=
+ (cfg->quirks & IO_PGTABLE_QUIRK_SHORT_SUPERSECTION) ?
+ (SZ_4K | SZ_64K | SZ_1M | SZ_16M) : (SZ_4K | SZ_64K | SZ_1M);
+
+ data = kzalloc(sizeof(*data), GFP_KERNEL);
+ if (!data)
+ return NULL;
+
+ data->pgd_size = SZ_16K;
+ data->pgd = __arm_short_alloc_pgtable(
+ data->pgd_size,
+ GFP_KERNEL | __GFP_ZERO | __GFP_DMA,
+ true, cfg);
+ if (!data->pgd)
+ goto out_free_data;
+ wmb();/* Ensure the empty pgd is visible before any actual TTBR write */
+
+ data->pgtable_cached = kmem_cache_create(
+ "io-pgtable-arm-short",
+ ARM_SHORT_BYTES_PER_PTE,
+ ARM_SHORT_BYTES_PER_PTE,
+ 0, NULL);
+ if (!data->pgtable_cached)
+ goto out_free_pgd;
+
+ /* TTBRs */
+ cfg->arm_short_cfg.ttbr[0] = virt_to_phys(data->pgd);
+ cfg->arm_short_cfg.ttbr[1] = 0;
+ cfg->arm_short_cfg.tcr = 0;
+ cfg->arm_short_cfg.nmrr = 0;
+ cfg->arm_short_cfg.prrr = 0;
+
+ data->iop.ops = (struct io_pgtable_ops) {
+ .map = arm_short_map,
+ .unmap = arm_short_unmap,
+ .iova_to_phys = arm_short_iova_to_phys,
+ };
+
+ return &data->iop;
+
+out_free_pgd:
+ __arm_short_free_pgtable(data->pgd, data->pgd_size, true, cfg);
+out_free_data:
+ kfree(data);
+ return NULL;
+}
+
+static void arm_short_free_pgtable(struct io_pgtable *iop)
+{
+ struct arm_short_io_pgtable *data = io_pgtable_to_data(iop);
+
+ kmem_cache_destroy(data->pgtable_cached);
+ __arm_short_free_pgtable(data->pgd, data->pgd_size,
+ true, &data->iop.cfg);
+ kfree(data);
+}
+
+struct io_pgtable_init_fns io_pgtable_arm_short_init_fns = {
+ .alloc = arm_short_alloc_pgtable,
+ .free = arm_short_free_pgtable,
+};
+
+#ifdef CONFIG_IOMMU_IO_PGTABLE_SHORT_SELFTEST
+
+static struct io_pgtable_cfg *cfg_cookie;
+
+static void dummy_tlb_flush_all(void *cookie)
+{
+ WARN_ON(cookie != cfg_cookie);
+}
+
+static void dummy_tlb_add_flush(unsigned long iova, size_t size, bool leaf,
+ void *cookie)
+{
+ WARN_ON(cookie != cfg_cookie);
+ WARN_ON(!(size & cfg_cookie->pgsize_bitmap));
+}
+
+static void dummy_tlb_sync(void *cookie)
+{
+ WARN_ON(cookie != cfg_cookie);
+}
+
+static struct iommu_gather_ops dummy_tlb_ops = {
+ .tlb_flush_all = dummy_tlb_flush_all,
+ .tlb_add_flush = dummy_tlb_add_flush,
+ .tlb_sync = dummy_tlb_sync,
+};
+
+#define __FAIL(ops) ({ \
+ WARN(1, "selftest: test failed\n"); \
+ selftest_running = false; \
+ -EFAULT; \
+})
+
+static int __init arm_short_do_selftests(void)
+{
+ struct io_pgtable_ops *ops;
+ struct io_pgtable_cfg cfg = {
+ .tlb = &dummy_tlb_ops,
+ .oas = 32,
+ .ias = 32,
+ .quirks = IO_PGTABLE_QUIRK_ARM_NS |
+ IO_PGTABLE_QUIRK_SHORT_SUPERSECTION,
+ .pgsize_bitmap = SZ_4K | SZ_64K | SZ_1M | SZ_16M,
+ };
+ unsigned int iova, size, iova_start;
+ unsigned int i, loopnr = 0;
+
+ selftest_running = true;
+
+ cfg_cookie = &cfg;
+
+ ops = alloc_io_pgtable_ops(ARM_SHORT_DESC, &cfg, &cfg);
+ if (!ops) {
+ pr_err("Failed to alloc short desc io pgtable\n");
+ return -EINVAL;
+ }
+
+ /*
+ * Initial sanity checks.
+ * Empty page tables shouldn't provide any translations.
+ */
+ if (ops->iova_to_phys(ops, 42))
+ return __FAIL(ops);
+
+ if (ops->iova_to_phys(ops, SZ_1G + 42))
+ return __FAIL(ops);
+
+ if (ops->iova_to_phys(ops, SZ_2G + 42))
+ return __FAIL(ops);
+
+ /*
+ * Distinct mappings of different granule sizes.
+ */
+ iova = 0;
+ i = find_first_bit(&cfg.pgsize_bitmap, BITS_PER_LONG);
+ while (i != BITS_PER_LONG) {
+ size = 1UL << i;
+ if (ops->map(ops, iova, iova, size, IOMMU_READ |
+ IOMMU_WRITE |
+ IOMMU_NOEXEC |
+ IOMMU_CACHE))
+ return __FAIL(ops);
+
+ /* Overlapping mappings */
+ if (!ops->map(ops, iova, iova + size, size,
+ IOMMU_READ | IOMMU_NOEXEC))
+ return __FAIL(ops);
+
+ if (ops->iova_to_phys(ops, iova + 42) != (iova + 42))
+ return __FAIL(ops);
+
+ iova += SZ_16M;
+ i++;
+ i = find_next_bit(&cfg.pgsize_bitmap, BITS_PER_LONG, i);
+ loopnr++;
+ }
+
+ /* Partial unmap */
+ i = 1;
+ size = 1UL << __ffs(cfg.pgsize_bitmap);
+ while (i < loopnr) {
+ iova_start = i * SZ_16M;
+ if (ops->unmap(ops, iova_start + size, size) != size)
+ return __FAIL(ops);
+
+ /* Remap of partial unmap */
+ if (ops->map(ops, iova_start + size, size, size, IOMMU_READ))
+ return __FAIL(ops);
+
+ if (ops->iova_to_phys(ops, iova_start + size + 42)
+ != (size + 42))
+ return __FAIL(ops);
+ i++;
+ }
+
+ /* Full unmap */
+ iova = 0;
+ i = find_first_bit(&cfg.pgsize_bitmap, BITS_PER_LONG);
+ while (i != BITS_PER_LONG) {
+ size = 1UL << i;
+
+ if (ops->unmap(ops, iova, size) != size)
+ return __FAIL(ops);
+
+ if (ops->iova_to_phys(ops, iova + 42))
+ return __FAIL(ops);
+
+ /* Remap full block */
+ if (ops->map(ops, iova, iova, size, IOMMU_WRITE))
+ return __FAIL(ops);
+
+ if (ops->iova_to_phys(ops, iova + 42) != (iova + 42))
+ return __FAIL(ops);
+
+ iova += SZ_16M;
+ i++;
+ i = find_next_bit(&cfg.pgsize_bitmap, BITS_PER_LONG, i);
+ }
+
+ free_io_pgtable_ops(ops);
+
+ selftest_running = false;
+ return 0;
+}
+
+subsys_initcall(arm_short_do_selftests);
+#endif
diff --git a/drivers/iommu/io-pgtable-arm.c b/drivers/iommu/io-pgtable-arm.c
index e4bc2b2..9978eca 100644
--- a/drivers/iommu/io-pgtable-arm.c
+++ b/drivers/iommu/io-pgtable-arm.c
@@ -38,9 +38,6 @@
#define io_pgtable_to_data(x) \
container_of((x), struct arm_lpae_io_pgtable, iop)
-#define io_pgtable_ops_to_pgtable(x) \
- container_of((x), struct io_pgtable, ops)
-
#define io_pgtable_ops_to_data(x) \
io_pgtable_to_data(io_pgtable_ops_to_pgtable(x))
diff --git a/drivers/iommu/io-pgtable.c b/drivers/iommu/io-pgtable.c
index 6436fe2..14a9b3a 100644
--- a/drivers/iommu/io-pgtable.c
+++ b/drivers/iommu/io-pgtable.c
@@ -28,6 +28,7 @@ extern struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s1_init_fns;
extern struct io_pgtable_init_fns io_pgtable_arm_32_lpae_s2_init_fns;
extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s1_init_fns;
extern struct io_pgtable_init_fns io_pgtable_arm_64_lpae_s2_init_fns;
+extern struct io_pgtable_init_fns io_pgtable_arm_short_init_fns;
static const struct io_pgtable_init_fns *
io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] =
@@ -38,6 +39,9 @@ io_pgtable_init_table[IO_PGTABLE_NUM_FMTS] =
[ARM_64_LPAE_S1] = &io_pgtable_arm_64_lpae_s1_init_fns,
[ARM_64_LPAE_S2] = &io_pgtable_arm_64_lpae_s2_init_fns,
#endif
+#ifdef CONFIG_IOMMU_IO_PGTABLE_SHORT
+ [ARM_SHORT_DESC] = &io_pgtable_arm_short_init_fns,
+#endif
};
struct io_pgtable_ops *alloc_io_pgtable_ops(enum io_pgtable_fmt fmt,
diff --git a/drivers/iommu/io-pgtable.h b/drivers/iommu/io-pgtable.h
index 68c63d9..0f45e60 100644
--- a/drivers/iommu/io-pgtable.h
+++ b/drivers/iommu/io-pgtable.h
@@ -9,6 +9,7 @@ enum io_pgtable_fmt {
ARM_32_LPAE_S2,
ARM_64_LPAE_S1,
ARM_64_LPAE_S2,
+ ARM_SHORT_DESC,
IO_PGTABLE_NUM_FMTS,
};
@@ -45,6 +46,9 @@ struct iommu_gather_ops {
*/
struct io_pgtable_cfg {
#define IO_PGTABLE_QUIRK_ARM_NS (1 << 0) /* Set NS bit in PTEs */
+ #define IO_PGTABLE_QUIRK_SHORT_SUPERSECTION BIT(1)
+ #define IO_PGTABLE_QUIRK_SHORT_NO_XN BIT(2) /* No XN bit */
+ #define IO_PGTABLE_QUIRK_SHORT_NO_PERMS BIT(3) /* No AP bit */
int quirks;
unsigned long pgsize_bitmap;
unsigned int ias;
@@ -64,6 +68,13 @@ struct io_pgtable_cfg {
u64 vttbr;
u64 vtcr;
} arm_lpae_s2_cfg;
+
+ struct {
+ u32 ttbr[2];
+ u32 tcr;
+ u32 nmrr;
+ u32 prrr;
+ } arm_short_cfg;
};
};
@@ -130,6 +141,9 @@ struct io_pgtable {
struct io_pgtable_ops ops;
};
+#define io_pgtable_ops_to_pgtable(x) \
+ container_of((x), struct io_pgtable, ops)
+
/**
* struct io_pgtable_init_fns - Alloc/free a set of page tables for a
* particular format.
--
1.8.1.1.dirty
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Yong Wu <yong.wu@mediatek.com> |
|---|---|
| Date | 2015-08-03 12:30 +0200 |
| Subject | [PATCH v4 6/6] dts: mt8173: Add iommu/smi nodes for mt8173 |
| Message-ID | <pTlai-fp-27@gated-at.bofh.it> |
| In reply to | #1198710 |
This patch add the iommu/larbs nodes for mt8173
Signed-off-by: Yong Wu <yong.wu@mediatek.com>
---
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 6c3f047..a92956d 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"
@@ -265,6 +266,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,larb = <&larb0 &larb1 &larb2
+ &larb3 &larb4 &larb5>;
+ #iommu-cells = <2>;
+ };
+
apmixedsys: clock-controller@10209000 {
compatible = "mediatek,mt8173-apmixedsys";
reg = <0 0x10209000 0 0x1000>;
@@ -501,29 +513,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";
+ 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 linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Yong Wu <yong.wu@mediatek.com> |
|---|---|
| Date | 2015-08-03 12:30 +0200 |
| Subject | [PATCH v4 1/6] dt-bindings: iommu: Add binding for mediatek IOMMU |
| Message-ID | <pTlai-fp-29@gated-at.bofh.it> |
| In reply to | #1198710 |
This patch add mediatek iommu dts binding document.
Signed-off-by: Yong Wu <yong.wu@mediatek.com>
---
.../devicetree/bindings/iommu/mediatek,iommu.txt | 61 ++++++++++++
include/dt-bindings/memory/mt8173-larb-port.h | 105 +++++++++++++++++++++
2 files changed, 166 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..691739a
--- /dev/null
+++ b/Documentation/devicetree/bindings/iommu/mediatek,iommu.txt
@@ -0,0 +1,61 @@
+* Mediatek IOMMU Architecture Implementation
+
+ Mediatek Socs may contain a implementation of Multimedia Memory
+Management Unit(M4U),which use ARM Short-descriptor translation table
+to achieve address translation.
+
+ The IOMMU Hardware Block Diagram, please check below:
+
+ EMI (External Memory Interface)
+ |
+ m4u (Multimedia Memory Management Unit)
+ |
+ smi (Smart Multimedia Interface)
+ |
+ +---------------+-------
+ | |
+ | |
+ vdec larb disp larb ... SoCs have different local arbiter(larb).
+ | |
+ | |
+ +----+----+ +-----+-----+
+ | | | | | | ...
+ | | | | | | ...
+ | | | | | | ...
+ MC PP VLD OVL0 RDMA0 WDMA0 ... There are different ports in each larb.
+
+ 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 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 some ports like MC, PP, VLD in the
+video decode local arbiter, all the 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,larb : 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 2. There are 2 cells needed to enable/disable iommu.
+ The first one is local arbiter index(larbid), and the other is port
+ index(portid) within local arbiter. Specifies the larbid and portid as
+ defined in dt-binding/memory/mt8173-larb-port.h.
+
+Example:
+ iommu: mmsys_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,larb = <&larb0 &larb1 &larb2 &larb3 &larb4 &larb5>;
+ #iommu-cells = <2>;
+ };
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..7517087
--- /dev/null
+++ b/include/dt-bindings/memory/mt8173-larb-port.h
@@ -0,0 +1,105 @@
+/*
+ * 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 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 0
+#define M4U_PORT_DISP_RDMA0 1
+#define M4U_PORT_DISP_WDMA0 2
+#define M4U_PORT_DISP_OD_R 3
+#define M4U_PORT_DISP_OD_W 4
+#define M4U_PORT_MDP_RDMA0 5
+#define M4U_PORT_MDP_WDMA 6
+#define M4U_PORT_MDP_WROT0 7
+
+/* larb1 */
+#define M4U_PORT_HW_VDEC_MC_EXT 0
+#define M4U_PORT_HW_VDEC_PP_EXT 1
+#define M4U_PORT_HW_VDEC_UFO_EXT 2
+#define M4U_PORT_HW_VDEC_VLD_EXT 3
+#define M4U_PORT_HW_VDEC_VLD2_EXT 4
+#define M4U_PORT_HW_VDEC_AVC_MV_EXT 5
+#define M4U_PORT_HW_VDEC_PRED_RD_EXT 6
+#define M4U_PORT_HW_VDEC_PRED_WR_EXT 7
+#define M4U_PORT_HW_VDEC_PPWRAP_EXT 8
+#define M4U_PORT_HW_VDEC_TILE 9
+
+/* larb2 */
+#define M4U_PORT_IMGO 0
+#define M4U_PORT_RRZO 1
+#define M4U_PORT_AAO 2
+#define M4U_PORT_LCSO 3
+#define M4U_PORT_ESFKO 4
+#define M4U_PORT_IMGO_D 5
+#define M4U_PORT_LSCI 6
+#define M4U_PORT_LSCI_D 7
+#define M4U_PORT_BPCI 8
+#define M4U_PORT_BPCI_D 9
+#define M4U_PORT_UFDI 10
+#define M4U_PORT_IMGI 11
+#define M4U_PORT_IMG2O 12
+#define M4U_PORT_IMG3O 13
+#define M4U_PORT_VIPI 14
+#define M4U_PORT_VIP2I 15
+#define M4U_PORT_VIP3I 16
+#define M4U_PORT_LCEI 17
+#define M4U_PORT_RB 18
+#define M4U_PORT_RP 19
+#define M4U_PORT_WR 20
+
+/* larb3 */
+#define M4U_PORT_VENC_RCPU 0
+#define M4U_PORT_VENC_REC 1
+#define M4U_PORT_VENC_BSDMA 2
+#define M4U_PORT_VENC_SV_COMV 3
+#define M4U_PORT_VENC_RD_COMV 4
+#define M4U_PORT_JPGENC_RDMA 5
+#define M4U_PORT_JPGENC_BSDMA 6
+#define M4U_PORT_JPGDEC_WDMA 7
+#define M4U_PORT_JPGDEC_BSDMA 8
+#define M4U_PORT_VENC_CUR_LUMA 9
+#define M4U_PORT_VENC_CUR_CHROMA 10
+#define M4U_PORT_VENC_REF_LUMA 11
+#define M4U_PORT_VENC_REF_CHROMA 12
+#define M4U_PORT_VENC_NBM_RDMA 13
+#define M4U_PORT_VENC_NBM_WDMA 14
+
+/* larb4 */
+#define M4U_PORT_DISP_OVL1 0
+#define M4U_PORT_DISP_RDMA1 1
+#define M4U_PORT_DISP_RDMA2 2
+#define M4U_PORT_DISP_WDMA1 3
+#define M4U_PORT_MDP_RDMA1 4
+#define M4U_PORT_MDP_WROT1 5
+
+/* larb5 */
+#define M4U_PORT_VENC_RCPU_SET2 0
+#define M4U_PORT_VENC_REC_FRM_SET2 1
+#define M4U_PORT_VENC_REF_LUMA_SET2 2
+#define M4U_PORT_VENC_REC_CHROMA_SET2 3
+#define M4U_PORT_VENC_BSDMA_SET2 4
+#define M4U_PORT_VENC_CUR_LUMA_SET2 5
+#define M4U_PORT_VENC_CUR_CHROMA_SET2 6
+#define M4U_PORT_VENC_RD_COMA_SET2 7
+#define M4U_PORT_VENC_SV_COMA_SET2 8
+
+#endif
--
1.8.1.1.dirty
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [next] | [standalone]
| From | Yong Wu <yong.wu@mediatek.com> |
|---|---|
| Date | 2015-08-03 12:30 +0200 |
| Subject | [PATCH v4 4/6] memory: mediatek: Add SMI driver |
| Message-ID | <pTlai-fp-37@gated-at.bofh.it> |
| In reply to | #1198710 |
This patch add SMI(Smart Multimedia Interface) driver. This driver
is responsible to enable/disable iommu and control the clocks of each
local arbiter
Signed-off-by: Yong Wu <yong.wu@mediatek.com>
---
drivers/memory/Kconfig | 8 ++
drivers/memory/Makefile | 1 +
drivers/memory/mtk-smi.c | 285 +++++++++++++++++++++++++++++++++++++++++++++
include/soc/mediatek/smi.h | 60 ++++++++++
4 files changed, 354 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 8406c668..c0e1607 100644
--- a/drivers/memory/Kconfig
+++ b/drivers/memory/Kconfig
@@ -100,6 +100,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 clock for each
+ local arbiter.
+
source "drivers/memory/tegra/Kconfig"
endif
diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile
index b670441..f854e40 100644
--- a/drivers/memory/Makefile
+++ b/drivers/memory/Makefile
@@ -14,5 +14,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..e62cceb
--- /dev/null
+++ b/drivers/memory/mtk-smi.c
@@ -0,0 +1,285 @@
+/*
+ * 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/io.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.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 clk *clk_apb;
+ struct clk *clk_smi;
+};
+
+struct mtk_smi_larb {
+ void __iomem *base;
+ struct clk *clk_apb;
+ struct clk *clk_smi;
+ struct device *smi;
+};
+
+struct mtk_larb_mmu {
+ u32 mmu;
+};
+
+static int mtk_smi_common_get(struct device *smidev)
+{
+ struct mtk_smi_common *smipriv = dev_get_drvdata(smidev);
+ int ret;
+
+ ret = pm_runtime_get_sync(smidev);
+ if (ret < 0)
+ return ret;
+
+ ret = clk_prepare_enable(smipriv->clk_apb);
+ if (ret) {
+ dev_err(smidev, "Failed to enable the apb clock\n");
+ goto err_put_pm;
+ }
+ ret = clk_prepare_enable(smipriv->clk_smi);
+ if (ret) {
+ dev_err(smidev, "Failed to enable the smi clock\n");
+ goto err_disable_apb;
+ }
+ return ret;
+
+err_disable_apb:
+ clk_disable_unprepare(smipriv->clk_apb);
+err_put_pm:
+ pm_runtime_put(smidev);
+ return ret;
+}
+
+static void mtk_smi_common_put(struct device *smidev)
+{
+ struct mtk_smi_common *smipriv = dev_get_drvdata(smidev);
+
+ clk_disable_unprepare(smipriv->clk_smi);
+ clk_disable_unprepare(smipriv->clk_apb);
+ pm_runtime_put(smidev);
+}
+
+int mtk_smi_larb_get(struct device *larbdev)
+{
+ struct mtk_smi_larb *larbpriv = dev_get_drvdata(larbdev);
+ struct mtk_larb_mmu *mmucfg = larbdev->archdata.iommu;
+ int ret;
+
+ ret = mtk_smi_common_get(larbpriv->smi);
+ if (ret)
+ return ret;
+
+ ret = pm_runtime_get_sync(larbdev);
+ if (ret < 0)
+ goto err_put_smicommon;
+
+ ret = clk_prepare_enable(larbpriv->clk_apb);
+ if (ret) {
+ dev_err(larbdev, "Failed to enable the apb clock\n");
+ goto err_put_pm;
+ }
+
+ ret = clk_prepare_enable(larbpriv->clk_smi);
+ if (ret) {
+ dev_err(larbdev, "Failed to enable the smi clock\n");
+ goto err_disable_apb;
+ }
+
+ /* config iommu */
+ writel_relaxed(mmucfg->mmu, larbpriv->base + SMI_LARB_MMU_EN);
+
+ return ret;
+
+err_disable_apb:
+ clk_disable_unprepare(larbpriv->clk_apb);
+err_put_pm:
+ pm_runtime_put(larbdev);
+err_put_smicommon:
+ mtk_smi_common_put(larbpriv->smi);
+ return ret;
+}
+
+void mtk_smi_larb_put(struct device *larbdev)
+{
+ struct mtk_smi_larb *larbpriv = dev_get_drvdata(larbdev);
+
+ clk_disable_unprepare(larbpriv->clk_smi);
+ clk_disable_unprepare(larbpriv->clk_apb);
+ pm_runtime_put(larbdev);
+ mtk_smi_common_put(larbpriv->smi);
+}
+
+int mtk_smi_config_port(struct device *larbdev, unsigned int larbportid,
+ bool enable)
+{
+ struct mtk_larb_mmu *mmucfg = larbdev->archdata.iommu;
+
+ if (!mmucfg) {
+ mmucfg = kzalloc(sizeof(*mmucfg), GFP_KERNEL);
+ if (!mmucfg)
+ return -ENOMEM;
+ larbdev->archdata.iommu = mmucfg;
+ }
+
+ dev_dbg(larbdev, "%s iommu port id %d\n",
+ enable ? "enable" : "disable", larbportid);
+
+ if (enable)
+ mmucfg->mmu |= F_SMI_MMU_EN(larbportid);
+ else
+ mmucfg->mmu &= ~F_SMI_MMU_EN(larbportid);
+
+ return 0;
+}
+
+static int mtk_smi_larb_probe(struct platform_device *pdev)
+{
+ struct mtk_smi_larb *larbpriv;
+ struct resource *res;
+ struct device *dev = &pdev->dev;
+ struct device_node *smi_node;
+ struct platform_device *smi_pdev;
+
+ larbpriv = devm_kzalloc(dev, sizeof(*larbpriv), GFP_KERNEL);
+ if (!larbpriv)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ larbpriv->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(larbpriv->base))
+ return PTR_ERR(larbpriv->base);
+
+ larbpriv->clk_apb = devm_clk_get(dev, "apb");
+ if (IS_ERR(larbpriv->clk_apb))
+ return PTR_ERR(larbpriv->clk_apb);
+
+ larbpriv->clk_smi = devm_clk_get(dev, "smi");
+ if (IS_ERR(larbpriv->clk_smi))
+ return PTR_ERR(larbpriv->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) {
+ larbpriv->smi = &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, larbpriv);
+ return 0;
+}
+
+static int mtk_smi_larb_remove(struct platform_device *pdev)
+{
+ struct mtk_larb_mmu *mmucfg = pdev->dev.archdata.iommu;
+
+ kfree(mmucfg);
+ pm_runtime_disable(&pdev->dev);
+ 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_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct mtk_smi_common *smipriv;
+ int ret;
+
+ smipriv = devm_kzalloc(dev, sizeof(*smipriv), GFP_KERNEL);
+ if (!smipriv)
+ return -ENOMEM;
+
+ smipriv->clk_apb = devm_clk_get(dev, "apb");
+ if (IS_ERR(smipriv->clk_apb))
+ return PTR_ERR(smipriv->clk_apb);
+
+ smipriv->clk_smi = devm_clk_get(dev, "smi");
+ if (IS_ERR(smipriv->clk_smi))
+ return PTR_ERR(smipriv->clk_smi);
+
+ pm_runtime_enable(dev);
+ dev_set_drvdata(dev, smipriv);
+ return ret;
+}
+
+static int mtk_smi_remove(struct platform_device *pdev)
+{
+ pm_runtime_disable(&pdev->dev);
+ return 0;
+}
+
+static const struct of_device_id mtk_smi_of_ids[] = {
+ { .compatible = "mediatek,mt8173-smi",},
+ {}
+};
+
+static struct platform_driver mtk_smi_driver = {
+ .probe = mtk_smi_probe,
+ .remove = mtk_smi_remove,
+ .driver = {
+ .name = "mtk-smi",
+ .of_match_table = mtk_smi_of_ids,
+ }
+};
+
+static int __init mtk_smi_init(void)
+{
+ int ret;
+
+ ret = platform_driver_register(&mtk_smi_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_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..189d575
--- /dev/null
+++ b/include/soc/mediatek/smi.h
@@ -0,0 +1,60 @@
+/*
+ * 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 is only for iommu.
+ *
+ * Returns 0 if successfully, others if failed.
+ */
+int mtk_smi_config_port(struct device *larbdev, unsigned int larbportid,
+ bool enable);
+/*
+ * The two function below config iommu and enable/disable the clock
+ * for the larb.
+ *
+ * mtk_smi_larb_get must be called before the multimedia HW work.
+ * mtk_smi_larb_put must be called after HW done.
+ * Both should be called in non-atomic context.
+ *
+ * Returns 0 if successfully, others if failed.
+ */
+int mtk_smi_larb_get(struct device *larbdev);
+void mtk_smi_larb_put(struct device *larbdev);
+
+#else
+
+static int
+mtk_smi_config_port(struct device *larbdev, unsigned int larbportid,
+ bool enable)
+{
+ return 0;
+}
+
+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
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
[toc] | [prev] | [standalone]
Back to top | Article view | linux.kernel
csiph-web