You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
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 ...
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
* Mediatek IOMMU Architecture Implementation
|
||||
|
||||
Some Mediatek SOCs contain a Multimedia Memory Management Unit (M4U) which
|
||||
uses the ARM Short-Descriptor translation table format for address translation.
|
||||
|
||||
About the M4U Hardware Block Diagram, please check below:
|
||||
|
||||
EMI (External Memory Interface)
|
||||
|
|
||||
m4u (Multimedia Memory Management Unit)
|
||||
|
|
||||
SMI Common(Smart Multimedia Interface Common)
|
||||
|
|
||||
+----------------+-------
|
||||
| |
|
||||
| |
|
||||
SMI larb0 SMI larb1 ... SoCs have several SMI local arbiter(larb).
|
||||
(display) (vdec)
|
||||
| |
|
||||
| |
|
||||
+-----+-----+ +----+----+
|
||||
| | | | | |
|
||||
| | |... | | | ... There are different ports in each larb.
|
||||
| | | | | |
|
||||
OVL0 RDMA0 WDMA0 MC PP VLD
|
||||
|
||||
As above, The Multimedia HW will go through SMI and M4U while it
|
||||
access EMI. SMI is a 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>;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
SMI (Smart Multimedia Interface) Common
|
||||
|
||||
The hardware block diagram please check bindings/iommu/mediatek,iommu.txt
|
||||
|
||||
Required properties:
|
||||
- compatible : must be "mediatek,mt8173-smi-common"
|
||||
- reg : the register and size of the SMI block.
|
||||
- power-domains : a phandle to the power domain of this local arbiter.
|
||||
- clocks : Must contain an entry for each entry in clock-names.
|
||||
- clock-names : must contain 2 entries, as follows:
|
||||
- "apb" : Advanced Peripheral Bus clock, It's the clock for setting
|
||||
the register.
|
||||
- "smi" : It's the clock for transfer data and command.
|
||||
They may be the same if both source clocks are the same.
|
||||
|
||||
Example:
|
||||
smi_common: smi@14022000 {
|
||||
compatible = "mediatek,mt8173-smi-common";
|
||||
reg = <0 0x14022000 0 0x1000>;
|
||||
power-domains = <&scpsys MT8173_POWER_DOMAIN_MM>;
|
||||
clocks = <&mmsys CLK_MM_SMI_COMMON>,
|
||||
<&mmsys CLK_MM_SMI_COMMON>;
|
||||
clock-names = "apb", "smi";
|
||||
};
|
||||
@@ -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";
|
||||
};
|
||||
@@ -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 @@
|
||||
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 @@
|
||||
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
-3
@@ -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
|
||||
|
||||
+27
-23
@@ -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 @@ out_unlock:
|
||||
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,
|
||||
|
||||
+50
-29
@@ -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;
|
||||
|
||||
+362
-250
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
|
||||
+137
-81
@@ -86,7 +86,8 @@ struct rk_iommu_domain {
|
||||
|
||||
struct rk_iommu {
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
void __iomem **bases;
|
||||
int num_mmu;
|
||||
int irq;
|
||||
struct list_head node; /* entry in rk_iommu_domain.iommus */
|
||||
struct iommu_domain *domain; /* domain to which iommu is attached */
|
||||
@@ -271,47 +272,70 @@ static u32 rk_iova_page_offset(dma_addr_t iova)
|
||||
return (u32)(iova & RK_IOVA_PAGE_MASK) >> RK_IOVA_PAGE_SHIFT;
|
||||
}
|
||||
|
||||
static u32 rk_iommu_read(struct rk_iommu *iommu, u32 offset)
|
||||
static u32 rk_iommu_read(void __iomem *base, u32 offset)
|
||||
{
|
||||
return readl(iommu->base + offset);
|
||||
return readl(base + offset);
|
||||
}
|
||||
|
||||
static void rk_iommu_write(struct rk_iommu *iommu, u32 offset, u32 value)
|
||||
static void rk_iommu_write(void __iomem *base, u32 offset, u32 value)
|
||||
{
|
||||
writel(value, iommu->base + offset);
|
||||
writel(value, base + offset);
|
||||
}
|
||||
|
||||
static void rk_iommu_command(struct rk_iommu *iommu, u32 command)
|
||||
{
|
||||
writel(command, iommu->base + RK_MMU_COMMAND);
|
||||
int i;
|
||||
|
||||
for (i = 0; i < iommu->num_mmu; i++)
|
||||
writel(command, iommu->bases[i] + RK_MMU_COMMAND);
|
||||
}
|
||||
|
||||
static void rk_iommu_base_command(void __iomem *base, u32 command)
|
||||
{
|
||||
writel(command, base + RK_MMU_COMMAND);
|
||||
}
|
||||
static void rk_iommu_zap_lines(struct rk_iommu *iommu, dma_addr_t iova,
|
||||
size_t size)
|
||||
{
|
||||
int i;
|
||||
|
||||
dma_addr_t iova_end = iova + size;
|
||||
/*
|
||||
* TODO(djkurtz): Figure out when it is more efficient to shootdown the
|
||||
* entire iotlb rather than iterate over individual iovas.
|
||||
*/
|
||||
for (; iova < iova_end; iova += SPAGE_SIZE)
|
||||
rk_iommu_write(iommu, RK_MMU_ZAP_ONE_LINE, iova);
|
||||
for (i = 0; i < iommu->num_mmu; i++)
|
||||
for (; iova < iova_end; iova += SPAGE_SIZE)
|
||||
rk_iommu_write(iommu->bases[i], RK_MMU_ZAP_ONE_LINE, iova);
|
||||
}
|
||||
|
||||
static bool rk_iommu_is_stall_active(struct rk_iommu *iommu)
|
||||
{
|
||||
return rk_iommu_read(iommu, RK_MMU_STATUS) & RK_MMU_STATUS_STALL_ACTIVE;
|
||||
bool active = true;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < iommu->num_mmu; i++)
|
||||
active &= rk_iommu_read(iommu->bases[i], RK_MMU_STATUS) &
|
||||
RK_MMU_STATUS_STALL_ACTIVE;
|
||||
|
||||
return active;
|
||||
}
|
||||
|
||||
static bool rk_iommu_is_paging_enabled(struct rk_iommu *iommu)
|
||||
{
|
||||
return rk_iommu_read(iommu, RK_MMU_STATUS) &
|
||||
RK_MMU_STATUS_PAGING_ENABLED;
|
||||
bool enable = true;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < iommu->num_mmu; i++)
|
||||
enable &= rk_iommu_read(iommu->bases[i], RK_MMU_STATUS) &
|
||||
RK_MMU_STATUS_PAGING_ENABLED;
|
||||
|
||||
return enable;
|
||||
}
|
||||
|
||||
static int rk_iommu_enable_stall(struct rk_iommu *iommu)
|
||||
{
|
||||
int ret;
|
||||
int ret, i;
|
||||
|
||||
if (rk_iommu_is_stall_active(iommu))
|
||||
return 0;
|
||||
@@ -324,15 +348,16 @@ static int rk_iommu_enable_stall(struct rk_iommu *iommu)
|
||||
|
||||
ret = rk_wait_for(rk_iommu_is_stall_active(iommu), 1);
|
||||
if (ret)
|
||||
dev_err(iommu->dev, "Enable stall request timed out, status: %#08x\n",
|
||||
rk_iommu_read(iommu, RK_MMU_STATUS));
|
||||
for (i = 0; i < iommu->num_mmu; i++)
|
||||
dev_err(iommu->dev, "Enable stall request timed out, status: %#08x\n",
|
||||
rk_iommu_read(iommu->bases[i], RK_MMU_STATUS));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rk_iommu_disable_stall(struct rk_iommu *iommu)
|
||||
{
|
||||
int ret;
|
||||
int ret, i;
|
||||
|
||||
if (!rk_iommu_is_stall_active(iommu))
|
||||
return 0;
|
||||
@@ -341,15 +366,16 @@ static int rk_iommu_disable_stall(struct rk_iommu *iommu)
|
||||
|
||||
ret = rk_wait_for(!rk_iommu_is_stall_active(iommu), 1);
|
||||
if (ret)
|
||||
dev_err(iommu->dev, "Disable stall request timed out, status: %#08x\n",
|
||||
rk_iommu_read(iommu, RK_MMU_STATUS));
|
||||
for (i = 0; i < iommu->num_mmu; i++)
|
||||
dev_err(iommu->dev, "Disable stall request timed out, status: %#08x\n",
|
||||
rk_iommu_read(iommu->bases[i], RK_MMU_STATUS));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rk_iommu_enable_paging(struct rk_iommu *iommu)
|
||||
{
|
||||
int ret;
|
||||
int ret, i;
|
||||
|
||||
if (rk_iommu_is_paging_enabled(iommu))
|
||||
return 0;
|
||||
@@ -358,15 +384,16 @@ static int rk_iommu_enable_paging(struct rk_iommu *iommu)
|
||||
|
||||
ret = rk_wait_for(rk_iommu_is_paging_enabled(iommu), 1);
|
||||
if (ret)
|
||||
dev_err(iommu->dev, "Enable paging request timed out, status: %#08x\n",
|
||||
rk_iommu_read(iommu, RK_MMU_STATUS));
|
||||
for (i = 0; i < iommu->num_mmu; i++)
|
||||
dev_err(iommu->dev, "Enable paging request timed out, status: %#08x\n",
|
||||
rk_iommu_read(iommu->bases[i], RK_MMU_STATUS));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rk_iommu_disable_paging(struct rk_iommu *iommu)
|
||||
{
|
||||
int ret;
|
||||
int ret, i;
|
||||
|
||||
if (!rk_iommu_is_paging_enabled(iommu))
|
||||
return 0;
|
||||
@@ -375,41 +402,49 @@ static int rk_iommu_disable_paging(struct rk_iommu *iommu)
|
||||
|
||||
ret = rk_wait_for(!rk_iommu_is_paging_enabled(iommu), 1);
|
||||
if (ret)
|
||||
dev_err(iommu->dev, "Disable paging request timed out, status: %#08x\n",
|
||||
rk_iommu_read(iommu, RK_MMU_STATUS));
|
||||
for (i = 0; i < iommu->num_mmu; i++)
|
||||
dev_err(iommu->dev, "Disable paging request timed out, status: %#08x\n",
|
||||
rk_iommu_read(iommu->bases[i], RK_MMU_STATUS));
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rk_iommu_force_reset(struct rk_iommu *iommu)
|
||||
{
|
||||
int ret;
|
||||
int ret, i;
|
||||
u32 dte_addr;
|
||||
|
||||
/*
|
||||
* Check if register DTE_ADDR is working by writing DTE_ADDR_DUMMY
|
||||
* and verifying that upper 5 nybbles are read back.
|
||||
*/
|
||||
rk_iommu_write(iommu, RK_MMU_DTE_ADDR, DTE_ADDR_DUMMY);
|
||||
for (i = 0; i < iommu->num_mmu; i++) {
|
||||
rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, DTE_ADDR_DUMMY);
|
||||
|
||||
dte_addr = rk_iommu_read(iommu, RK_MMU_DTE_ADDR);
|
||||
if (dte_addr != (DTE_ADDR_DUMMY & RK_DTE_PT_ADDRESS_MASK)) {
|
||||
dev_err(iommu->dev, "Error during raw reset. MMU_DTE_ADDR is not functioning\n");
|
||||
return -EFAULT;
|
||||
dte_addr = rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR);
|
||||
if (dte_addr != (DTE_ADDR_DUMMY & RK_DTE_PT_ADDRESS_MASK)) {
|
||||
dev_err(iommu->dev, "Error during raw reset. MMU_DTE_ADDR is not functioning\n");
|
||||
return -EFAULT;
|
||||
}
|
||||
}
|
||||
|
||||
rk_iommu_command(iommu, RK_MMU_CMD_FORCE_RESET);
|
||||
|
||||
ret = rk_wait_for(rk_iommu_read(iommu, RK_MMU_DTE_ADDR) == 0x00000000,
|
||||
FORCE_RESET_TIMEOUT);
|
||||
if (ret)
|
||||
dev_err(iommu->dev, "FORCE_RESET command timed out\n");
|
||||
for (i = 0; i < iommu->num_mmu; i++) {
|
||||
ret = rk_wait_for(rk_iommu_read(iommu->bases[i], RK_MMU_DTE_ADDR) == 0x00000000,
|
||||
FORCE_RESET_TIMEOUT);
|
||||
if (ret) {
|
||||
dev_err(iommu->dev, "FORCE_RESET command timed out\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void log_iova(struct rk_iommu *iommu, dma_addr_t iova)
|
||||
static void log_iova(struct rk_iommu *iommu, int index, dma_addr_t iova)
|
||||
{
|
||||
void __iomem *base = iommu->bases[index];
|
||||
u32 dte_index, pte_index, page_offset;
|
||||
u32 mmu_dte_addr;
|
||||
phys_addr_t mmu_dte_addr_phys, dte_addr_phys;
|
||||
@@ -425,7 +460,7 @@ static void log_iova(struct rk_iommu *iommu, dma_addr_t iova)
|
||||
pte_index = rk_iova_pte_index(iova);
|
||||
page_offset = rk_iova_page_offset(iova);
|
||||
|
||||
mmu_dte_addr = rk_iommu_read(iommu, RK_MMU_DTE_ADDR);
|
||||
mmu_dte_addr = rk_iommu_read(base, RK_MMU_DTE_ADDR);
|
||||
mmu_dte_addr_phys = (phys_addr_t)mmu_dte_addr;
|
||||
|
||||
dte_addr_phys = mmu_dte_addr_phys + (4 * dte_index);
|
||||
@@ -460,51 +495,56 @@ static irqreturn_t rk_iommu_irq(int irq, void *dev_id)
|
||||
u32 status;
|
||||
u32 int_status;
|
||||
dma_addr_t iova;
|
||||
irqreturn_t ret = IRQ_NONE;
|
||||
int i;
|
||||
|
||||
int_status = rk_iommu_read(iommu, RK_MMU_INT_STATUS);
|
||||
if (int_status == 0)
|
||||
return IRQ_NONE;
|
||||
for (i = 0; i < iommu->num_mmu; i++) {
|
||||
int_status = rk_iommu_read(iommu->bases[i], RK_MMU_INT_STATUS);
|
||||
if (int_status == 0)
|
||||
continue;
|
||||
|
||||
iova = rk_iommu_read(iommu, RK_MMU_PAGE_FAULT_ADDR);
|
||||
ret = IRQ_HANDLED;
|
||||
iova = rk_iommu_read(iommu->bases[i], RK_MMU_PAGE_FAULT_ADDR);
|
||||
|
||||
if (int_status & RK_MMU_IRQ_PAGE_FAULT) {
|
||||
int flags;
|
||||
if (int_status & RK_MMU_IRQ_PAGE_FAULT) {
|
||||
int flags;
|
||||
|
||||
status = rk_iommu_read(iommu, RK_MMU_STATUS);
|
||||
flags = (status & RK_MMU_STATUS_PAGE_FAULT_IS_WRITE) ?
|
||||
IOMMU_FAULT_WRITE : IOMMU_FAULT_READ;
|
||||
status = rk_iommu_read(iommu->bases[i], RK_MMU_STATUS);
|
||||
flags = (status & RK_MMU_STATUS_PAGE_FAULT_IS_WRITE) ?
|
||||
IOMMU_FAULT_WRITE : IOMMU_FAULT_READ;
|
||||
|
||||
dev_err(iommu->dev, "Page fault at %pad of type %s\n",
|
||||
&iova,
|
||||
(flags == IOMMU_FAULT_WRITE) ? "write" : "read");
|
||||
dev_err(iommu->dev, "Page fault at %pad of type %s\n",
|
||||
&iova,
|
||||
(flags == IOMMU_FAULT_WRITE) ? "write" : "read");
|
||||
|
||||
log_iova(iommu, iova);
|
||||
log_iova(iommu, i, iova);
|
||||
|
||||
/*
|
||||
* Report page fault to any installed handlers.
|
||||
* Ignore the return code, though, since we always zap cache
|
||||
* and clear the page fault anyway.
|
||||
*/
|
||||
if (iommu->domain)
|
||||
report_iommu_fault(iommu->domain, iommu->dev, iova,
|
||||
flags);
|
||||
else
|
||||
dev_err(iommu->dev, "Page fault while iommu not attached to domain?\n");
|
||||
/*
|
||||
* Report page fault to any installed handlers.
|
||||
* Ignore the return code, though, since we always zap cache
|
||||
* and clear the page fault anyway.
|
||||
*/
|
||||
if (iommu->domain)
|
||||
report_iommu_fault(iommu->domain, iommu->dev, iova,
|
||||
flags);
|
||||
else
|
||||
dev_err(iommu->dev, "Page fault while iommu not attached to domain?\n");
|
||||
|
||||
rk_iommu_command(iommu, RK_MMU_CMD_ZAP_CACHE);
|
||||
rk_iommu_command(iommu, RK_MMU_CMD_PAGE_FAULT_DONE);
|
||||
rk_iommu_base_command(iommu->bases[i], RK_MMU_CMD_ZAP_CACHE);
|
||||
rk_iommu_base_command(iommu->bases[i], RK_MMU_CMD_PAGE_FAULT_DONE);
|
||||
}
|
||||
|
||||
if (int_status & RK_MMU_IRQ_BUS_ERROR)
|
||||
dev_err(iommu->dev, "BUS_ERROR occurred at %pad\n", &iova);
|
||||
|
||||
if (int_status & ~RK_MMU_IRQ_MASK)
|
||||
dev_err(iommu->dev, "unexpected int_status: %#08x\n",
|
||||
int_status);
|
||||
|
||||
rk_iommu_write(iommu->bases[i], RK_MMU_INT_CLEAR, int_status);
|
||||
}
|
||||
|
||||
if (int_status & RK_MMU_IRQ_BUS_ERROR)
|
||||
dev_err(iommu->dev, "BUS_ERROR occurred at %pad\n", &iova);
|
||||
|
||||
if (int_status & ~RK_MMU_IRQ_MASK)
|
||||
dev_err(iommu->dev, "unexpected int_status: %#08x\n",
|
||||
int_status);
|
||||
|
||||
rk_iommu_write(iommu, RK_MMU_INT_CLEAR, int_status);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static phys_addr_t rk_iommu_iova_to_phys(struct iommu_domain *domain,
|
||||
@@ -746,7 +786,7 @@ static int rk_iommu_attach_device(struct iommu_domain *domain,
|
||||
struct rk_iommu *iommu;
|
||||
struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
|
||||
unsigned long flags;
|
||||
int ret;
|
||||
int ret, i;
|
||||
phys_addr_t dte_addr;
|
||||
|
||||
/*
|
||||
@@ -773,9 +813,11 @@ static int rk_iommu_attach_device(struct iommu_domain *domain,
|
||||
return ret;
|
||||
|
||||
dte_addr = virt_to_phys(rk_domain->dt);
|
||||
rk_iommu_write(iommu, RK_MMU_DTE_ADDR, dte_addr);
|
||||
rk_iommu_command(iommu, RK_MMU_CMD_ZAP_CACHE);
|
||||
rk_iommu_write(iommu, RK_MMU_INT_MASK, RK_MMU_IRQ_MASK);
|
||||
for (i = 0; i < iommu->num_mmu; i++) {
|
||||
rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, dte_addr);
|
||||
rk_iommu_command(iommu->bases[i], RK_MMU_CMD_ZAP_CACHE);
|
||||
rk_iommu_write(iommu->bases[i], RK_MMU_INT_MASK, RK_MMU_IRQ_MASK);
|
||||
}
|
||||
|
||||
ret = rk_iommu_enable_paging(iommu);
|
||||
if (ret)
|
||||
@@ -798,6 +840,7 @@ static void rk_iommu_detach_device(struct iommu_domain *domain,
|
||||
struct rk_iommu *iommu;
|
||||
struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
|
||||
unsigned long flags;
|
||||
int i;
|
||||
|
||||
/* Allow 'virtual devices' (eg drm) to detach from domain */
|
||||
iommu = rk_iommu_from_dev(dev);
|
||||
@@ -811,8 +854,10 @@ static void rk_iommu_detach_device(struct iommu_domain *domain,
|
||||
/* Ignore error while disabling, just keep going */
|
||||
rk_iommu_enable_stall(iommu);
|
||||
rk_iommu_disable_paging(iommu);
|
||||
rk_iommu_write(iommu, RK_MMU_INT_MASK, 0);
|
||||
rk_iommu_write(iommu, RK_MMU_DTE_ADDR, 0);
|
||||
for (i = 0; i < iommu->num_mmu; i++) {
|
||||
rk_iommu_write(iommu->bases[i], RK_MMU_INT_MASK, 0);
|
||||
rk_iommu_write(iommu->bases[i], RK_MMU_DTE_ADDR, 0);
|
||||
}
|
||||
rk_iommu_disable_stall(iommu);
|
||||
|
||||
devm_free_irq(dev, iommu->irq, iommu);
|
||||
@@ -988,6 +1033,7 @@ static int rk_iommu_probe(struct platform_device *pdev)
|
||||
struct device *dev = &pdev->dev;
|
||||
struct rk_iommu *iommu;
|
||||
struct resource *res;
|
||||
int i;
|
||||
|
||||
iommu = devm_kzalloc(dev, sizeof(*iommu), GFP_KERNEL);
|
||||
if (!iommu)
|
||||
@@ -995,11 +1041,21 @@ static int rk_iommu_probe(struct platform_device *pdev)
|
||||
|
||||
platform_set_drvdata(pdev, iommu);
|
||||
iommu->dev = dev;
|
||||
iommu->num_mmu = 0;
|
||||
iommu->bases = devm_kzalloc(dev, sizeof(*iommu->bases) * iommu->num_mmu,
|
||||
GFP_KERNEL);
|
||||
if (!iommu->bases)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
iommu->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(iommu->base))
|
||||
return PTR_ERR(iommu->base);
|
||||
for (i = 0; i < pdev->num_resources; i++) {
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
|
||||
iommu->bases[i] = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(iommu->bases[i]))
|
||||
continue;
|
||||
iommu->num_mmu++;
|
||||
}
|
||||
if (iommu->num_mmu == 0)
|
||||
return PTR_ERR(iommu->bases[0]);
|
||||
|
||||
iommu->irq = platform_get_irq(pdev, 0);
|
||||
if (iommu->irq < 0) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user