Commit b4af7f77 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'iommu-updates-v4.6' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu

Pull IOMMU updates from Joerg Roedel:

 - updates for the Exynos IOMMU driver to make use of default domains
   and to add support for the SYSMMU v5

 - new Mediatek IOMMU driver

 - support for the ARMv7 short descriptor format in the io-pgtable code

 - default domain support for the ARM SMMU

 - couple of other small fixes all over the place

* tag 'iommu-updates-v4.6' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu: (41 commits)
  iommu/ipmmu-vmsa: Add r8a7795 DT binding
  iommu/mediatek: Check for NULL instead of IS_ERR()
  iommu/io-pgtable-armv7s: Fix kmem_cache_alloc() flags
  iommu/mediatek: Fix handling of of_count_phandle_with_args result
  iommu/dma: Fix NEED_SG_DMA_LENGTH dependency
  iommu/mediatek: Mark PM functions as __maybe_unused
  iommu/mediatek: Select ARM_DMA_USE_IOMMU
  iommu/exynos: Use proper readl/writel register interface
  iommu/exynos: Pointers are nto physical addresses
  dts: mt8173: Add iommu/smi nodes for mt8173
  iommu/mediatek: Add mt8173 IOMMU driver
  memory: mediatek: Add SMI driver
  dt-bindings: mediatek: Add smi dts binding
  dt-bindings: iommu: Add binding for mediatek IOMMU
  iommu/ipmmu-vmsa: Use ARCH_RENESAS
  iommu/exynos: Support multiple attach_device calls
  iommu/exynos: Add Maintainers entry for Exynos SYSMMU driver
  iommu/exynos: Add support for v5 SYSMMU
  iommu/exynos: Update device tree documentation
  iommu/exynos: Add support for SYSMMU controller with bogus version reg
  ...
