From f580fd3f9d78cf0425ab98950796c578d8a82167 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Mon, 26 Jun 2017 17:33:12 +0200 Subject: [PATCH 1/6] dt-bindings: misc: Add Tegra186 MISC registers bindings The MISC register block found on Tegra186 SoCs contains registers that can be used to identify a given chip and various strapping options. Signed-off-by: Thierry Reding --- .../bindings/misc/nvidia,tegra186-misc.txt | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 Documentation/devicetree/bindings/misc/nvidia,tegra186-misc.txt diff --git a/Documentation/devicetree/bindings/misc/nvidia,tegra186-misc.txt b/Documentation/devicetree/bindings/misc/nvidia,tegra186-misc.txt new file mode 100644 index 000000000000..892ba4384abc --- /dev/null +++ b/Documentation/devicetree/bindings/misc/nvidia,tegra186-misc.txt @@ -0,0 +1,12 @@ +NVIDIA Tegra186 MISC register block + +The MISC register block found on Tegra186 SoCs contains registers that can be +used to identify a given chip and various strapping options. + +Required properties: +- compatible: Must be: + - Tegra186: "nvidia,tegra186-misc" +- reg: Should contain 2 entries: The first entry gives the physical address + and length of the register region which contains revision and debug + features. The second entry specifies the physical address and length + of the register region indicating the strapping options. From 029ab5eaf091ce5eaa1f3017f66fd1d10f431d61 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 13 Dec 2017 12:53:43 +0100 Subject: [PATCH 2/6] dt-bindings: memory: Add Tegra186 support As opposed to earlier incarnations, the memory controller on Tegra186 no longer implements an SMMU. Instead the SMMU is a regular ARM SMMU and in a separate IP block. However, the memory controller programs the SMMU stream IDs for each of the memory clients. Add a header file with definitions for each of these stream IDs and mark the #iommu-cells property as required on Tegra30 to Tegra210 in the device tree bindings. Signed-off-by: Thierry Reding --- .../memory-controllers/nvidia,tegra30-mc.txt | 2 + include/dt-bindings/memory/tegra186-mc.h | 111 ++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 include/dt-bindings/memory/tegra186-mc.h diff --git a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-mc.txt b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-mc.txt index 8dbe47013c2b..14968b048cd3 100644 --- a/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-mc.txt +++ b/Documentation/devicetree/bindings/memory-controllers/nvidia,tegra30-mc.txt @@ -12,6 +12,8 @@ Required properties: - clock-names: Must include the following entries: - mc: the module's clock input - interrupts: The interrupt outputs from the controller. + +Required properties for Tegra30, Tegra114, Tegra124, Tegra132 and Tegra210: - #iommu-cells: Should be 1. The single cell of the IOMMU specifier defines the SWGROUP of the master. diff --git a/include/dt-bindings/memory/tegra186-mc.h b/include/dt-bindings/memory/tegra186-mc.h new file mode 100644 index 000000000000..64813536aec9 --- /dev/null +++ b/include/dt-bindings/memory/tegra186-mc.h @@ -0,0 +1,111 @@ +#ifndef DT_BINDINGS_MEMORY_TEGRA186_MC_H +#define DT_BINDINGS_MEMORY_TEGRA186_MC_H + +/* special clients */ +#define TEGRA186_SID_INVALID 0x00 +#define TEGRA186_SID_PASSTHROUGH 0x7f + +/* host1x clients */ +#define TEGRA186_SID_HOST1X 0x01 +#define TEGRA186_SID_CSI 0x02 +#define TEGRA186_SID_VIC 0x03 +#define TEGRA186_SID_VI 0x04 +#define TEGRA186_SID_ISP 0x05 +#define TEGRA186_SID_NVDEC 0x06 +#define TEGRA186_SID_NVENC 0x07 +#define TEGRA186_SID_NVJPG 0x08 +#define TEGRA186_SID_NVDISPLAY 0x09 +#define TEGRA186_SID_TSEC 0x0a +#define TEGRA186_SID_TSECB 0x0b +#define TEGRA186_SID_SE 0x0c +#define TEGRA186_SID_SE1 0x0d +#define TEGRA186_SID_SE2 0x0e +#define TEGRA186_SID_SE3 0x0f + +/* GPU clients */ +#define TEGRA186_SID_GPU 0x10 + +/* other SoC clients */ +#define TEGRA186_SID_AFI 0x11 +#define TEGRA186_SID_HDA 0x12 +#define TEGRA186_SID_ETR 0x13 +#define TEGRA186_SID_EQOS 0x14 +#define TEGRA186_SID_UFSHC 0x15 +#define TEGRA186_SID_AON 0x16 +#define TEGRA186_SID_SDMMC4 0x17 +#define TEGRA186_SID_SDMMC3 0x18 +#define TEGRA186_SID_SDMMC2 0x19 +#define TEGRA186_SID_SDMMC1 0x1a +#define TEGRA186_SID_XUSB_HOST 0x1b +#define TEGRA186_SID_XUSB_DEV 0x1c +#define TEGRA186_SID_SATA 0x1d +#define TEGRA186_SID_APE 0x1e +#define TEGRA186_SID_SCE 0x1f + +/* GPC DMA clients */ +#define TEGRA186_SID_GPCDMA_0 0x20 +#define TEGRA186_SID_GPCDMA_1 0x21 +#define TEGRA186_SID_GPCDMA_2 0x22 +#define TEGRA186_SID_GPCDMA_3 0x23 +#define TEGRA186_SID_GPCDMA_4 0x24 +#define TEGRA186_SID_GPCDMA_5 0x25 +#define TEGRA186_SID_GPCDMA_6 0x26 +#define TEGRA186_SID_GPCDMA_7 0x27 + +/* APE DMA clients */ +#define TEGRA186_SID_APE_1 0x28 +#define TEGRA186_SID_APE_2 0x29 + +/* camera RTCPU */ +#define TEGRA186_SID_RCE 0x2a + +/* camera RTCPU on host1x address space */ +#define TEGRA186_SID_RCE_1X 0x2b + +/* APE DMA clients */ +#define TEGRA186_SID_APE_3 0x2c + +/* camera RTCPU running on APE */ +#define TEGRA186_SID_APE_CAM 0x2d +#define TEGRA186_SID_APE_CAM_1X 0x2e + +/* + * The BPMP has its SID value hardcoded in the firmware. Changing it requires + * considerable effort. + */ +#define TEGRA186_SID_BPMP 0x32 + +/* for SMMU tests */ +#define TEGRA186_SID_SMMU_TEST 0x33 + +/* host1x virtualization channels */ +#define TEGRA186_SID_HOST1X_CTX0 0x38 +#define TEGRA186_SID_HOST1X_CTX1 0x39 +#define TEGRA186_SID_HOST1X_CTX2 0x3a +#define TEGRA186_SID_HOST1X_CTX3 0x3b +#define TEGRA186_SID_HOST1X_CTX4 0x3c +#define TEGRA186_SID_HOST1X_CTX5 0x3d +#define TEGRA186_SID_HOST1X_CTX6 0x3e +#define TEGRA186_SID_HOST1X_CTX7 0x3f + +/* host1x command buffers */ +#define TEGRA186_SID_HOST1X_VM0 0x40 +#define TEGRA186_SID_HOST1X_VM1 0x41 +#define TEGRA186_SID_HOST1X_VM2 0x42 +#define TEGRA186_SID_HOST1X_VM3 0x43 +#define TEGRA186_SID_HOST1X_VM4 0x44 +#define TEGRA186_SID_HOST1X_VM5 0x45 +#define TEGRA186_SID_HOST1X_VM6 0x46 +#define TEGRA186_SID_HOST1X_VM7 0x47 + +/* SE data buffers */ +#define TEGRA186_SID_SE_VM0 0x48 +#define TEGRA186_SID_SE_VM1 0x49 +#define TEGRA186_SID_SE_VM2 0x4a +#define TEGRA186_SID_SE_VM3 0x4b +#define TEGRA186_SID_SE_VM4 0x4c +#define TEGRA186_SID_SE_VM5 0x4d +#define TEGRA186_SID_SE_VM6 0x4e +#define TEGRA186_SID_SE_VM7 0x4f + +#endif From 02b0cc52c0c3c89641276cb1e7abddd35e036923 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Wed, 13 Dec 2017 12:58:21 +0100 Subject: [PATCH 3/6] memory: tegra: Add Tegra186 support The memory controller found on Tegra186 is different in some respects to its predecessors. Most notably it no longer implements an SMMU, but does assign ARM SMMU stream IDs for each memory client instead. Provide a driver that programs these registers so that memory clients can translate addresses via the ARM SMMU. Signed-off-by: Thierry Reding --- drivers/memory/tegra/Makefile | 1 + drivers/memory/tegra/tegra186.c | 600 ++++++++++++++++++++++++++++++++ 2 files changed, 601 insertions(+) create mode 100644 drivers/memory/tegra/tegra186.c diff --git a/drivers/memory/tegra/Makefile b/drivers/memory/tegra/Makefile index b44e8627a5e0..ce87a9470034 100644 --- a/drivers/memory/tegra/Makefile +++ b/drivers/memory/tegra/Makefile @@ -10,3 +10,4 @@ tegra-mc-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210.o obj-$(CONFIG_TEGRA_MC) += tegra-mc.o obj-$(CONFIG_TEGRA124_EMC) += tegra124-emc.o +obj-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186.o diff --git a/drivers/memory/tegra/tegra186.c b/drivers/memory/tegra/tegra186.c new file mode 100644 index 000000000000..7254fb596979 --- /dev/null +++ b/drivers/memory/tegra/tegra186.c @@ -0,0 +1,600 @@ +/* + * Copyright (C) 2017 NVIDIA CORPORATION. All rights reserved. + * + * 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. + */ + +#include +#include +#include + +#include + +struct tegra_mc { + struct device *dev; + void __iomem *regs; +}; + +struct tegra_mc_client { + const char *name; + unsigned int sid; + struct { + unsigned int override; + unsigned int security; + } regs; +}; + +static const struct tegra_mc_client tegra186_mc_clients[] = { + { + .name = "ptcr", + .sid = TEGRA186_SID_PASSTHROUGH, + .regs = { + .override = 0x000, + .security = 0x004, + }, + }, { + .name = "afir", + .sid = TEGRA186_SID_AFI, + .regs = { + .override = 0x070, + .security = 0x074, + }, + }, { + .name = "hdar", + .sid = TEGRA186_SID_HDA, + .regs = { + .override = 0x0a8, + .security = 0x0ac, + }, + }, { + .name = "host1xdmar", + .sid = TEGRA186_SID_HOST1X, + .regs = { + .override = 0x0b0, + .security = 0x0b4, + }, + }, { + .name = "nvencsrd", + .sid = TEGRA186_SID_NVENC, + .regs = { + .override = 0x0e0, + .security = 0x0e4, + }, + }, { + .name = "satar", + .sid = TEGRA186_SID_SATA, + .regs = { + .override = 0x0f8, + .security = 0x0fc, + }, + }, { + .name = "mpcorer", + .sid = TEGRA186_SID_PASSTHROUGH, + .regs = { + .override = 0x138, + .security = 0x13c, + }, + }, { + .name = "nvencswr", + .sid = TEGRA186_SID_NVENC, + .regs = { + .override = 0x158, + .security = 0x15c, + }, + }, { + .name = "afiw", + .sid = TEGRA186_SID_AFI, + .regs = { + .override = 0x188, + .security = 0x18c, + }, + }, { + .name = "hdaw", + .sid = TEGRA186_SID_HDA, + .regs = { + .override = 0x1a8, + .security = 0x1ac, + }, + }, { + .name = "mpcorew", + .sid = TEGRA186_SID_PASSTHROUGH, + .regs = { + .override = 0x1c8, + .security = 0x1cc, + }, + }, { + .name = "sataw", + .sid = TEGRA186_SID_SATA, + .regs = { + .override = 0x1e8, + .security = 0x1ec, + }, + }, { + .name = "ispra", + .sid = TEGRA186_SID_ISP, + .regs = { + .override = 0x220, + .security = 0x224, + }, + }, { + .name = "ispwa", + .sid = TEGRA186_SID_ISP, + .regs = { + .override = 0x230, + .security = 0x234, + }, + }, { + .name = "ispwb", + .sid = TEGRA186_SID_ISP, + .regs = { + .override = 0x238, + .security = 0x23c, + }, + }, { + .name = "xusb_hostr", + .sid = TEGRA186_SID_XUSB_HOST, + .regs = { + .override = 0x250, + .security = 0x254, + }, + }, { + .name = "xusb_hostw", + .sid = TEGRA186_SID_XUSB_HOST, + .regs = { + .override = 0x258, + .security = 0x25c, + }, + }, { + .name = "xusb_devr", + .sid = TEGRA186_SID_XUSB_DEV, + .regs = { + .override = 0x260, + .security = 0x264, + }, + }, { + .name = "xusb_devw", + .sid = TEGRA186_SID_XUSB_DEV, + .regs = { + .override = 0x268, + .security = 0x26c, + }, + }, { + .name = "tsecsrd", + .sid = TEGRA186_SID_TSEC, + .regs = { + .override = 0x2a0, + .security = 0x2a4, + }, + }, { + .name = "tsecswr", + .sid = TEGRA186_SID_TSEC, + .regs = { + .override = 0x2a8, + .security = 0x2ac, + }, + }, { + .name = "gpusrd", + .sid = TEGRA186_SID_GPU, + .regs = { + .override = 0x2c0, + .security = 0x2c4, + }, + }, { + .name = "gpuswr", + .sid = TEGRA186_SID_GPU, + .regs = { + .override = 0x2c8, + .security = 0x2cc, + }, + }, { + .name = "sdmmcra", + .sid = TEGRA186_SID_SDMMC1, + .regs = { + .override = 0x300, + .security = 0x304, + }, + }, { + .name = "sdmmcraa", + .sid = TEGRA186_SID_SDMMC2, + .regs = { + .override = 0x308, + .security = 0x30c, + }, + }, { + .name = "sdmmcr", + .sid = TEGRA186_SID_SDMMC3, + .regs = { + .override = 0x310, + .security = 0x314, + }, + }, { + .name = "sdmmcrab", + .sid = TEGRA186_SID_SDMMC4, + .regs = { + .override = 0x318, + .security = 0x31c, + }, + }, { + .name = "sdmmcwa", + .sid = TEGRA186_SID_SDMMC1, + .regs = { + .override = 0x320, + .security = 0x324, + }, + }, { + .name = "sdmmcwaa", + .sid = TEGRA186_SID_SDMMC2, + .regs = { + .override = 0x328, + .security = 0x32c, + }, + }, { + .name = "sdmmcw", + .sid = TEGRA186_SID_SDMMC3, + .regs = { + .override = 0x330, + .security = 0x334, + }, + }, { + .name = "sdmmcwab", + .sid = TEGRA186_SID_SDMMC4, + .regs = { + .override = 0x338, + .security = 0x33c, + }, + }, { + .name = "vicsrd", + .sid = TEGRA186_SID_VIC, + .regs = { + .override = 0x360, + .security = 0x364, + }, + }, { + .name = "vicswr", + .sid = TEGRA186_SID_VIC, + .regs = { + .override = 0x368, + .security = 0x36c, + }, + }, { + .name = "viw", + .sid = TEGRA186_SID_VI, + .regs = { + .override = 0x390, + .security = 0x394, + }, + }, { + .name = "nvdecsrd", + .sid = TEGRA186_SID_NVDEC, + .regs = { + .override = 0x3c0, + .security = 0x3c4, + }, + }, { + .name = "nvdecswr", + .sid = TEGRA186_SID_NVDEC, + .regs = { + .override = 0x3c8, + .security = 0x3cc, + }, + }, { + .name = "aper", + .sid = TEGRA186_SID_APE, + .regs = { + .override = 0x3d0, + .security = 0x3d4, + }, + }, { + .name = "apew", + .sid = TEGRA186_SID_APE, + .regs = { + .override = 0x3d8, + .security = 0x3dc, + }, + }, { + .name = "nvjpgsrd", + .sid = TEGRA186_SID_NVJPG, + .regs = { + .override = 0x3f0, + .security = 0x3f4, + }, + }, { + .name = "nvjpgswr", + .sid = TEGRA186_SID_NVJPG, + .regs = { + .override = 0x3f8, + .security = 0x3fc, + }, + }, { + .name = "sesrd", + .sid = TEGRA186_SID_SE, + .regs = { + .override = 0x400, + .security = 0x404, + }, + }, { + .name = "seswr", + .sid = TEGRA186_SID_SE, + .regs = { + .override = 0x408, + .security = 0x40c, + }, + }, { + .name = "etrr", + .sid = TEGRA186_SID_ETR, + .regs = { + .override = 0x420, + .security = 0x424, + }, + }, { + .name = "etrw", + .sid = TEGRA186_SID_ETR, + .regs = { + .override = 0x428, + .security = 0x42c, + }, + }, { + .name = "tsecsrdb", + .sid = TEGRA186_SID_TSECB, + .regs = { + .override = 0x430, + .security = 0x434, + }, + }, { + .name = "tsecswrb", + .sid = TEGRA186_SID_TSECB, + .regs = { + .override = 0x438, + .security = 0x43c, + }, + }, { + .name = "gpusrd2", + .sid = TEGRA186_SID_GPU, + .regs = { + .override = 0x440, + .security = 0x444, + }, + }, { + .name = "gpuswr2", + .sid = TEGRA186_SID_GPU, + .regs = { + .override = 0x448, + .security = 0x44c, + }, + }, { + .name = "axisr", + .sid = TEGRA186_SID_GPCDMA_0, + .regs = { + .override = 0x460, + .security = 0x464, + }, + }, { + .name = "axisw", + .sid = TEGRA186_SID_GPCDMA_0, + .regs = { + .override = 0x468, + .security = 0x46c, + }, + }, { + .name = "eqosr", + .sid = TEGRA186_SID_EQOS, + .regs = { + .override = 0x470, + .security = 0x474, + }, + }, { + .name = "eqosw", + .sid = TEGRA186_SID_EQOS, + .regs = { + .override = 0x478, + .security = 0x47c, + }, + }, { + .name = "ufshcr", + .sid = TEGRA186_SID_UFSHC, + .regs = { + .override = 0x480, + .security = 0x484, + }, + }, { + .name = "ufshcw", + .sid = TEGRA186_SID_UFSHC, + .regs = { + .override = 0x488, + .security = 0x48c, + }, + }, { + .name = "nvdisplayr", + .sid = TEGRA186_SID_NVDISPLAY, + .regs = { + .override = 0x490, + .security = 0x494, + }, + }, { + .name = "bpmpr", + .sid = TEGRA186_SID_BPMP, + .regs = { + .override = 0x498, + .security = 0x49c, + }, + }, { + .name = "bpmpw", + .sid = TEGRA186_SID_BPMP, + .regs = { + .override = 0x4a0, + .security = 0x4a4, + }, + }, { + .name = "bpmpdmar", + .sid = TEGRA186_SID_BPMP, + .regs = { + .override = 0x4a8, + .security = 0x4ac, + }, + }, { + .name = "bpmpdmaw", + .sid = TEGRA186_SID_BPMP, + .regs = { + .override = 0x4b0, + .security = 0x4b4, + }, + }, { + .name = "aonr", + .sid = TEGRA186_SID_AON, + .regs = { + .override = 0x4b8, + .security = 0x4bc, + }, + }, { + .name = "aonw", + .sid = TEGRA186_SID_AON, + .regs = { + .override = 0x4c0, + .security = 0x4c4, + }, + }, { + .name = "aondmar", + .sid = TEGRA186_SID_AON, + .regs = { + .override = 0x4c8, + .security = 0x4cc, + }, + }, { + .name = "aondmaw", + .sid = TEGRA186_SID_AON, + .regs = { + .override = 0x4d0, + .security = 0x4d4, + }, + }, { + .name = "scer", + .sid = TEGRA186_SID_SCE, + .regs = { + .override = 0x4d8, + .security = 0x4dc, + }, + }, { + .name = "scew", + .sid = TEGRA186_SID_SCE, + .regs = { + .override = 0x4e0, + .security = 0x4e4, + }, + }, { + .name = "scedmar", + .sid = TEGRA186_SID_SCE, + .regs = { + .override = 0x4e8, + .security = 0x4ec, + }, + }, { + .name = "scedmaw", + .sid = TEGRA186_SID_SCE, + .regs = { + .override = 0x4f0, + .security = 0x4f4, + }, + }, { + .name = "apedmar", + .sid = TEGRA186_SID_APE, + .regs = { + .override = 0x4f8, + .security = 0x4fc, + }, + }, { + .name = "apedmaw", + .sid = TEGRA186_SID_APE, + .regs = { + .override = 0x500, + .security = 0x504, + }, + }, { + .name = "nvdisplayr1", + .sid = TEGRA186_SID_NVDISPLAY, + .regs = { + .override = 0x508, + .security = 0x50c, + }, + }, { + .name = "vicsrd1", + .sid = TEGRA186_SID_VIC, + .regs = { + .override = 0x510, + .security = 0x514, + }, + }, { + .name = "nvdecsrd1", + .sid = TEGRA186_SID_NVDEC, + .regs = { + .override = 0x518, + .security = 0x51c, + }, + }, +}; + +static int tegra186_mc_probe(struct platform_device *pdev) +{ + struct resource *res; + struct tegra_mc *mc; + unsigned int i; + int err = 0; + + mc = devm_kzalloc(&pdev->dev, sizeof(*mc), GFP_KERNEL); + if (!mc) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + mc->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(mc->regs)) + return PTR_ERR(mc->regs); + + mc->dev = &pdev->dev; + + for (i = 0; i < ARRAY_SIZE(tegra186_mc_clients); i++) { + const struct tegra_mc_client *client = &tegra186_mc_clients[i]; + u32 override, security; + + override = readl(mc->regs + client->regs.override); + security = readl(mc->regs + client->regs.security); + + dev_dbg(&pdev->dev, "client %s: override: %x security: %x\n", + client->name, override, security); + + dev_dbg(&pdev->dev, "setting SID %u for %s\n", client->sid, + client->name); + writel(client->sid, mc->regs + client->regs.override); + + override = readl(mc->regs + client->regs.override); + security = readl(mc->regs + client->regs.security); + + dev_dbg(&pdev->dev, "client %s: override: %x security: %x\n", + client->name, override, security); + } + + platform_set_drvdata(pdev, mc); + + return err; +} + +static const struct of_device_id tegra186_mc_of_match[] = { + { .compatible = "nvidia,tegra186-mc", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, tegra186_mc_of_match); + +static struct platform_driver tegra186_mc_driver = { + .driver = { + .name = "tegra186-mc", + .of_match_table = tegra186_mc_of_match, + .suppress_bind_attrs = true, + }, + .prevent_deferred_probe = true, + .probe = tegra186_mc_probe, +}; +module_platform_driver(tegra186_mc_driver); + +MODULE_AUTHOR("Thierry Reding "); +MODULE_DESCRIPTION("NVIDIA Tegra186 Memory Controller driver"); +MODULE_LICENSE("GPL v2"); From 2a8102dfe0da7dbb61794e6b85dc7ac9271e5fc8 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 12 Oct 2017 16:29:19 +0200 Subject: [PATCH 4/6] memory: tegra: Create SMMU display groups Create SMMU display groups for Tegra30, Tegra114, Tegra124 and Tegra210. This allows the display controllers on these devices to share the same IOMMU domain using the standard IOMMU group mechanism. Signed-off-by: Thierry Reding --- drivers/memory/tegra/tegra114.c | 15 +++++++++++++++ drivers/memory/tegra/tegra124.c | 17 +++++++++++++++++ drivers/memory/tegra/tegra210.c | 15 +++++++++++++++ drivers/memory/tegra/tegra30.c | 15 +++++++++++++++ include/soc/tegra/mc.h | 9 +++++++++ 5 files changed, 71 insertions(+) diff --git a/drivers/memory/tegra/tegra114.c b/drivers/memory/tegra/tegra114.c index ba8fff3d66a6..b20e6e3e208e 100644 --- a/drivers/memory/tegra/tegra114.c +++ b/drivers/memory/tegra/tegra114.c @@ -912,11 +912,26 @@ static const struct tegra_smmu_swgroup tegra114_swgroups[] = { { .name = "tsec", .swgroup = TEGRA_SWGROUP_TSEC, .reg = 0x294 }, }; +static const unsigned int tegra114_group_display[] = { + TEGRA_SWGROUP_DC, + TEGRA_SWGROUP_DCB, +}; + +static const struct tegra_smmu_group_soc tegra114_groups[] = { + { + .name = "display", + .swgroups = tegra114_group_display, + .num_swgroups = ARRAY_SIZE(tegra114_group_display), + }, +}; + static const struct tegra_smmu_soc tegra114_smmu_soc = { .clients = tegra114_mc_clients, .num_clients = ARRAY_SIZE(tegra114_mc_clients), .swgroups = tegra114_swgroups, .num_swgroups = ARRAY_SIZE(tegra114_swgroups), + .groups = tegra114_groups, + .num_groups = ARRAY_SIZE(tegra114_groups), .supports_round_robin_arbitration = false, .supports_request_limit = false, .num_tlb_lines = 32, diff --git a/drivers/memory/tegra/tegra124.c b/drivers/memory/tegra/tegra124.c index 5a58e440f4a7..8b6360eabb8a 100644 --- a/drivers/memory/tegra/tegra124.c +++ b/drivers/memory/tegra/tegra124.c @@ -999,12 +999,27 @@ static const struct tegra_smmu_swgroup tegra124_swgroups[] = { { .name = "vi", .swgroup = TEGRA_SWGROUP_VI, .reg = 0x280 }, }; +static const unsigned int tegra124_group_display[] = { + TEGRA_SWGROUP_DC, + TEGRA_SWGROUP_DCB, +}; + +static const struct tegra_smmu_group_soc tegra124_groups[] = { + { + .name = "display", + .swgroups = tegra124_group_display, + .num_swgroups = ARRAY_SIZE(tegra124_group_display), + }, +}; + #ifdef CONFIG_ARCH_TEGRA_124_SOC static const struct tegra_smmu_soc tegra124_smmu_soc = { .clients = tegra124_mc_clients, .num_clients = ARRAY_SIZE(tegra124_mc_clients), .swgroups = tegra124_swgroups, .num_swgroups = ARRAY_SIZE(tegra124_swgroups), + .groups = tegra124_groups, + .num_groups = ARRAY_SIZE(tegra124_groups), .supports_round_robin_arbitration = true, .supports_request_limit = true, .num_tlb_lines = 32, @@ -1029,6 +1044,8 @@ static const struct tegra_smmu_soc tegra132_smmu_soc = { .num_clients = ARRAY_SIZE(tegra124_mc_clients), .swgroups = tegra124_swgroups, .num_swgroups = ARRAY_SIZE(tegra124_swgroups), + .groups = tegra124_groups, + .num_groups = ARRAY_SIZE(tegra124_groups), .supports_round_robin_arbitration = true, .supports_request_limit = true, .num_tlb_lines = 32, diff --git a/drivers/memory/tegra/tegra210.c b/drivers/memory/tegra/tegra210.c index 5e144abe4c18..d398bcd3fc57 100644 --- a/drivers/memory/tegra/tegra210.c +++ b/drivers/memory/tegra/tegra210.c @@ -1059,11 +1059,26 @@ static const struct tegra_smmu_swgroup tegra210_swgroups[] = { { .name = "tsecb", .swgroup = TEGRA_SWGROUP_TSECB, .reg = 0xad4 }, }; +static const unsigned int tegra210_group_display[] = { + TEGRA_SWGROUP_DC, + TEGRA_SWGROUP_DCB, +}; + +static const struct tegra_smmu_group_soc tegra210_groups[] = { + { + .name = "display", + .swgroups = tegra210_group_display, + .num_swgroups = ARRAY_SIZE(tegra210_group_display), + }, +}; + static const struct tegra_smmu_soc tegra210_smmu_soc = { .clients = tegra210_mc_clients, .num_clients = ARRAY_SIZE(tegra210_mc_clients), .swgroups = tegra210_swgroups, .num_swgroups = ARRAY_SIZE(tegra210_swgroups), + .groups = tegra210_groups, + .num_groups = ARRAY_SIZE(tegra210_groups), .supports_round_robin_arbitration = true, .supports_request_limit = true, .num_tlb_lines = 32, diff --git a/drivers/memory/tegra/tegra30.c b/drivers/memory/tegra/tegra30.c index b44737840e70..d756c837f23e 100644 --- a/drivers/memory/tegra/tegra30.c +++ b/drivers/memory/tegra/tegra30.c @@ -934,11 +934,26 @@ static const struct tegra_smmu_swgroup tegra30_swgroups[] = { { .name = "isp", .swgroup = TEGRA_SWGROUP_ISP, .reg = 0x258 }, }; +static const unsigned int tegra30_group_display[] = { + TEGRA_SWGROUP_DC, + TEGRA_SWGROUP_DCB, +}; + +static const struct tegra_smmu_group_soc tegra30_groups[] = { + { + .name = "display", + .swgroups = tegra30_group_display, + .num_swgroups = ARRAY_SIZE(tegra30_group_display), + }, +}; + static const struct tegra_smmu_soc tegra30_smmu_soc = { .clients = tegra30_mc_clients, .num_clients = ARRAY_SIZE(tegra30_mc_clients), .swgroups = tegra30_swgroups, .num_swgroups = ARRAY_SIZE(tegra30_swgroups), + .groups = tegra30_groups, + .num_groups = ARRAY_SIZE(tegra30_groups), .supports_round_robin_arbitration = false, .supports_request_limit = false, .num_tlb_lines = 16, diff --git a/include/soc/tegra/mc.h b/include/soc/tegra/mc.h index 44202ff897fd..233bae954970 100644 --- a/include/soc/tegra/mc.h +++ b/include/soc/tegra/mc.h @@ -51,6 +51,12 @@ struct tegra_smmu_swgroup { unsigned int reg; }; +struct tegra_smmu_group_soc { + const char *name; + const unsigned int *swgroups; + unsigned int num_swgroups; +}; + struct tegra_smmu_soc { const struct tegra_mc_client *clients; unsigned int num_clients; @@ -58,6 +64,9 @@ struct tegra_smmu_soc { const struct tegra_smmu_swgroup *swgroups; unsigned int num_swgroups; + const struct tegra_smmu_group_soc *groups; + unsigned int num_groups; + bool supports_round_robin_arbitration; bool supports_request_limit; From 7f4c9176f760f4006af9f0863403f977a0bb3c52 Mon Sep 17 00:00:00 2001 From: Thierry Reding Date: Thu, 12 Oct 2017 16:19:16 +0200 Subject: [PATCH 5/6] iommu/tegra: Allow devices to be grouped Implement the ->device_group() and ->of_xlate() callbacks which are used in order to group devices. Each group can then share a single domain. This is implemented primarily in order to achieve the same semantics on Tegra210 and earlier as on Tegra186 where the Tegra SMMU was replaced by an ARM SMMU. Users of the IOMMU API can now use the same code to share domains between devices, whereas previously they used to attach each device individually. Acked-by: Alex Williamson Signed-off-by: Thierry Reding --- drivers/iommu/tegra-smmu.c | 124 +++++++++++++++++++++++++++++++++++-- 1 file changed, 120 insertions(+), 4 deletions(-) diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 3b6449e2cbf1..8885635d0a3b 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -20,6 +20,12 @@ #include #include +struct tegra_smmu_group { + struct list_head list; + const struct tegra_smmu_group_soc *soc; + struct iommu_group *group; +}; + struct tegra_smmu { void __iomem *regs; struct device *dev; @@ -27,6 +33,8 @@ struct tegra_smmu { struct tegra_mc *mc; const struct tegra_smmu_soc *soc; + struct list_head groups; + unsigned long pfn_mask; unsigned long tlb_mask; @@ -703,19 +711,47 @@ static struct tegra_smmu *tegra_smmu_find(struct device_node *np) return mc->smmu; } +static int tegra_smmu_configure(struct tegra_smmu *smmu, struct device *dev, + struct of_phandle_args *args) +{ + const struct iommu_ops *ops = smmu->iommu.ops; + int err; + + err = iommu_fwspec_init(dev, &dev->of_node->fwnode, ops); + if (err < 0) { + dev_err(dev, "failed to initialize fwspec: %d\n", err); + return err; + } + + err = ops->of_xlate(dev, args); + if (err < 0) { + dev_err(dev, "failed to parse SW group ID: %d\n", err); + iommu_fwspec_free(dev); + return err; + } + + return 0; +} + static int tegra_smmu_add_device(struct device *dev) { struct device_node *np = dev->of_node; + struct tegra_smmu *smmu = NULL; struct iommu_group *group; struct of_phandle_args args; unsigned int index = 0; + int err; while (of_parse_phandle_with_args(np, "iommus", "#iommu-cells", index, &args) == 0) { - struct tegra_smmu *smmu; - smmu = tegra_smmu_find(args.np); if (smmu) { + err = tegra_smmu_configure(smmu, dev, &args); + of_node_put(args.np); + + if (err < 0) + return err; + /* * Only a single IOMMU master interface is currently * supported by the Linux kernel, so abort after the @@ -728,9 +764,13 @@ static int tegra_smmu_add_device(struct device *dev) break; } + of_node_put(args.np); index++; } + if (!smmu) + return -ENODEV; + group = iommu_group_get_for_dev(dev); if (IS_ERR(group)) return PTR_ERR(group); @@ -751,6 +791,80 @@ static void tegra_smmu_remove_device(struct device *dev) iommu_group_remove_device(dev); } +static const struct tegra_smmu_group_soc * +tegra_smmu_find_group(struct tegra_smmu *smmu, unsigned int swgroup) +{ + unsigned int i, j; + + for (i = 0; i < smmu->soc->num_groups; i++) + for (j = 0; j < smmu->soc->groups[i].num_swgroups; j++) + if (smmu->soc->groups[i].swgroups[j] == swgroup) + return &smmu->soc->groups[i]; + + return NULL; +} + +static struct iommu_group *tegra_smmu_group_get(struct tegra_smmu *smmu, + unsigned int swgroup) +{ + const struct tegra_smmu_group_soc *soc; + struct tegra_smmu_group *group; + + soc = tegra_smmu_find_group(smmu, swgroup); + if (!soc) + return NULL; + + mutex_lock(&smmu->lock); + + list_for_each_entry(group, &smmu->groups, list) + if (group->soc == soc) { + mutex_unlock(&smmu->lock); + return group->group; + } + + group = devm_kzalloc(smmu->dev, sizeof(*group), GFP_KERNEL); + if (!group) { + mutex_unlock(&smmu->lock); + return NULL; + } + + INIT_LIST_HEAD(&group->list); + group->soc = soc; + + group->group = iommu_group_alloc(); + if (!group->group) { + devm_kfree(smmu->dev, group); + mutex_unlock(&smmu->lock); + return NULL; + } + + list_add_tail(&group->list, &smmu->groups); + mutex_unlock(&smmu->lock); + + return group->group; +} + +static struct iommu_group *tegra_smmu_device_group(struct device *dev) +{ + struct iommu_fwspec *fwspec = dev->iommu_fwspec; + struct tegra_smmu *smmu = dev->archdata.iommu; + struct iommu_group *group; + + group = tegra_smmu_group_get(smmu, fwspec->ids[0]); + if (!group) + group = generic_device_group(dev); + + return group; +} + +static int tegra_smmu_of_xlate(struct device *dev, + struct of_phandle_args *args) +{ + u32 id = args->args[0]; + + return iommu_fwspec_add_ids(dev, &id, 1); +} + static const struct iommu_ops tegra_smmu_ops = { .capable = tegra_smmu_capable, .domain_alloc = tegra_smmu_domain_alloc, @@ -759,12 +873,12 @@ static const struct iommu_ops tegra_smmu_ops = { .detach_dev = tegra_smmu_detach_dev, .add_device = tegra_smmu_add_device, .remove_device = tegra_smmu_remove_device, - .device_group = generic_device_group, + .device_group = tegra_smmu_device_group, .map = tegra_smmu_map, .unmap = tegra_smmu_unmap, .map_sg = default_iommu_map_sg, .iova_to_phys = tegra_smmu_iova_to_phys, - + .of_xlate = tegra_smmu_of_xlate, .pgsize_bitmap = SZ_4K, }; @@ -913,6 +1027,7 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev, if (!smmu->asids) return ERR_PTR(-ENOMEM); + INIT_LIST_HEAD(&smmu->groups); mutex_init(&smmu->lock); smmu->regs = mc->regs; @@ -954,6 +1069,7 @@ struct tegra_smmu *tegra_smmu_probe(struct device *dev, return ERR_PTR(err); iommu_device_set_ops(&smmu->iommu, &tegra_smmu_ops); + iommu_device_set_fwnode(&smmu->iommu, dev->fwnode); err = iommu_device_register(&smmu->iommu); if (err) { From 83476bfaf6ac1cebf0cc5a3bdcf5031ef875cf42 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 20 Dec 2017 03:06:09 +0000 Subject: [PATCH 6/6] iommu/tegra-smmu: Fix return value check in tegra_smmu_group_get() In case of error, the function iommu_group_alloc() returns ERR_PTR() and never returns NULL. The NULL test in the return value check should be replaced with IS_ERR(). Fixes: 7f4c9176f760 ("iommu/tegra: Allow devices to be grouped") Signed-off-by: Wei Yongjun Acked-by: Alex Williamson Signed-off-by: Thierry Reding --- drivers/iommu/tegra-smmu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iommu/tegra-smmu.c b/drivers/iommu/tegra-smmu.c index 8885635d0a3b..44d40bc771b5 100644 --- a/drivers/iommu/tegra-smmu.c +++ b/drivers/iommu/tegra-smmu.c @@ -832,7 +832,7 @@ static struct iommu_group *tegra_smmu_group_get(struct tegra_smmu *smmu, group->soc = soc; group->group = iommu_group_alloc(); - if (!group->group) { + if (IS_ERR(group->group)) { devm_kfree(smmu->dev, group); mutex_unlock(&smmu->lock); return NULL;