parents 968f3e37 70cf769c
* 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 bridge 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>;
...
};
......@@ -7,23 +7,34 @@ connected to the IPMMU through a port called micro-TLB.
Required Properties:
- compatible: Must contain SoC-specific and generic entries from below.
- compatible: Must contain SoC-specific and generic entry below in case
the device is compatible with the R-Car Gen2 VMSA-compatible IPMMU.
- "renesas,ipmmu-r8a73a4" for the R8A73A4 (R-Mobile APE6) IPMMU.
- "renesas,ipmmu-r8a7790" for the R8A7790 (R-Car H2) IPMMU.
- "renesas,ipmmu-r8a7791" for the R8A7791 (R-Car M2-W) IPMMU.
- "renesas,ipmmu-r8a7793" for the R8A7793 (R-Car M2-N) IPMMU.
- "renesas,ipmmu-r8a7794" for the R8A7794 (R-Car E2) IPMMU.
- "renesas,ipmmu-r8a7795" for the R8A7795 (R-Car H3) IPMMU.
- "renesas,ipmmu-vmsa" for generic R-Car Gen2 VMSA-compatible IPMMU.
- reg: Base address and size of the IPMMU registers.
- interrupts: Specifiers for the MMU fault interrupts. For instances that
support secure mode two interrupts must be specified, for non-secure and
secure mode, in that order. For instances that don't support secure mode a
single interrupt must be specified.
single interrupt must be specified. Not required for cache IPMMUs.
- #iommu-cells: Must be 1.
Optional properties:
- renesas,ipmmu-main: reference to the main IPMMU instance in two cells.
The first cell is a phandle to the main IPMMU and the second cell is
the interrupt bit number associated with the particular cache IPMMU device.
The interrupt bit number needs to match the main IPMMU IMSSTR register.
Only used by cache IPMMU instances.
Each bus master connected to an IPMMU must reference the IPMMU in its device
node with the following property:
......
......@@ -23,28 +23,24 @@ MMUs.
for window 1, 2 and 3.
* M2M Scalers and G2D in Exynos5420 has one System MMU on the read channel and
the other System MMU on the write channel.
The drivers must consider how to handle those System MMUs. One of the idea is
to implement child devices or sub-devices which are the client devices of the
System MMU.
Note:
The current DT binding for the Exynos System MMU is incomplete.
The following properties can be removed or changed, if found incompatible with
the "Generic IOMMU Binding" support for attaching devices to the IOMMU.
For information on assigning System MMU controller to its peripheral devices,
see generic IOMMU bindings.
Required properties:
- compatible: Should be "samsung,exynos-sysmmu"
- reg: A tuple of base address and size of System MMU registers.
- #iommu-cells: Should be <0>.
- interrupt-parent: The phandle of the interrupt controller of System MMU
- interrupts: An interrupt specifier for interrupt signal of System MMU,
according to the format defined by a particular interrupt
controller.
- clock-names: Should be "sysmmu" if the System MMU is needed to gate its clock.
- clock-names: Should be "sysmmu" or a pair of "aclk" and "pclk" to gate
SYSMMU core clocks.
Optional "master" if the clock to the System MMU is gated by
another gate clock other than "sysmmu".
Exynos4 SoCs, there needs no "master" clock.
Exynos5 SoCs, some System MMUs must have "master" clocks.
- clocks: Required if the System MMU is needed to gate its clock.
another gate clock other core (usually main gate clock
of peripheral device this SYSMMU belongs to).
- clocks: Phandles for respective clocks described by clock-names.
- power-domains: Required if the System MMU is needed to gate its power.
Please refer to the following document:
Documentation/devicetree/bindings/power/pd-samsung.txt
......@@ -57,6 +53,7 @@ Examples:
power-domains = <&pd_gsc>;
clocks = <&clock CLK_GSCL0>;
clock-names = "gscl";
iommus = <&sysmmu_gsc0>;
};
sysmmu_gsc0: sysmmu@13E80000 {
......@@ -67,4 +64,5 @@ Examples:
clock-names = "sysmmu", "master";
clocks = <&clock CLK_SMMU_GSCL0>, <&clock CLK_GSCL0>;
power-domains = <&pd_gsc>;
#iommu-cells = <0>;
};
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";
};
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";
};
......@@ -1838,11 +1838,13 @@ F: drivers/edac/synopsys_edac.c
ARM SMMU DRIVERS
M: Will Deacon <will.deacon@arm.com>
R: Robin Murphy <robin.murphy@arm.com>
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
S: Maintained
F: drivers/iommu/arm-smmu.c
F: drivers/iommu/arm-smmu-v3.c
F: drivers/iommu/io-pgtable-arm.c
F: drivers/iommu/io-pgtable-arm-v7s.c
ARM64 PORT (AARCH64 ARCHITECTURE)
M: Catalin Marinas <catalin.marinas@arm.com>
......@@ -4362,6 +4364,12 @@ L: dri-devel@lists.freedesktop.org
S: Maintained
F: drivers/gpu/drm/exynos/exynos_dp*
EXYNOS SYSMMU (IOMMU) driver
M: Marek Szyprowski <m.szyprowski@samsung.com>
L: iommu@lists.linux-foundation.org
S: Maintained
F: drivers/iommu/exynos-iommu.c
EXYNOS MIPI DISPLAY DRIVERS
M: Inki Dae <inki.dae@samsung.com>
M: Donghwa Lee <dh09.lee@samsung.com>
......
......@@ -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/phy/phy.h>
#include <dt-bindings/power/mt8173-power.h>
#include <dt-bindings/reset/mt8173-resets.h>
......@@ -277,6 +278,17 @@ sysirq: intpol-controller@10200620 {
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>;
};
efuse: efuse@10206000 {
compatible = "mediatek,mt8173-efuse";
reg = <0 0x10206000 0 0x1000>;
......@@ -605,29 +617,98 @@ pwm1: pwm@1401f000 {
status = "disabled";
};
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";
};
};
};
......@@ -39,6 +39,25 @@ config IOMMU_IO_PGTABLE_LPAE_SELFTEST
If unsure, say N here.
config IOMMU_IO_PGTABLE_ARMV7S
bool "ARMv7/v8 Short Descriptor Format"
select IOMMU_IO_PGTABLE
depends on HAS_DMA && (ARM || ARM64 || COMPILE_TEST)
help
Enable support for the ARM Short-descriptor pagetable format.
This supports 32-bit virtual and physical addresses mapped using
2-level tables with 4KB pages/1MB sections, and contiguous entries
for 64KB pages/16MB supersections if indicated by the IOMMU driver.
config IOMMU_IO_PGTABLE_ARMV7S_SELFTEST
bool "ARMv7s selftests"
depends on IOMMU_IO_PGTABLE_ARMV7S
help
Enable self-tests for ARMv7s page table allocator. This performs
a series of page-table consistency checks during boot.
If unsure, say N here.
endmenu
config IOMMU_IOVA
......@@ -51,9 +70,9 @@ config OF_IOMMU
# IOMMU-agnostic DMA-mapping layer
config IOMMU_DMA
bool
depends on NEED_SG_DMA_LENGTH
select IOMMU_API
select IOMMU_IOVA
select NEED_SG_DMA_LENGTH
config FSL_PAMU
bool "Freescale IOMMU support"
......@@ -243,7 +262,7 @@ config TEGRA_IOMMU_SMMU
config EXYNOS_IOMMU
bool "Exynos IOMMU Support"
depends on ARCH_EXYNOS && ARM && MMU
depends on ARCH_EXYNOS && MMU
select IOMMU_API
select ARM_DMA_USE_IOMMU
help
......@@ -266,7 +285,7 @@ config EXYNOS_IOMMU_DEBUG
config IPMMU_VMSA
bool "Renesas VMSA-compatible IPMMU"
depends on ARM_LPAE
depends on ARCH_SHMOBILE || COMPILE_TEST
depends on ARCH_RENESAS || COMPILE_TEST
select IOMMU_API
select IOMMU_IO_PGTABLE_LPAE
select ARM_DMA_USE_IOMMU
......@@ -318,4 +337,21 @@ config S390_IOMMU
help
Support for the IOMMU API for s390 PCI devices.
config MTK_IOMMU
bool "MTK IOMMU Support"
depends on ARM || ARM64
depends on ARCH_MEDIATEK || COMPILE_TEST
select ARM_DMA_USE_IOMMU
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
......@@ -3,6 +3,7 @@ obj-$(CONFIG_IOMMU_API) += iommu-traces.o
obj-$(CONFIG_IOMMU_API) += iommu-sysfs.o
obj-$(CONFIG_IOMMU_DMA) += dma-iommu.o
obj-$(CONFIG_IOMMU_IO_PGTABLE) += io-pgtable.o
obj-$(CONFIG_IOMMU_IO_PGTABLE_ARMV7S) += io-pgtable-arm-v7s.o
obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o
obj-$(CONFIG_IOMMU_IOVA) += iova.o
obj-$(CONFIG_OF_IOMMU) += of_iommu.o
......@@ -16,6 +17,7 @@ obj-$(CONFIG_INTEL_IOMMU) += intel-iommu.o
obj-$(CONFIG_INTEL_IOMMU_SVM) += intel-svm.o
obj-$(CONFIG_IPMMU_VMSA) += ipmmu-vmsa.o
obj-$(CONFIG_IRQ_REMAP) += intel_irq_remapping.o irq_remapping.o
obj-$(CONFIG_MTK_IOMMU) += mtk_iommu.o
obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o
obj-$(CONFIG_OMAP_IOMMU_DEBUG) += omap-iommu-debug.o
obj-$(CONFIG_ROCKCHIP_IOMMU) += rockchip-iommu.o
......
......@@ -21,6 +21,7 @@
*/
#include <linux/delay.h>
#include <linux/dma-iommu.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/iommu.h>
......@@ -1396,7 +1397,7 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
{
struct arm_smmu_domain *smmu_domain;
if (type != IOMMU_DOMAIN_UNMANAGED)
if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_DMA)
return NULL;
/*
......@@ -1408,6 +1409,12 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
if (!smmu_domain)
return NULL;
if (type == IOMMU_DOMAIN_DMA &&
iommu_get_dma_cookie(&smmu_domain->domain)) {
kfree(smmu_domain);
return NULL;
}
mutex_init(&smmu_domain->init_mutex);
spin_lock_init(&smmu_domain->pgtbl_lock);
return &smmu_domain->domain;
......@@ -1436,6 +1443,7 @@ static void arm_smmu_domain_free(struct iommu_domain *domain)
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
struct arm_smmu_device *smmu = smmu_domain->smmu;
iommu_put_dma_cookie(domain);
free_io_pgtable_ops(smmu_domain->pgtbl_ops);
/* Free the CD and ASID, if we allocated them */
......@@ -1630,6 +1638,17 @@ static int arm_smmu_install_ste_for_group(struct arm_smmu_group *smmu_group)
return 0;
}
static void arm_smmu_detach_dev(struct device *dev)
{
struct arm_smmu_group *smmu_group = arm_smmu_group_get(dev);
smmu_group->ste.bypass = true;
if (IS_ERR_VALUE(arm_smmu_install_ste_for_group(smmu_group)))
dev_warn(dev, "failed to install bypass STE\n");
smmu_group->domain = NULL;
}
static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
{
int ret = 0;
......@@ -1642,7 +1661,7 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
/* Already attached to a different domain? */
if (smmu_group->domain && smmu_group->domain != smmu_domain)
return -EEXIST;
arm_smmu_detach_dev(dev);
smmu = smmu_group->smmu;
mutex_lock(&smmu_domain->init_mutex);
......@@ -1668,7 +1687,12 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
goto out_unlock;
smmu_group->domain = smmu_domain;
smmu_group->ste.bypass = false;
/*
* FIXME: This should always be "false" once we have IOMMU-backed
* DMA ops for all devices behind the SMMU.
*/
smmu_group->ste.bypass = domain->type == IOMMU_DOMAIN_DMA;
ret = arm_smmu_install_ste_for_group(smmu_group);
if (IS_ERR_VALUE(ret))
......@@ -1679,25 +1703,6 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
return ret;
}
static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev)
{
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
struct arm_smmu_group *smmu_group = arm_smmu_group_get(dev);
BUG_ON(!smmu_domain);
BUG_ON(!smmu_group);
mutex_lock(&smmu_domain->init_mutex);
BUG_ON(smmu_group->domain != smmu_domain);
smmu_group->ste.bypass = true;
if (IS_ERR_VALUE(arm_smmu_install_ste_for_group(smmu_group)))
dev_warn(dev, "failed to install bypass STE\n");
smmu_group->domain = NULL;
mutex_unlock(&smmu_domain->init_mutex);
}
static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t size, int prot)
{
......@@ -1935,7 +1940,6 @@ static struct iommu_ops arm_smmu_ops = {
.domain_alloc = arm_smmu_domain_alloc,
.domain_free = arm_smmu_domain_free,
.attach_dev = arm_smmu_attach_dev,
.detach_dev = arm_smmu_detach_dev,
.map = arm_smmu_map,
.unmap = arm_smmu_unmap,
.iova_to_phys = arm_smmu_iova_to_phys,
......
......@@ -29,6 +29,7 @@
#define pr_fmt(fmt) "arm-smmu: " fmt
#include <linux/delay.h>
#include <linux/dma-iommu.h>
#include <linux/dma-mapping.h>
#include <linux/err.h>
#include <linux/interrupt.h>
......@@ -167,6 +168,9 @@
#define S2CR_TYPE_BYPASS (1 << S2CR_TYPE_SHIFT)
#define S2CR_TYPE_FAULT (2 << S2CR_TYPE_SHIFT)
#define S2CR_PRIVCFG_SHIFT 24
#define S2CR_PRIVCFG_UNPRIV (2 << S2CR_PRIVCFG_SHIFT)
/* Context bank attribute registers */
#define ARM_SMMU_GR1_CBAR(n) (0x0 + ((n) << 2))
#define CBAR_VMID_SHIFT 0
......@@ -257,9 +261,13 @@
#define FSYNR0_WNR (1 << 4)
static int force_stage;
module_param_named(force_stage, force_stage, int, S_IRUGO);
module_param(force_stage, int, S_IRUGO);
MODULE_PARM_DESC(force_stage,
"Force SMMU mappings to be installed at a particular stage of translation. A value of '1' or '2' forces the corresponding stage. All other values are ignored (i.e. no stage is forced). Note that selecting a specific stage will disable support for nested translation.");
static bool disable_bypass;
module_param(disable_bypass, bool, S_IRUGO);
MODULE_PARM_DESC(disable_bypass,
"Disable bypass streams such that incoming transactions from devices that are not attached to an iommu domain will report an abort back to the device and will not be allowed to pass through the SMMU.");
enum arm_smmu_arch_version {
ARM_SMMU_V1 = 1,
......@@ -963,7 +971,7 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
{
struct arm_smmu_domain *smmu_domain;
if (type != IOMMU_DOMAIN_UNMANAGED)
if (type != IOMMU_DOMAIN_UNMANAGED && type != IOMMU_DOMAIN_DMA)
return NULL;
/*
* Allocate the domain and initialise some of its data structures.
......@@ -974,6 +982,12 @@ static struct iommu_domain *arm_smmu_domain_alloc(unsigned type)
if (!smmu_domain)
return NULL;
if (type == IOMMU_DOMAIN_DMA &&
iommu_get_dma_cookie(&smmu_domain->domain)) {
kfree(smmu_domain);
return NULL;
}
mutex_init(&smmu_domain->init_mutex);
spin_lock_init(&smmu_domain->pgtbl_lock);
......@@ -988,6 +1002,7 @@ static void arm_smmu_domain_free(struct iommu_domain *domain)
* Free the domain resources. We assume that all devices have
* already been detached.
*/
iommu_put_dma_cookie(domain);
arm_smmu_destroy_domain_context(domain);
kfree(smmu_domain);
}
......@@ -1079,11 +1094,18 @@ static int arm_smmu_domain_add_master(struct arm_smmu_domain *smmu_domain,
if (ret)
return ret == -EEXIST ? 0 : ret;
/*
* FIXME: This won't be needed once we have IOMMU-backed DMA ops
* for all devices behind the SMMU.
*/
if (smmu_domain->domain.type == IOMMU_DOMAIN_DMA)
return 0;
for (i = 0; i < cfg->num_streamids; ++i) {
u32 idx, s2cr;
idx = cfg->smrs ? cfg->smrs[i].idx : cfg->streamids[i];
s2cr = S2CR_TYPE_TRANS |
s2cr = S2CR_TYPE_TRANS | S2CR_PRIVCFG_UNPRIV |
(smmu_domain->cfg.cbndx << S2CR_CBNDX_SHIFT);
writel_relaxed(s2cr, gr0_base + ARM_SMMU_GR0_S2CR(idx));
}
......@@ -1108,14 +1130,24 @@ static void arm_smmu_domain_remove_master(struct arm_smmu_domain *smmu_domain,
*/
for (i = 0; i < cfg->num_streamids; ++i) {
u32 idx = cfg->smrs ? cfg->smrs[i].idx : cfg->streamids[i];
u32 reg = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS;
writel_relaxed(S2CR_TYPE_BYPASS,
gr0_base + ARM_SMMU_GR0_S2CR(idx));
writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_S2CR(idx));
}
arm_smmu_master_free_smrs(smmu, cfg);
}
static void arm_smmu_detach_dev(struct device *dev,
struct arm_smmu_master_cfg *cfg)
{
struct iommu_domain *domain = dev->archdata.iommu;
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
dev->archdata.iommu = NULL;
arm_smmu_domain_remove_master(smmu_domain, cfg);
}
static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
{
int ret;
......@@ -1129,11 +1161,6 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
return -ENXIO;
}
if (dev->archdata.iommu) {
dev_err(dev, "already attached to IOMMU domain\n");
return -EEXIST;
}
/* Ensure that the domain is finalised */
ret = arm_smmu_init_domain_context(domain, smmu);
if (IS_ERR_VALUE(ret))
......@@ -1155,25 +1182,16 @@ static int arm_smmu_attach_dev(struct iommu_domain *domain, struct device *dev)
if (!cfg)
return -ENODEV;
/* Detach the dev from its current domain */
if (dev->archdata.iommu)
arm_smmu_detach_dev(dev, cfg);
ret = arm_smmu_domain_add_master(smmu_domain, cfg);
if (!ret)
dev->archdata.iommu = domain;
return ret;
}
static void arm_smmu_detach_dev(struct iommu_domain *domain, struct device *dev)
{
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
struct arm_smmu_master_cfg *cfg;
cfg = find_smmu_master_cfg(dev);
if (!cfg)
return;
dev->archdata.iommu = NULL;
arm_smmu_domain_remove_master(smmu_domain, cfg);
}
static int arm_smmu_map(struct iommu_domain *domain, unsigned long iova,
phys_addr_t paddr, size_t size, int prot)
{
......@@ -1449,7 +1467,6 @@ static struct iommu_ops arm_smmu_ops = {
.domain_alloc = arm_smmu_domain_alloc,
.domain_free = arm_smmu_domain_free,
.attach_dev = arm_smmu_attach_dev,
.detach_dev = arm_smmu_detach_dev,
.map = arm_smmu_map,
.unmap = arm_smmu_unmap,
.map_sg = default_iommu_map_sg,
......@@ -1473,11 +1490,11 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
reg = readl_relaxed(ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sGFSR);
writel(reg, ARM_SMMU_GR0_NS(smmu) + ARM_SMMU_GR0_sGFSR);
/* Mark all SMRn as invalid and all S2CRn as bypass */
/* Mark all SMRn as invalid and all S2CRn as bypass unless overridden */
reg = disable_bypass ? S2CR_TYPE_FAULT : S2CR_TYPE_BYPASS;
for (i = 0; i < smmu->num_mapping_groups; ++i) {
writel_relaxed(0, gr0_base + ARM_SMMU_GR0_SMR(i));
writel_relaxed(S2CR_TYPE_BYPASS,
gr0_base + ARM_SMMU_GR0_S2CR(i));
writel_relaxed(reg, gr0_base + ARM_SMMU_GR0_S2CR(i));
}
/* Make sure all context banks are disabled and clear CB_FSR */
......@@ -1499,8 +1516,12 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
/* Disable TLB broadcasting. */
reg |= (sCR0_VMIDPNE | sCR0_PTM);
/* Enable client access, but bypass when no mapping is found */
reg &= ~(sCR0_CLIENTPD | sCR0_USFCFG);
/* Enable client access, handling unmatched streams as appropriate */
reg &= ~sCR0_CLIENTPD;
if (disable_bypass)
reg |= sCR0_USFCFG;
else
reg &= ~sCR0_USFCFG;
/* Disable forced broadcasting */
reg &= ~sCR0_FB;
......
This diff is collapsed.
This diff is collapsed.
......@@ -446,7 +446,6 @@ static int arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data,
unsigned long blk_start, blk_end;
phys_addr_t blk_paddr;
arm_lpae_iopte table = 0;
struct io_pgtable_cfg *cfg = &data->iop.cfg;
blk_start = iova & ~(blk_size - 1);
blk_end = blk_start + blk_size;
......@@ -472,9 +471,9 @@ static int arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data,
}
}
__arm_lpae_set_pte(ptep, table, cfg);
__arm_lpae_set_pte(ptep, table, &data->iop.cfg);
iova &= ~(blk_size - 1);
cfg->tlb->tlb_add_flush(iova, blk_size, blk_size, true, data->iop.cookie);
io_pgtable_tlb_add_flush(&data->iop, iova, blk_size, blk_size, true);
return size;
}
......@@ -483,8 +482,7 @@ static int __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
arm_lpae_iopte *ptep)
{
arm_lpae_iopte pte;
const struct iommu_gather_ops *tlb = data->iop.cfg.tlb;
void *cookie = data->iop.cookie;
struct io_pgtable *iop = &data->iop;
size_t blk_size = ARM_LPAE_BLOCK_SIZE(lvl, data);
/* Something went horribly wrong and we ran out of page table */
......@@ -498,17 +496,17 @@ static int __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
/* If the size matches this level, we're in the right place */
if (size == blk_size) {
__arm_lpae_set_pte(ptep, 0, &data->iop.cfg);
__arm_lpae_set_pte(ptep, 0, &iop->cfg);
if (!iopte_leaf(pte, lvl)) {
/* Also flush any partial walks */
tlb->tlb_add_flush(iova, size, ARM_LPAE_GRANULE(data),
false, cookie);
tlb->tlb_sync(cookie);
io_pgtable_tlb_add_flush(iop, iova, size,
ARM_LPAE_GRANULE(data), false);
io_pgtable_tlb_sync(iop);
ptep = iopte_deref(pte, data);
__arm_lpae_free_pgtable(data, lvl + 1, ptep);
} else {
tlb->tlb_add_flush(iova, size, size, true, cookie);
io_pgtable_tlb_add_flush(iop, iova, size, size, true);
}
return size;
......@@ -532,13 +530,12 @@ static int arm_lpae_unmap(struct io_pgtable_ops *ops, unsigned long iova,
{
size_t unmapped;
struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);
struct io_pgtable *iop = &data->iop;
arm_lpae_iopte *ptep = data->pgd;
int lvl = ARM_LPAE_START_LVL(data);
unmapped = __arm_lpae_unmap(data, iova, size, lvl, ptep);
if (unmapped)
iop->cfg.tlb->tlb_sync(iop->cookie);
io_pgtable_tlb_sync(&data->iop);
return unmapped;
}
......@@ -662,8 +659,12 @@ static struct io_pgtable *
arm_64_lpae_alloc_pgtable_s1(struct io_pgtable_cfg *cfg, void *cookie)
{
u64 reg;
struct arm_lpae_io_pgtable *data = arm_lpae_alloc_pgtable(cfg);
struct arm_lpae_io_pgtable *data;
if (cfg->quirks & ~IO_PGTABLE_QUIRK_ARM_NS)
return NULL;
data = arm_lpae_alloc_pgtable(cfg);
if (!data)
return NULL;
......@@ -746,8 +747,13 @@ static struct io_pgtable *
arm_64_lpae_alloc_pgtable_s2(struct io_pgtable_cfg *cfg, void *cookie)
{
u64 reg, sl;
struct arm_lpae_io_pgtable *data = arm_lpae_alloc_pgtable(cfg);
struct arm_lpae_io_pgtable *data;
/* The NS quirk doesn't apply at stage 2 */
if (cfg->quirks)
return NULL;
data = arm_lpae_alloc_pgtable(cfg);
if (!data)
return NULL;
......
......@@ -33,6 +33,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_ARMV7S
[ARM_V7S] = &io_pgtable_arm_v7s_init_fns,
#endif
};
struct io_pgtable_ops *alloc_io_pgtable_ops(enum io_pgtable_fmt fmt,
......@@ -72,6 +75,6 @@ void free_io_pgtable_ops(struct io_pgtable_ops *ops)
return;
iop = container_of(ops, struct io_pgtable, ops);
iop->cfg.tlb->tlb_flush_all(iop->cookie);
io_pgtable_tlb_flush_all(iop);
io_pgtable_init_table[iop->fmt]->free(iop);
}
#ifndef __IO_PGTABLE_H
#define __IO_PGTABLE_H
#include <linux/bitops.h>
/*
* Public API for use by IOMMU drivers
......@@ -9,6 +10,7 @@ enum io_pgtable_fmt {
ARM_32_LPAE_S2,
ARM_64_LPAE_S1,
ARM_64_LPAE_S2,
ARM_V7S,
IO_PGTABLE_NUM_FMTS,
};
......@@ -45,8 +47,24 @@ struct iommu_gather_ops {
* page table walker.
*/
struct io_pgtable_cfg {
#define IO_PGTABLE_QUIRK_ARM_NS (1 << 0) /* Set NS bit in PTEs */
int quirks;
/*
* IO_PGTABLE_QUIRK_ARM_NS: (ARM formats) Set NS and NSTABLE bits in
* stage 1 PTEs, for hardware which insists on validating them
* even in non-secure state where they should normally be ignored.
*
* IO_PGTABLE_QUIRK_NO_PERMS: Ignore the IOMMU_READ, IOMMU_WRITE and
* IOMMU_NOEXEC flags and map everything with full access, for
* hardware which does not implement the permissions of a given
* format, and/or requires some format-specific default value.
*
* IO_PGTABLE_QUIRK_TLBI_ON_MAP: If the format forbids caching invalid
* (unmapped) entries but the hardware might do so anyway, perform
* TLB maintenance when mapping as well as when unmapping.
*/
#define IO_PGTABLE_QUIRK_ARM_NS BIT(0)
#define IO_PGTABLE_QUIRK_NO_PERMS BIT(1)
#define IO_PGTABLE_QUIRK_TLBI_ON_MAP BIT(2)
unsigned long quirks;
unsigned long pgsize_bitmap;
unsigned int ias;
unsigned int oas;
......@@ -65,6 +83,13 @@ struct io_pgtable_cfg {
u64 vttbr;
u64 vtcr;
} arm_lpae_s2_cfg;
struct {
u32 ttbr[2];
u32 tcr;
u32 nmrr;
u32 prrr;
} arm_v7s_cfg;
};
};
......@@ -121,18 +146,41 @@ void free_io_pgtable_ops(struct io_pgtable_ops *ops);
* @fmt: The page table format.
* @cookie: An opaque token provided by the IOMMU driver and passed back to
* any callback routines.
* @tlb_sync_pending: Private flag for optimising out redundant syncs.
* @cfg: A copy of the page table configuration.
* @ops: The page table operations in use for this set of page tables.
*/
struct io_pgtable {
enum io_pgtable_fmt fmt;
void *cookie;
bool tlb_sync_pending;
struct io_pgtable_cfg cfg;
struct io_pgtable_ops ops;
};
#define io_pgtable_ops_to_pgtable(x) container_of((x), struct io_pgtable, ops)
static inline void io_pgtable_tlb_flush_all(struct io_pgtable *iop)
{
iop->cfg.tlb->tlb_flush_all(iop->cookie);
iop->tlb_sync_pending = true;
}
static inline void io_pgtable_tlb_add_flush(struct io_pgtable *iop,
unsigned long iova, size_t size, size_t granule, bool leaf)
{
iop->cfg.tlb->tlb_add_flush(iova, size, granule, leaf, iop->cookie);
iop->tlb_sync_pending = true;
}
static inline void io_pgtable_tlb_sync(struct io_pgtable *iop)
{
if (iop->tlb_sync_pending) {
iop->cfg.tlb->tlb_sync(iop->cookie);
iop->tlb_sync_pending = false;
}
}
/**
* struct io_pgtable_init_fns - Alloc/free a set of page tables for a
* particular format.
......@@ -149,5 +197,6 @@ 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_v7s_init_fns;
#endif /* __IO_PGTABLE_H */
......@@ -1314,6 +1314,7 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova,
unsigned long orig_iova = iova;
unsigned int min_pagesz;
size_t orig_size = size;
phys_addr_t orig_paddr = paddr;
int ret = 0;
if (unlikely(domain->ops->map == NULL ||
......@@ -1358,7 +1359,7 @@ int iommu_map(struct iommu_domain *domain, unsigned long iova,
if (ret)
iommu_unmap(domain, orig_iova, orig_size - size);
else
trace_map(orig_iova, paddr, orig_size);
trace_map(orig_iova, orig_paddr, orig_size);
return ret;
}
......
This diff is collapsed.
......@@ -110,6 +110,7 @@ void of_iommu_set_ops(struct device_node *np, struct iommu_ops *ops)
if (WARN_ON(!iommu))
return;
of_node_get(np);
INIT_LIST_HEAD(&iommu->list);
iommu->np = np;
iommu->ops = ops;
......
This diff is collapsed.
......@@ -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
......@@ -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/
/*
* Copyright (c) 2015-2016 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/device.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/of.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
struct mtk_smi {
struct device *dev;
struct clk *clk_apb, *clk_smi;
};
struct mtk_smi_larb { /* larb: local arbiter */
struct mtk_smi smi;
void __iomem *base;
struct device *smi_common_dev;
u32 *mmu;
};
static int mtk_smi_enable(const struct mtk_smi *smi)
{
int ret;
ret = pm_runtime_get_sync(smi->dev);
if (ret < 0)
return ret;
ret = clk_prepare_enable(smi->clk_apb);
if (ret)
goto err_put_pm;
ret = clk_prepare_enable(smi->clk_smi);
if (ret)
goto err_disable_apb;
return 0;
err_disable_apb:
clk_disable_unprepare(smi->clk_apb);
err_put_pm:
pm_runtime_put_sync(smi->dev);
return ret;
}
static void mtk_smi_disable(const struct mtk_smi *smi)
{
clk_disable_unprepare(smi->clk_smi);
clk_disable_unprepare(smi->clk_apb);
pm_runtime_put_sync(smi->dev);
}
int mtk_smi_larb_get(struct device *larbdev)
{
struct mtk_smi_larb *larb = dev_get_drvdata(larbdev);
struct mtk_smi *common = dev_get_drvdata(larb->smi_common_dev);
int ret;
/* Enable the smi-common's power and clocks */
ret = mtk_smi_enable(common);
if (ret)
return ret;
/* Enable the larb's power and clocks */
ret = mtk_smi_enable(&larb->smi);
if (ret) {
mtk_smi_disable(common);
return ret;
}
/* Configure the iommu info for this larb */
writel(*larb->mmu, larb->base + SMI_LARB_MMU_EN);
return 0;
}
void mtk_smi_larb_put(struct device *larbdev)
{
struct mtk_smi_larb *larb = dev_get_drvdata(larbdev);
struct mtk_smi *common = dev_get_drvdata(larb->smi_common_dev);
/*
* Don't de-configure the iommu info for this larb since there may be
* several modules in this larb.
* The iommu info will be reset after power off.
*/
mtk_smi_disable(&larb->smi);
mtk_smi_disable(common);
}
static int
mtk_smi_larb_bind(struct device *dev, struct device *master, void *data)
{
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
struct mtk_smi_iommu *smi_iommu = data;
unsigned int i;
for (i = 0; i < smi_iommu->larb_nr; i++) {
if (dev == smi_iommu->larb_imu[i].dev) {
/* The 'mmu' may be updated in iommu-attach/detach. */
larb->mmu = &smi_iommu->larb_imu[i].mmu;
return 0;
}
}
return -ENODEV;
}
static void
mtk_smi_larb_unbind(struct device *dev, struct device *master, void *data)
{
/* Do nothing as the iommu is always enabled. */
}
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;
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->smi.clk_apb = devm_clk_get(dev, "apb");
if (IS_ERR(larb->smi.clk_apb))
return PTR_ERR(larb->smi.clk_apb);
larb->smi.clk_smi = devm_clk_get(dev, "smi");
if (IS_ERR(larb->smi.clk_smi))
return PTR_ERR(larb->smi.clk_smi);
larb->smi.dev = dev;
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);
platform_set_drvdata(pdev, 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;
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);
platform_set_drvdata(pdev, 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);
/*
* Copyright (c) 2015-2016 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
/*
* Copyright (c) 2015-2016 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/bitops.h>
#include <linux/device.h>
#ifdef CONFIG_MTK_SMI
#define MTK_LARB_NR_MAX 8
#define MTK_SMI_MMU_EN(port) BIT(port)
struct mtk_smi_larb_iommu {
struct device *dev;
unsigned int mmu;
};
struct mtk_smi_iommu {
unsigned int larb_nr;
struct mtk_smi_larb_iommu larb_imu[MTK_LARB_NR_MAX];
};
/*
* 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 int mtk_smi_larb_get(struct device *larbdev)
{
return 0;
}
static inline void mtk_smi_larb_put(struct device *larbdev) { }
#endif
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment