mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git
synced 2025-01-07 14:32:23 +00:00
Merge branches 'arm/omap', 'arm/exynos', 'arm/smmu', 'arm/mediatek', 'arm/qcom', 'arm/renesas', 'x86/amd', 'x86/vt-d' and 'core' into next
This commit is contained in:
parent
f74c2bb987
96088a203a
7991eb39ee
097a7df2e3
4c00889341
8758553791
3623002f0f
3d70889532
1f76249cc3
2896ba40d0
commit
e95adb9add
@ -1732,6 +1732,11 @@
|
|||||||
Note that using this option lowers the security
|
Note that using this option lowers the security
|
||||||
provided by tboot because it makes the system
|
provided by tboot because it makes the system
|
||||||
vulnerable to DMA attacks.
|
vulnerable to DMA attacks.
|
||||||
|
nobounce [Default off]
|
||||||
|
Disable bounce buffer for unstrusted devices such as
|
||||||
|
the Thunderbolt devices. This will treat the untrusted
|
||||||
|
devices as the trusted ones, hence might expose security
|
||||||
|
risks of DMA attacks.
|
||||||
|
|
||||||
intel_idle.max_cstate= [KNL,HW,ACPI,X86]
|
intel_idle.max_cstate= [KNL,HW,ACPI,X86]
|
||||||
0 disables intel_idle and fall back on acpi_idle.
|
0 disables intel_idle and fall back on acpi_idle.
|
||||||
@ -1811,7 +1816,7 @@
|
|||||||
synchronously.
|
synchronously.
|
||||||
|
|
||||||
iommu.passthrough=
|
iommu.passthrough=
|
||||||
[ARM64] Configure DMA to bypass the IOMMU by default.
|
[ARM64, X86] Configure DMA to bypass the IOMMU by default.
|
||||||
Format: { "0" | "1" }
|
Format: { "0" | "1" }
|
||||||
0 - Use IOMMU translation for DMA.
|
0 - Use IOMMU translation for DMA.
|
||||||
1 - Bypass the IOMMU for DMA.
|
1 - Bypass the IOMMU for DMA.
|
||||||
|
@ -11,10 +11,23 @@ ARM Short-Descriptor translation table format for address translation.
|
|||||||
|
|
|
|
||||||
m4u (Multimedia Memory Management Unit)
|
m4u (Multimedia Memory Management Unit)
|
||||||
|
|
|
|
||||||
|
+--------+
|
||||||
|
| |
|
||||||
|
gals0-rx gals1-rx (Global Async Local Sync rx)
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
gals0-tx gals1-tx (Global Async Local Sync tx)
|
||||||
|
| | Some SoCs may have GALS.
|
||||||
|
+--------+
|
||||||
|
|
|
||||||
SMI Common(Smart Multimedia Interface Common)
|
SMI Common(Smart Multimedia Interface Common)
|
||||||
|
|
|
|
||||||
+----------------+-------
|
+----------------+-------
|
||||||
| |
|
| |
|
||||||
|
| gals-rx There may be GALS in some larbs.
|
||||||
|
| |
|
||||||
|
| |
|
||||||
|
| gals-tx
|
||||||
| |
|
| |
|
||||||
SMI larb0 SMI larb1 ... SoCs have several SMI local arbiter(larb).
|
SMI larb0 SMI larb1 ... SoCs have several SMI local arbiter(larb).
|
||||||
(display) (vdec)
|
(display) (vdec)
|
||||||
@ -36,6 +49,10 @@ each local arbiter.
|
|||||||
like display, video decode, and camera. And there are different ports
|
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
|
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.
|
video decode local arbiter, all these ports are according to the video HW.
|
||||||
|
In some SoCs, there may be a GALS(Global Async Local Sync) module between
|
||||||
|
smi-common and m4u, and additional GALS module between smi-larb and
|
||||||
|
smi-common. GALS can been seen as a "asynchronous fifo" which could help
|
||||||
|
synchronize for the modules in different clock frequency.
|
||||||
|
|
||||||
Required properties:
|
Required properties:
|
||||||
- compatible : must be one of the following string:
|
- compatible : must be one of the following string:
|
||||||
@ -44,18 +61,25 @@ Required properties:
|
|||||||
"mediatek,mt7623-m4u", "mediatek,mt2701-m4u" for mt7623 which uses
|
"mediatek,mt7623-m4u", "mediatek,mt2701-m4u" for mt7623 which uses
|
||||||
generation one m4u HW.
|
generation one m4u HW.
|
||||||
"mediatek,mt8173-m4u" for mt8173 which uses generation two m4u HW.
|
"mediatek,mt8173-m4u" for mt8173 which uses generation two m4u HW.
|
||||||
|
"mediatek,mt8183-m4u" for mt8183 which uses generation two m4u HW.
|
||||||
- reg : m4u register base and size.
|
- reg : m4u register base and size.
|
||||||
- interrupts : the interrupt of m4u.
|
- interrupts : the interrupt of m4u.
|
||||||
- clocks : must contain one entry for each clock-names.
|
- clocks : must contain one entry for each clock-names.
|
||||||
- clock-names : must be "bclk", It is the block clock of m4u.
|
- clock-names : Only 1 optional clock:
|
||||||
|
- "bclk": the block clock of m4u.
|
||||||
|
Here is the list which require this "bclk":
|
||||||
|
- mt2701, mt2712, mt7623 and mt8173.
|
||||||
|
Note that m4u use the EMI clock which always has been enabled before kernel
|
||||||
|
if there is no this "bclk".
|
||||||
- mediatek,larbs : List of phandle to the local arbiters in the current Socs.
|
- 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
|
Refer to bindings/memory-controllers/mediatek,smi-larb.txt. It must sort
|
||||||
according to the local arbiter index, like larb0, larb1, larb2...
|
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.
|
- iommu-cells : must be 1. This is the mtk_m4u_id according to the HW.
|
||||||
Specifies the mtk_m4u_id as defined in
|
Specifies the mtk_m4u_id as defined in
|
||||||
dt-binding/memory/mt2701-larb-port.h for mt2701, mt7623
|
dt-binding/memory/mt2701-larb-port.h for mt2701, mt7623
|
||||||
dt-binding/memory/mt2712-larb-port.h for mt2712, and
|
dt-binding/memory/mt2712-larb-port.h for mt2712,
|
||||||
dt-binding/memory/mt8173-larb-port.h for mt8173.
|
dt-binding/memory/mt8173-larb-port.h for mt8173, and
|
||||||
|
dt-binding/memory/mt8183-larb-port.h for mt8183.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
iommu: iommu@10205000 {
|
iommu: iommu@10205000 {
|
||||||
|
@ -2,9 +2,10 @@ SMI (Smart Multimedia Interface) Common
|
|||||||
|
|
||||||
The hardware block diagram please check bindings/iommu/mediatek,iommu.txt
|
The hardware block diagram please check bindings/iommu/mediatek,iommu.txt
|
||||||
|
|
||||||
Mediatek SMI have two generations of HW architecture, mt2712 and mt8173 use
|
Mediatek SMI have two generations of HW architecture, here is the list
|
||||||
the second generation of SMI HW while mt2701 uses the first generation HW of
|
which generation the SoCs use:
|
||||||
SMI.
|
generation 1: mt2701 and mt7623.
|
||||||
|
generation 2: mt2712, mt8173 and mt8183.
|
||||||
|
|
||||||
There's slight differences between the two SMI, for generation 2, the
|
There's slight differences between the two SMI, for generation 2, the
|
||||||
register which control the iommu port is at each larb's register base. But
|
register which control the iommu port is at each larb's register base. But
|
||||||
@ -19,6 +20,7 @@ Required properties:
|
|||||||
"mediatek,mt2712-smi-common"
|
"mediatek,mt2712-smi-common"
|
||||||
"mediatek,mt7623-smi-common", "mediatek,mt2701-smi-common"
|
"mediatek,mt7623-smi-common", "mediatek,mt2701-smi-common"
|
||||||
"mediatek,mt8173-smi-common"
|
"mediatek,mt8173-smi-common"
|
||||||
|
"mediatek,mt8183-smi-common"
|
||||||
- reg : the register and size of the SMI block.
|
- reg : the register and size of the SMI block.
|
||||||
- power-domains : a phandle to the power domain of this local arbiter.
|
- power-domains : a phandle to the power domain of this local arbiter.
|
||||||
- clocks : Must contain an entry for each entry in clock-names.
|
- clocks : Must contain an entry for each entry in clock-names.
|
||||||
@ -30,6 +32,10 @@ Required properties:
|
|||||||
They may be the same if both source clocks are the same.
|
They may be the same if both source clocks are the same.
|
||||||
- "async" : asynchronous clock, it help transform the smi clock into the emi
|
- "async" : asynchronous clock, it help transform the smi clock into the emi
|
||||||
clock domain, this clock is only needed by generation 1 smi HW.
|
clock domain, this clock is only needed by generation 1 smi HW.
|
||||||
|
and these 2 option clocks for generation 2 smi HW:
|
||||||
|
- "gals0": the path0 clock of GALS(Global Async Local Sync).
|
||||||
|
- "gals1": the path1 clock of GALS(Global Async Local Sync).
|
||||||
|
Here is the list which has this GALS: mt8183.
|
||||||
|
|
||||||
Example:
|
Example:
|
||||||
smi_common: smi@14022000 {
|
smi_common: smi@14022000 {
|
||||||
|
@ -8,6 +8,7 @@ Required properties:
|
|||||||
"mediatek,mt2712-smi-larb"
|
"mediatek,mt2712-smi-larb"
|
||||||
"mediatek,mt7623-smi-larb", "mediatek,mt2701-smi-larb"
|
"mediatek,mt7623-smi-larb", "mediatek,mt2701-smi-larb"
|
||||||
"mediatek,mt8173-smi-larb"
|
"mediatek,mt8173-smi-larb"
|
||||||
|
"mediatek,mt8183-smi-larb"
|
||||||
- reg : the register and size of this local arbiter.
|
- reg : the register and size of this local arbiter.
|
||||||
- mediatek,smi : a phandle to the smi_common node.
|
- mediatek,smi : a phandle to the smi_common node.
|
||||||
- power-domains : a phandle to the power domain of this local arbiter.
|
- power-domains : a phandle to the power domain of this local arbiter.
|
||||||
@ -16,6 +17,9 @@ Required properties:
|
|||||||
- "apb" : Advanced Peripheral Bus clock, It's the clock for setting
|
- "apb" : Advanced Peripheral Bus clock, It's the clock for setting
|
||||||
the register.
|
the register.
|
||||||
- "smi" : It's the clock for transfer data and command.
|
- "smi" : It's the clock for transfer data and command.
|
||||||
|
and this optional clock name:
|
||||||
|
- "gals": the clock for GALS(Global Async Local Sync).
|
||||||
|
Here is the list which has this GALS: mt8183.
|
||||||
|
|
||||||
Required property for mt2701, mt2712 and mt7623:
|
Required property for mt2701, mt2712 and mt7623:
|
||||||
- mediatek,larb-id :the hardware id of this larb.
|
- mediatek,larb-id :the hardware id of this larb.
|
||||||
|
@ -1350,8 +1350,7 @@ M: Will Deacon <will@kernel.org>
|
|||||||
R: Robin Murphy <robin.murphy@arm.com>
|
R: Robin Murphy <robin.murphy@arm.com>
|
||||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||||
S: Maintained
|
S: Maintained
|
||||||
F: drivers/iommu/arm-smmu.c
|
F: drivers/iommu/arm-smmu*
|
||||||
F: drivers/iommu/arm-smmu-v3.c
|
|
||||||
F: drivers/iommu/io-pgtable-arm.c
|
F: drivers/iommu/io-pgtable-arm.c
|
||||||
F: drivers/iommu/io-pgtable-arm-v7s.c
|
F: drivers/iommu/io-pgtable-arm-v7s.c
|
||||||
|
|
||||||
|
@ -229,3 +229,5 @@ include/generated/ti-pm-asm-offsets.h: arch/arm/mach-omap2/pm-asm-offsets.s FORC
|
|||||||
$(obj)/sleep33xx.o $(obj)/sleep43xx.o: include/generated/ti-pm-asm-offsets.h
|
$(obj)/sleep33xx.o $(obj)/sleep43xx.o: include/generated/ti-pm-asm-offsets.h
|
||||||
|
|
||||||
targets += pm-asm-offsets.s
|
targets += pm-asm-offsets.s
|
||||||
|
|
||||||
|
obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o
|
||||||
|
43
arch/arm/mach-omap2/omap-iommu.c
Normal file
43
arch/arm/mach-omap2/omap-iommu.c
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
/*
|
||||||
|
* OMAP IOMMU quirks for various TI SoCs
|
||||||
|
*
|
||||||
|
* Copyright (C) 2015-2019 Texas Instruments Incorporated - http://www.ti.com/
|
||||||
|
* Suman Anna <s-anna@ti.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/err.h>
|
||||||
|
|
||||||
|
#include "omap_hwmod.h"
|
||||||
|
#include "omap_device.h"
|
||||||
|
#include "powerdomain.h"
|
||||||
|
|
||||||
|
int omap_iommu_set_pwrdm_constraint(struct platform_device *pdev, bool request,
|
||||||
|
u8 *pwrst)
|
||||||
|
{
|
||||||
|
struct powerdomain *pwrdm;
|
||||||
|
struct omap_device *od;
|
||||||
|
u8 next_pwrst;
|
||||||
|
|
||||||
|
od = to_omap_device(pdev);
|
||||||
|
if (!od)
|
||||||
|
return -ENODEV;
|
||||||
|
|
||||||
|
if (od->hwmods_cnt != 1)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
pwrdm = omap_hwmod_get_pwrdm(od->hwmods[0]);
|
||||||
|
if (!pwrdm)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
if (request)
|
||||||
|
*pwrst = pwrdm_read_next_pwrst(pwrdm);
|
||||||
|
|
||||||
|
if (*pwrst > PWRDM_POWER_RET)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
next_pwrst = request ? PWRDM_POWER_ON : *pwrst;
|
||||||
|
|
||||||
|
return pwrdm_set_next_pwrst(pwrdm, next_pwrst);
|
||||||
|
}
|
@ -8,10 +8,8 @@
|
|||||||
extern void no_iommu_init(void);
|
extern void no_iommu_init(void);
|
||||||
#ifdef CONFIG_INTEL_IOMMU
|
#ifdef CONFIG_INTEL_IOMMU
|
||||||
extern int force_iommu, no_iommu;
|
extern int force_iommu, no_iommu;
|
||||||
extern int iommu_pass_through;
|
|
||||||
extern int iommu_detected;
|
extern int iommu_detected;
|
||||||
#else
|
#else
|
||||||
#define iommu_pass_through (0)
|
|
||||||
#define no_iommu (1)
|
#define no_iommu (1)
|
||||||
#define iommu_detected (0)
|
#define iommu_detected (0)
|
||||||
#endif
|
#endif
|
||||||
|
@ -22,8 +22,6 @@ int force_iommu __read_mostly = 1;
|
|||||||
int force_iommu __read_mostly;
|
int force_iommu __read_mostly;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
int iommu_pass_through;
|
|
||||||
|
|
||||||
static int __init pci_iommu_init(void)
|
static int __init pci_iommu_init(void)
|
||||||
{
|
{
|
||||||
if (iommu_detected)
|
if (iommu_detected)
|
||||||
|
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
extern int force_iommu, no_iommu;
|
extern int force_iommu, no_iommu;
|
||||||
extern int iommu_detected;
|
extern int iommu_detected;
|
||||||
extern int iommu_pass_through;
|
|
||||||
|
|
||||||
/* 10 seconds */
|
/* 10 seconds */
|
||||||
#define DMAR_OPERATION_TIMEOUT ((cycles_t) tsc_khz*10*1000)
|
#define DMAR_OPERATION_TIMEOUT ((cycles_t) tsc_khz*10*1000)
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
#include <linux/dma-direct.h>
|
#include <linux/dma-direct.h>
|
||||||
#include <linux/dma-debug.h>
|
#include <linux/dma-debug.h>
|
||||||
|
#include <linux/iommu.h>
|
||||||
#include <linux/dmar.h>
|
#include <linux/dmar.h>
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
#include <linux/memblock.h>
|
#include <linux/memblock.h>
|
||||||
@ -34,21 +35,6 @@ int no_iommu __read_mostly;
|
|||||||
/* Set this to 1 if there is a HW IOMMU in the system */
|
/* Set this to 1 if there is a HW IOMMU in the system */
|
||||||
int iommu_detected __read_mostly = 0;
|
int iommu_detected __read_mostly = 0;
|
||||||
|
|
||||||
/*
|
|
||||||
* This variable becomes 1 if iommu=pt is passed on the kernel command line.
|
|
||||||
* If this variable is 1, IOMMU implementations do no DMA translation for
|
|
||||||
* devices and allow every device to access to whole physical memory. This is
|
|
||||||
* useful if a user wants to use an IOMMU only for KVM device assignment to
|
|
||||||
* guests and not for driver dma translation.
|
|
||||||
* It is also possible to disable by default in kernel config, and enable with
|
|
||||||
* iommu=nopt at boot time.
|
|
||||||
*/
|
|
||||||
#ifdef CONFIG_IOMMU_DEFAULT_PASSTHROUGH
|
|
||||||
int iommu_pass_through __read_mostly = 1;
|
|
||||||
#else
|
|
||||||
int iommu_pass_through __read_mostly;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
extern struct iommu_table_entry __iommu_table[], __iommu_table_end[];
|
extern struct iommu_table_entry __iommu_table[], __iommu_table_end[];
|
||||||
|
|
||||||
void __init pci_iommu_alloc(void)
|
void __init pci_iommu_alloc(void)
|
||||||
@ -120,9 +106,9 @@ static __init int iommu_setup(char *p)
|
|||||||
swiotlb = 1;
|
swiotlb = 1;
|
||||||
#endif
|
#endif
|
||||||
if (!strncmp(p, "pt", 2))
|
if (!strncmp(p, "pt", 2))
|
||||||
iommu_pass_through = 1;
|
iommu_set_default_passthrough(true);
|
||||||
if (!strncmp(p, "nopt", 4))
|
if (!strncmp(p, "nopt", 4))
|
||||||
iommu_pass_through = 0;
|
iommu_set_default_translated(true);
|
||||||
|
|
||||||
gart_parse_options(p);
|
gart_parse_options(p);
|
||||||
|
|
||||||
|
@ -222,7 +222,7 @@ void panfrost_mmu_unmap(struct panfrost_gem_object *bo)
|
|||||||
size_t unmapped_page;
|
size_t unmapped_page;
|
||||||
size_t pgsize = get_pgsize(iova, len - unmapped_len);
|
size_t pgsize = get_pgsize(iova, len - unmapped_len);
|
||||||
|
|
||||||
unmapped_page = ops->unmap(ops, iova, pgsize);
|
unmapped_page = ops->unmap(ops, iova, pgsize, NULL);
|
||||||
if (!unmapped_page)
|
if (!unmapped_page)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -247,20 +247,28 @@ static void mmu_tlb_inv_context_s1(void *cookie)
|
|||||||
mmu_hw_do_operation(pfdev, 0, 0, ~0UL, AS_COMMAND_FLUSH_MEM);
|
mmu_hw_do_operation(pfdev, 0, 0, ~0UL, AS_COMMAND_FLUSH_MEM);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mmu_tlb_inv_range_nosync(unsigned long iova, size_t size,
|
|
||||||
size_t granule, bool leaf, void *cookie)
|
|
||||||
{}
|
|
||||||
|
|
||||||
static void mmu_tlb_sync_context(void *cookie)
|
static void mmu_tlb_sync_context(void *cookie)
|
||||||
{
|
{
|
||||||
//struct panfrost_device *pfdev = cookie;
|
//struct panfrost_device *pfdev = cookie;
|
||||||
// TODO: Wait 1000 GPU cycles for HW_ISSUE_6367/T60X
|
// TODO: Wait 1000 GPU cycles for HW_ISSUE_6367/T60X
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct iommu_gather_ops mmu_tlb_ops = {
|
static void mmu_tlb_flush_walk(unsigned long iova, size_t size, size_t granule,
|
||||||
|
void *cookie)
|
||||||
|
{
|
||||||
|
mmu_tlb_sync_context(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mmu_tlb_flush_leaf(unsigned long iova, size_t size, size_t granule,
|
||||||
|
void *cookie)
|
||||||
|
{
|
||||||
|
mmu_tlb_sync_context(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iommu_flush_ops mmu_tlb_ops = {
|
||||||
.tlb_flush_all = mmu_tlb_inv_context_s1,
|
.tlb_flush_all = mmu_tlb_inv_context_s1,
|
||||||
.tlb_add_flush = mmu_tlb_inv_range_nosync,
|
.tlb_flush_walk = mmu_tlb_flush_walk,
|
||||||
.tlb_sync = mmu_tlb_sync_context,
|
.tlb_flush_leaf = mmu_tlb_flush_leaf,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *access_type_name(struct panfrost_device *pfdev,
|
static const char *access_type_name(struct panfrost_device *pfdev,
|
||||||
|
@ -182,6 +182,7 @@ config INTEL_IOMMU
|
|||||||
select IOMMU_IOVA
|
select IOMMU_IOVA
|
||||||
select NEED_DMA_MAP_STATE
|
select NEED_DMA_MAP_STATE
|
||||||
select DMAR_TABLE
|
select DMAR_TABLE
|
||||||
|
select SWIOTLB
|
||||||
help
|
help
|
||||||
DMA remapping (DMAR) devices support enables independent address
|
DMA remapping (DMAR) devices support enables independent address
|
||||||
translations for Direct Memory Access (DMA) from devices.
|
translations for Direct Memory Access (DMA) from devices.
|
||||||
|
@ -10,13 +10,14 @@ obj-$(CONFIG_IOMMU_IO_PGTABLE_LPAE) += io-pgtable-arm.o
|
|||||||
obj-$(CONFIG_IOMMU_IOVA) += iova.o
|
obj-$(CONFIG_IOMMU_IOVA) += iova.o
|
||||||
obj-$(CONFIG_OF_IOMMU) += of_iommu.o
|
obj-$(CONFIG_OF_IOMMU) += of_iommu.o
|
||||||
obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o
|
obj-$(CONFIG_MSM_IOMMU) += msm_iommu.o
|
||||||
obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o
|
obj-$(CONFIG_AMD_IOMMU) += amd_iommu.o amd_iommu_init.o amd_iommu_quirks.o
|
||||||
obj-$(CONFIG_AMD_IOMMU_DEBUGFS) += amd_iommu_debugfs.o
|
obj-$(CONFIG_AMD_IOMMU_DEBUGFS) += amd_iommu_debugfs.o
|
||||||
obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
|
obj-$(CONFIG_AMD_IOMMU_V2) += amd_iommu_v2.o
|
||||||
obj-$(CONFIG_ARM_SMMU) += arm-smmu.o
|
obj-$(CONFIG_ARM_SMMU) += arm-smmu.o arm-smmu-impl.o
|
||||||
obj-$(CONFIG_ARM_SMMU_V3) += arm-smmu-v3.o
|
obj-$(CONFIG_ARM_SMMU_V3) += arm-smmu-v3.o
|
||||||
obj-$(CONFIG_DMAR_TABLE) += dmar.o
|
obj-$(CONFIG_DMAR_TABLE) += dmar.o
|
||||||
obj-$(CONFIG_INTEL_IOMMU) += intel-iommu.o intel-pasid.o
|
obj-$(CONFIG_INTEL_IOMMU) += intel-iommu.o intel-pasid.o
|
||||||
|
obj-$(CONFIG_INTEL_IOMMU) += intel-trace.o
|
||||||
obj-$(CONFIG_INTEL_IOMMU_DEBUGFS) += intel-iommu-debugfs.o
|
obj-$(CONFIG_INTEL_IOMMU_DEBUGFS) += intel-iommu-debugfs.o
|
||||||
obj-$(CONFIG_INTEL_IOMMU_SVM) += intel-svm.o
|
obj-$(CONFIG_INTEL_IOMMU_SVM) += intel-svm.o
|
||||||
obj-$(CONFIG_IPMMU_VMSA) += ipmmu-vmsa.o
|
obj-$(CONFIG_IPMMU_VMSA) += ipmmu-vmsa.o
|
||||||
|
@ -436,7 +436,7 @@ static int iommu_init_device(struct device *dev)
|
|||||||
* invalid address), we ignore the capability for the device so
|
* invalid address), we ignore the capability for the device so
|
||||||
* it'll be forced to go into translation mode.
|
* it'll be forced to go into translation mode.
|
||||||
*/
|
*/
|
||||||
if ((iommu_pass_through || !amd_iommu_force_isolation) &&
|
if ((iommu_default_passthrough() || !amd_iommu_force_isolation) &&
|
||||||
dev_is_pci(dev) && pci_iommuv2_capable(to_pci_dev(dev))) {
|
dev_is_pci(dev) && pci_iommuv2_capable(to_pci_dev(dev))) {
|
||||||
struct amd_iommu *iommu;
|
struct amd_iommu *iommu;
|
||||||
|
|
||||||
@ -2256,7 +2256,7 @@ static int amd_iommu_add_device(struct device *dev)
|
|||||||
|
|
||||||
BUG_ON(!dev_data);
|
BUG_ON(!dev_data);
|
||||||
|
|
||||||
if (iommu_pass_through || dev_data->iommu_v2)
|
if (dev_data->iommu_v2)
|
||||||
iommu_request_dm_for_dev(dev);
|
iommu_request_dm_for_dev(dev);
|
||||||
|
|
||||||
/* Domains are initialized for this device - have a look what we ended up with */
|
/* Domains are initialized for this device - have a look what we ended up with */
|
||||||
@ -2577,7 +2577,9 @@ static int map_sg(struct device *dev, struct scatterlist *sglist,
|
|||||||
|
|
||||||
bus_addr = address + s->dma_address + (j << PAGE_SHIFT);
|
bus_addr = address + s->dma_address + (j << PAGE_SHIFT);
|
||||||
phys_addr = (sg_phys(s) & PAGE_MASK) + (j << PAGE_SHIFT);
|
phys_addr = (sg_phys(s) & PAGE_MASK) + (j << PAGE_SHIFT);
|
||||||
ret = iommu_map_page(domain, bus_addr, phys_addr, PAGE_SIZE, prot, GFP_ATOMIC);
|
ret = iommu_map_page(domain, bus_addr, phys_addr,
|
||||||
|
PAGE_SIZE, prot,
|
||||||
|
GFP_ATOMIC | __GFP_NOWARN);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto out_unmap;
|
goto out_unmap;
|
||||||
|
|
||||||
@ -2835,7 +2837,7 @@ int __init amd_iommu_init_api(void)
|
|||||||
|
|
||||||
int __init amd_iommu_init_dma_ops(void)
|
int __init amd_iommu_init_dma_ops(void)
|
||||||
{
|
{
|
||||||
swiotlb = (iommu_pass_through || sme_me_mask) ? 1 : 0;
|
swiotlb = (iommu_default_passthrough() || sme_me_mask) ? 1 : 0;
|
||||||
iommu_detected = 1;
|
iommu_detected = 1;
|
||||||
|
|
||||||
if (amd_iommu_unmap_flush)
|
if (amd_iommu_unmap_flush)
|
||||||
@ -3085,7 +3087,8 @@ static int amd_iommu_map(struct iommu_domain *dom, unsigned long iova,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static size_t amd_iommu_unmap(struct iommu_domain *dom, unsigned long iova,
|
static size_t amd_iommu_unmap(struct iommu_domain *dom, unsigned long iova,
|
||||||
size_t page_size)
|
size_t page_size,
|
||||||
|
struct iommu_iotlb_gather *gather)
|
||||||
{
|
{
|
||||||
struct protection_domain *domain = to_pdomain(dom);
|
struct protection_domain *domain = to_pdomain(dom);
|
||||||
size_t unmap_size;
|
size_t unmap_size;
|
||||||
@ -3226,9 +3229,10 @@ static void amd_iommu_flush_iotlb_all(struct iommu_domain *domain)
|
|||||||
domain_flush_complete(dom);
|
domain_flush_complete(dom);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void amd_iommu_iotlb_range_add(struct iommu_domain *domain,
|
static void amd_iommu_iotlb_sync(struct iommu_domain *domain,
|
||||||
unsigned long iova, size_t size)
|
struct iommu_iotlb_gather *gather)
|
||||||
{
|
{
|
||||||
|
amd_iommu_flush_iotlb_all(domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct iommu_ops amd_iommu_ops = {
|
const struct iommu_ops amd_iommu_ops = {
|
||||||
@ -3249,8 +3253,7 @@ const struct iommu_ops amd_iommu_ops = {
|
|||||||
.is_attach_deferred = amd_iommu_is_attach_deferred,
|
.is_attach_deferred = amd_iommu_is_attach_deferred,
|
||||||
.pgsize_bitmap = AMD_IOMMU_PGSIZES,
|
.pgsize_bitmap = AMD_IOMMU_PGSIZES,
|
||||||
.flush_iotlb_all = amd_iommu_flush_iotlb_all,
|
.flush_iotlb_all = amd_iommu_flush_iotlb_all,
|
||||||
.iotlb_range_add = amd_iommu_iotlb_range_add,
|
.iotlb_sync = amd_iommu_iotlb_sync,
|
||||||
.iotlb_sync = amd_iommu_flush_iotlb_all,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*****************************************************************************
|
/*****************************************************************************
|
||||||
@ -4343,13 +4346,62 @@ static const struct irq_domain_ops amd_ir_domain_ops = {
|
|||||||
.deactivate = irq_remapping_deactivate,
|
.deactivate = irq_remapping_deactivate,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
int amd_iommu_activate_guest_mode(void *data)
|
||||||
|
{
|
||||||
|
struct amd_ir_data *ir_data = (struct amd_ir_data *)data;
|
||||||
|
struct irte_ga *entry = (struct irte_ga *) ir_data->entry;
|
||||||
|
|
||||||
|
if (!AMD_IOMMU_GUEST_IR_VAPIC(amd_iommu_guest_ir) ||
|
||||||
|
!entry || entry->lo.fields_vapic.guest_mode)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
entry->lo.val = 0;
|
||||||
|
entry->hi.val = 0;
|
||||||
|
|
||||||
|
entry->lo.fields_vapic.guest_mode = 1;
|
||||||
|
entry->lo.fields_vapic.ga_log_intr = 1;
|
||||||
|
entry->hi.fields.ga_root_ptr = ir_data->ga_root_ptr;
|
||||||
|
entry->hi.fields.vector = ir_data->ga_vector;
|
||||||
|
entry->lo.fields_vapic.ga_tag = ir_data->ga_tag;
|
||||||
|
|
||||||
|
return modify_irte_ga(ir_data->irq_2_irte.devid,
|
||||||
|
ir_data->irq_2_irte.index, entry, NULL);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(amd_iommu_activate_guest_mode);
|
||||||
|
|
||||||
|
int amd_iommu_deactivate_guest_mode(void *data)
|
||||||
|
{
|
||||||
|
struct amd_ir_data *ir_data = (struct amd_ir_data *)data;
|
||||||
|
struct irte_ga *entry = (struct irte_ga *) ir_data->entry;
|
||||||
|
struct irq_cfg *cfg = ir_data->cfg;
|
||||||
|
|
||||||
|
if (!AMD_IOMMU_GUEST_IR_VAPIC(amd_iommu_guest_ir) ||
|
||||||
|
!entry || !entry->lo.fields_vapic.guest_mode)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
entry->lo.val = 0;
|
||||||
|
entry->hi.val = 0;
|
||||||
|
|
||||||
|
entry->lo.fields_remap.dm = apic->irq_dest_mode;
|
||||||
|
entry->lo.fields_remap.int_type = apic->irq_delivery_mode;
|
||||||
|
entry->hi.fields.vector = cfg->vector;
|
||||||
|
entry->lo.fields_remap.destination =
|
||||||
|
APICID_TO_IRTE_DEST_LO(cfg->dest_apicid);
|
||||||
|
entry->hi.fields.destination =
|
||||||
|
APICID_TO_IRTE_DEST_HI(cfg->dest_apicid);
|
||||||
|
|
||||||
|
return modify_irte_ga(ir_data->irq_2_irte.devid,
|
||||||
|
ir_data->irq_2_irte.index, entry, NULL);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL(amd_iommu_deactivate_guest_mode);
|
||||||
|
|
||||||
static int amd_ir_set_vcpu_affinity(struct irq_data *data, void *vcpu_info)
|
static int amd_ir_set_vcpu_affinity(struct irq_data *data, void *vcpu_info)
|
||||||
{
|
{
|
||||||
|
int ret;
|
||||||
struct amd_iommu *iommu;
|
struct amd_iommu *iommu;
|
||||||
struct amd_iommu_pi_data *pi_data = vcpu_info;
|
struct amd_iommu_pi_data *pi_data = vcpu_info;
|
||||||
struct vcpu_data *vcpu_pi_info = pi_data->vcpu_data;
|
struct vcpu_data *vcpu_pi_info = pi_data->vcpu_data;
|
||||||
struct amd_ir_data *ir_data = data->chip_data;
|
struct amd_ir_data *ir_data = data->chip_data;
|
||||||
struct irte_ga *irte = (struct irte_ga *) ir_data->entry;
|
|
||||||
struct irq_2_irte *irte_info = &ir_data->irq_2_irte;
|
struct irq_2_irte *irte_info = &ir_data->irq_2_irte;
|
||||||
struct iommu_dev_data *dev_data = search_dev_data(irte_info->devid);
|
struct iommu_dev_data *dev_data = search_dev_data(irte_info->devid);
|
||||||
|
|
||||||
@ -4360,6 +4412,7 @@ static int amd_ir_set_vcpu_affinity(struct irq_data *data, void *vcpu_info)
|
|||||||
if (!dev_data || !dev_data->use_vapic)
|
if (!dev_data || !dev_data->use_vapic)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
ir_data->cfg = irqd_cfg(data);
|
||||||
pi_data->ir_data = ir_data;
|
pi_data->ir_data = ir_data;
|
||||||
|
|
||||||
/* Note:
|
/* Note:
|
||||||
@ -4378,37 +4431,24 @@ static int amd_ir_set_vcpu_affinity(struct irq_data *data, void *vcpu_info)
|
|||||||
|
|
||||||
pi_data->prev_ga_tag = ir_data->cached_ga_tag;
|
pi_data->prev_ga_tag = ir_data->cached_ga_tag;
|
||||||
if (pi_data->is_guest_mode) {
|
if (pi_data->is_guest_mode) {
|
||||||
/* Setting */
|
ir_data->ga_root_ptr = (pi_data->base >> 12);
|
||||||
irte->hi.fields.ga_root_ptr = (pi_data->base >> 12);
|
ir_data->ga_vector = vcpu_pi_info->vector;
|
||||||
irte->hi.fields.vector = vcpu_pi_info->vector;
|
ir_data->ga_tag = pi_data->ga_tag;
|
||||||
irte->lo.fields_vapic.ga_log_intr = 1;
|
ret = amd_iommu_activate_guest_mode(ir_data);
|
||||||
irte->lo.fields_vapic.guest_mode = 1;
|
if (!ret)
|
||||||
irte->lo.fields_vapic.ga_tag = pi_data->ga_tag;
|
ir_data->cached_ga_tag = pi_data->ga_tag;
|
||||||
|
|
||||||
ir_data->cached_ga_tag = pi_data->ga_tag;
|
|
||||||
} else {
|
} else {
|
||||||
/* Un-Setting */
|
ret = amd_iommu_deactivate_guest_mode(ir_data);
|
||||||
struct irq_cfg *cfg = irqd_cfg(data);
|
|
||||||
|
|
||||||
irte->hi.val = 0;
|
|
||||||
irte->lo.val = 0;
|
|
||||||
irte->hi.fields.vector = cfg->vector;
|
|
||||||
irte->lo.fields_remap.guest_mode = 0;
|
|
||||||
irte->lo.fields_remap.destination =
|
|
||||||
APICID_TO_IRTE_DEST_LO(cfg->dest_apicid);
|
|
||||||
irte->hi.fields.destination =
|
|
||||||
APICID_TO_IRTE_DEST_HI(cfg->dest_apicid);
|
|
||||||
irte->lo.fields_remap.int_type = apic->irq_delivery_mode;
|
|
||||||
irte->lo.fields_remap.dm = apic->irq_dest_mode;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This communicates the ga_tag back to the caller
|
* This communicates the ga_tag back to the caller
|
||||||
* so that it can do all the necessary clean up.
|
* so that it can do all the necessary clean up.
|
||||||
*/
|
*/
|
||||||
ir_data->cached_ga_tag = 0;
|
if (!ret)
|
||||||
|
ir_data->cached_ga_tag = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
return modify_irte_ga(irte_info->devid, irte_info->index, irte, ir_data);
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
14
drivers/iommu/amd_iommu.h
Normal file
14
drivers/iommu/amd_iommu.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||||
|
|
||||||
|
#ifndef AMD_IOMMU_H
|
||||||
|
#define AMD_IOMMU_H
|
||||||
|
|
||||||
|
int __init add_special_device(u8 type, u8 id, u16 *devid, bool cmd_line);
|
||||||
|
|
||||||
|
#ifdef CONFIG_DMI
|
||||||
|
void amd_iommu_apply_ivrs_quirks(void);
|
||||||
|
#else
|
||||||
|
static void amd_iommu_apply_ivrs_quirks(void) { }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
@ -32,6 +32,7 @@
|
|||||||
#include <asm/irq_remapping.h>
|
#include <asm/irq_remapping.h>
|
||||||
|
|
||||||
#include <linux/crash_dump.h>
|
#include <linux/crash_dump.h>
|
||||||
|
#include "amd_iommu.h"
|
||||||
#include "amd_iommu_proto.h"
|
#include "amd_iommu_proto.h"
|
||||||
#include "amd_iommu_types.h"
|
#include "amd_iommu_types.h"
|
||||||
#include "irq_remapping.h"
|
#include "irq_remapping.h"
|
||||||
@ -1002,7 +1003,7 @@ static void __init set_dev_entry_from_acpi(struct amd_iommu *iommu,
|
|||||||
set_iommu_for_device(iommu, devid);
|
set_iommu_for_device(iommu, devid);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init add_special_device(u8 type, u8 id, u16 *devid, bool cmd_line)
|
int __init add_special_device(u8 type, u8 id, u16 *devid, bool cmd_line)
|
||||||
{
|
{
|
||||||
struct devid_map *entry;
|
struct devid_map *entry;
|
||||||
struct list_head *list;
|
struct list_head *list;
|
||||||
@ -1153,6 +1154,8 @@ static int __init init_iommu_from_acpi(struct amd_iommu *iommu,
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
|
amd_iommu_apply_ivrs_quirks();
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* First save the recommended feature enable bits from ACPI
|
* First save the recommended feature enable bits from ACPI
|
||||||
*/
|
*/
|
||||||
|
92
drivers/iommu/amd_iommu_quirks.c
Normal file
92
drivers/iommu/amd_iommu_quirks.c
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Quirks for AMD IOMMU
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019 Kai-Heng Feng <kai.heng.feng@canonical.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef CONFIG_DMI
|
||||||
|
#include <linux/dmi.h>
|
||||||
|
|
||||||
|
#include "amd_iommu.h"
|
||||||
|
|
||||||
|
#define IVHD_SPECIAL_IOAPIC 1
|
||||||
|
|
||||||
|
struct ivrs_quirk_entry {
|
||||||
|
u8 id;
|
||||||
|
u16 devid;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
DELL_INSPIRON_7375 = 0,
|
||||||
|
DELL_LATITUDE_5495,
|
||||||
|
LENOVO_IDEAPAD_330S_15ARR,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct ivrs_quirk_entry ivrs_ioapic_quirks[][3] __initconst = {
|
||||||
|
/* ivrs_ioapic[4]=00:14.0 ivrs_ioapic[5]=00:00.2 */
|
||||||
|
[DELL_INSPIRON_7375] = {
|
||||||
|
{ .id = 4, .devid = 0xa0 },
|
||||||
|
{ .id = 5, .devid = 0x2 },
|
||||||
|
{}
|
||||||
|
},
|
||||||
|
/* ivrs_ioapic[4]=00:14.0 */
|
||||||
|
[DELL_LATITUDE_5495] = {
|
||||||
|
{ .id = 4, .devid = 0xa0 },
|
||||||
|
{}
|
||||||
|
},
|
||||||
|
/* ivrs_ioapic[32]=00:14.0 */
|
||||||
|
[LENOVO_IDEAPAD_330S_15ARR] = {
|
||||||
|
{ .id = 32, .devid = 0xa0 },
|
||||||
|
{}
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
static int __init ivrs_ioapic_quirk_cb(const struct dmi_system_id *d)
|
||||||
|
{
|
||||||
|
const struct ivrs_quirk_entry *i;
|
||||||
|
|
||||||
|
for (i = d->driver_data; i->id != 0 && i->devid != 0; i++)
|
||||||
|
add_special_device(IVHD_SPECIAL_IOAPIC, i->id, (u16 *)&i->devid, 0);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dmi_system_id ivrs_quirks[] __initconst = {
|
||||||
|
{
|
||||||
|
.callback = ivrs_ioapic_quirk_cb,
|
||||||
|
.ident = "Dell Inspiron 7375",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "Inspiron 7375"),
|
||||||
|
},
|
||||||
|
.driver_data = (void *)&ivrs_ioapic_quirks[DELL_INSPIRON_7375],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.callback = ivrs_ioapic_quirk_cb,
|
||||||
|
.ident = "Dell Latitude 5495",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "Latitude 5495"),
|
||||||
|
},
|
||||||
|
.driver_data = (void *)&ivrs_ioapic_quirks[DELL_LATITUDE_5495],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.callback = ivrs_ioapic_quirk_cb,
|
||||||
|
.ident = "Lenovo ideapad 330S-15ARR",
|
||||||
|
.matches = {
|
||||||
|
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
|
||||||
|
DMI_MATCH(DMI_PRODUCT_NAME, "81FB"),
|
||||||
|
},
|
||||||
|
.driver_data = (void *)&ivrs_ioapic_quirks[LENOVO_IDEAPAD_330S_15ARR],
|
||||||
|
},
|
||||||
|
{}
|
||||||
|
};
|
||||||
|
|
||||||
|
void __init amd_iommu_apply_ivrs_quirks(void)
|
||||||
|
{
|
||||||
|
dmi_check_system(ivrs_quirks);
|
||||||
|
}
|
||||||
|
#endif
|
@ -873,6 +873,15 @@ struct amd_ir_data {
|
|||||||
struct msi_msg msi_entry;
|
struct msi_msg msi_entry;
|
||||||
void *entry; /* Pointer to union irte or struct irte_ga */
|
void *entry; /* Pointer to union irte or struct irte_ga */
|
||||||
void *ref; /* Pointer to the actual irte */
|
void *ref; /* Pointer to the actual irte */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store information for activate/de-activate
|
||||||
|
* Guest virtual APIC mode during runtime.
|
||||||
|
*/
|
||||||
|
struct irq_cfg *cfg;
|
||||||
|
int ga_vector;
|
||||||
|
int ga_root_ptr;
|
||||||
|
int ga_tag;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct amd_irte_ops {
|
struct amd_irte_ops {
|
||||||
|
174
drivers/iommu/arm-smmu-impl.c
Normal file
174
drivers/iommu/arm-smmu-impl.c
Normal file
@ -0,0 +1,174 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0-only
|
||||||
|
// Miscellaneous Arm SMMU implementation and integration quirks
|
||||||
|
// Copyright (C) 2019 Arm Limited
|
||||||
|
|
||||||
|
#define pr_fmt(fmt) "arm-smmu: " fmt
|
||||||
|
|
||||||
|
#include <linux/bitfield.h>
|
||||||
|
#include <linux/of.h>
|
||||||
|
|
||||||
|
#include "arm-smmu.h"
|
||||||
|
|
||||||
|
|
||||||
|
static int arm_smmu_gr0_ns(int offset)
|
||||||
|
{
|
||||||
|
switch(offset) {
|
||||||
|
case ARM_SMMU_GR0_sCR0:
|
||||||
|
case ARM_SMMU_GR0_sACR:
|
||||||
|
case ARM_SMMU_GR0_sGFSR:
|
||||||
|
case ARM_SMMU_GR0_sGFSYNR0:
|
||||||
|
case ARM_SMMU_GR0_sGFSYNR1:
|
||||||
|
case ARM_SMMU_GR0_sGFSYNR2:
|
||||||
|
return offset + 0x400;
|
||||||
|
default:
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static u32 arm_smmu_read_ns(struct arm_smmu_device *smmu, int page,
|
||||||
|
int offset)
|
||||||
|
{
|
||||||
|
if (page == ARM_SMMU_GR0)
|
||||||
|
offset = arm_smmu_gr0_ns(offset);
|
||||||
|
return readl_relaxed(arm_smmu_page(smmu, page) + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void arm_smmu_write_ns(struct arm_smmu_device *smmu, int page,
|
||||||
|
int offset, u32 val)
|
||||||
|
{
|
||||||
|
if (page == ARM_SMMU_GR0)
|
||||||
|
offset = arm_smmu_gr0_ns(offset);
|
||||||
|
writel_relaxed(val, arm_smmu_page(smmu, page) + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Since we don't care for sGFAR, we can do without 64-bit accessors */
|
||||||
|
static const struct arm_smmu_impl calxeda_impl = {
|
||||||
|
.read_reg = arm_smmu_read_ns,
|
||||||
|
.write_reg = arm_smmu_write_ns,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct cavium_smmu {
|
||||||
|
struct arm_smmu_device smmu;
|
||||||
|
u32 id_base;
|
||||||
|
};
|
||||||
|
|
||||||
|
static int cavium_cfg_probe(struct arm_smmu_device *smmu)
|
||||||
|
{
|
||||||
|
static atomic_t context_count = ATOMIC_INIT(0);
|
||||||
|
struct cavium_smmu *cs = container_of(smmu, struct cavium_smmu, smmu);
|
||||||
|
/*
|
||||||
|
* Cavium CN88xx erratum #27704.
|
||||||
|
* Ensure ASID and VMID allocation is unique across all SMMUs in
|
||||||
|
* the system.
|
||||||
|
*/
|
||||||
|
cs->id_base = atomic_fetch_add(smmu->num_context_banks, &context_count);
|
||||||
|
dev_notice(smmu->dev, "\tenabling workaround for Cavium erratum 27704\n");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cavium_init_context(struct arm_smmu_domain *smmu_domain)
|
||||||
|
{
|
||||||
|
struct cavium_smmu *cs = container_of(smmu_domain->smmu,
|
||||||
|
struct cavium_smmu, smmu);
|
||||||
|
|
||||||
|
if (smmu_domain->stage == ARM_SMMU_DOMAIN_S2)
|
||||||
|
smmu_domain->cfg.vmid += cs->id_base;
|
||||||
|
else
|
||||||
|
smmu_domain->cfg.asid += cs->id_base;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct arm_smmu_impl cavium_impl = {
|
||||||
|
.cfg_probe = cavium_cfg_probe,
|
||||||
|
.init_context = cavium_init_context,
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct arm_smmu_device *cavium_smmu_impl_init(struct arm_smmu_device *smmu)
|
||||||
|
{
|
||||||
|
struct cavium_smmu *cs;
|
||||||
|
|
||||||
|
cs = devm_kzalloc(smmu->dev, sizeof(*cs), GFP_KERNEL);
|
||||||
|
if (!cs)
|
||||||
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
|
cs->smmu = *smmu;
|
||||||
|
cs->smmu.impl = &cavium_impl;
|
||||||
|
|
||||||
|
devm_kfree(smmu->dev, smmu);
|
||||||
|
|
||||||
|
return &cs->smmu;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define ARM_MMU500_ACTLR_CPRE (1 << 1)
|
||||||
|
|
||||||
|
#define ARM_MMU500_ACR_CACHE_LOCK (1 << 26)
|
||||||
|
#define ARM_MMU500_ACR_S2CRB_TLBEN (1 << 10)
|
||||||
|
#define ARM_MMU500_ACR_SMTNMB_TLBEN (1 << 8)
|
||||||
|
|
||||||
|
static int arm_mmu500_reset(struct arm_smmu_device *smmu)
|
||||||
|
{
|
||||||
|
u32 reg, major;
|
||||||
|
int i;
|
||||||
|
/*
|
||||||
|
* On MMU-500 r2p0 onwards we need to clear ACR.CACHE_LOCK before
|
||||||
|
* writes to the context bank ACTLRs will stick. And we just hope that
|
||||||
|
* Secure has also cleared SACR.CACHE_LOCK for this to take effect...
|
||||||
|
*/
|
||||||
|
reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_ID7);
|
||||||
|
major = FIELD_GET(ID7_MAJOR, reg);
|
||||||
|
reg = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sACR);
|
||||||
|
if (major >= 2)
|
||||||
|
reg &= ~ARM_MMU500_ACR_CACHE_LOCK;
|
||||||
|
/*
|
||||||
|
* Allow unmatched Stream IDs to allocate bypass
|
||||||
|
* TLB entries for reduced latency.
|
||||||
|
*/
|
||||||
|
reg |= ARM_MMU500_ACR_SMTNMB_TLBEN | ARM_MMU500_ACR_S2CRB_TLBEN;
|
||||||
|
arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sACR, reg);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Disable MMU-500's not-particularly-beneficial next-page
|
||||||
|
* prefetcher for the sake of errata #841119 and #826419.
|
||||||
|
*/
|
||||||
|
for (i = 0; i < smmu->num_context_banks; ++i) {
|
||||||
|
reg = arm_smmu_cb_read(smmu, i, ARM_SMMU_CB_ACTLR);
|
||||||
|
reg &= ~ARM_MMU500_ACTLR_CPRE;
|
||||||
|
arm_smmu_cb_write(smmu, i, ARM_SMMU_CB_ACTLR, reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct arm_smmu_impl arm_mmu500_impl = {
|
||||||
|
.reset = arm_mmu500_reset,
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We will inevitably have to combine model-specific implementation
|
||||||
|
* quirks with platform-specific integration quirks, but everything
|
||||||
|
* we currently support happens to work out as straightforward
|
||||||
|
* mutually-exclusive assignments.
|
||||||
|
*/
|
||||||
|
switch (smmu->model) {
|
||||||
|
case ARM_MMU500:
|
||||||
|
smmu->impl = &arm_mmu500_impl;
|
||||||
|
break;
|
||||||
|
case CAVIUM_SMMUV2:
|
||||||
|
return cavium_smmu_impl_init(smmu);
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (of_property_read_bool(smmu->dev->of_node,
|
||||||
|
"calxeda,smmu-secure-config-access"))
|
||||||
|
smmu->impl = &calxeda_impl;
|
||||||
|
|
||||||
|
return smmu;
|
||||||
|
}
|
@ -1,210 +0,0 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
|
||||||
/*
|
|
||||||
* IOMMU API for ARM architected SMMU implementations.
|
|
||||||
*
|
|
||||||
* Copyright (C) 2013 ARM Limited
|
|
||||||
*
|
|
||||||
* Author: Will Deacon <will.deacon@arm.com>
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _ARM_SMMU_REGS_H
|
|
||||||
#define _ARM_SMMU_REGS_H
|
|
||||||
|
|
||||||
/* Configuration registers */
|
|
||||||
#define ARM_SMMU_GR0_sCR0 0x0
|
|
||||||
#define sCR0_CLIENTPD (1 << 0)
|
|
||||||
#define sCR0_GFRE (1 << 1)
|
|
||||||
#define sCR0_GFIE (1 << 2)
|
|
||||||
#define sCR0_EXIDENABLE (1 << 3)
|
|
||||||
#define sCR0_GCFGFRE (1 << 4)
|
|
||||||
#define sCR0_GCFGFIE (1 << 5)
|
|
||||||
#define sCR0_USFCFG (1 << 10)
|
|
||||||
#define sCR0_VMIDPNE (1 << 11)
|
|
||||||
#define sCR0_PTM (1 << 12)
|
|
||||||
#define sCR0_FB (1 << 13)
|
|
||||||
#define sCR0_VMID16EN (1 << 31)
|
|
||||||
#define sCR0_BSU_SHIFT 14
|
|
||||||
#define sCR0_BSU_MASK 0x3
|
|
||||||
|
|
||||||
/* Auxiliary Configuration register */
|
|
||||||
#define ARM_SMMU_GR0_sACR 0x10
|
|
||||||
|
|
||||||
/* Identification registers */
|
|
||||||
#define ARM_SMMU_GR0_ID0 0x20
|
|
||||||
#define ARM_SMMU_GR0_ID1 0x24
|
|
||||||
#define ARM_SMMU_GR0_ID2 0x28
|
|
||||||
#define ARM_SMMU_GR0_ID3 0x2c
|
|
||||||
#define ARM_SMMU_GR0_ID4 0x30
|
|
||||||
#define ARM_SMMU_GR0_ID5 0x34
|
|
||||||
#define ARM_SMMU_GR0_ID6 0x38
|
|
||||||
#define ARM_SMMU_GR0_ID7 0x3c
|
|
||||||
#define ARM_SMMU_GR0_sGFSR 0x48
|
|
||||||
#define ARM_SMMU_GR0_sGFSYNR0 0x50
|
|
||||||
#define ARM_SMMU_GR0_sGFSYNR1 0x54
|
|
||||||
#define ARM_SMMU_GR0_sGFSYNR2 0x58
|
|
||||||
|
|
||||||
#define ID0_S1TS (1 << 30)
|
|
||||||
#define ID0_S2TS (1 << 29)
|
|
||||||
#define ID0_NTS (1 << 28)
|
|
||||||
#define ID0_SMS (1 << 27)
|
|
||||||
#define ID0_ATOSNS (1 << 26)
|
|
||||||
#define ID0_PTFS_NO_AARCH32 (1 << 25)
|
|
||||||
#define ID0_PTFS_NO_AARCH32S (1 << 24)
|
|
||||||
#define ID0_CTTW (1 << 14)
|
|
||||||
#define ID0_NUMIRPT_SHIFT 16
|
|
||||||
#define ID0_NUMIRPT_MASK 0xff
|
|
||||||
#define ID0_NUMSIDB_SHIFT 9
|
|
||||||
#define ID0_NUMSIDB_MASK 0xf
|
|
||||||
#define ID0_EXIDS (1 << 8)
|
|
||||||
#define ID0_NUMSMRG_SHIFT 0
|
|
||||||
#define ID0_NUMSMRG_MASK 0xff
|
|
||||||
|
|
||||||
#define ID1_PAGESIZE (1 << 31)
|
|
||||||
#define ID1_NUMPAGENDXB_SHIFT 28
|
|
||||||
#define ID1_NUMPAGENDXB_MASK 7
|
|
||||||
#define ID1_NUMS2CB_SHIFT 16
|
|
||||||
#define ID1_NUMS2CB_MASK 0xff
|
|
||||||
#define ID1_NUMCB_SHIFT 0
|
|
||||||
#define ID1_NUMCB_MASK 0xff
|
|
||||||
|
|
||||||
#define ID2_OAS_SHIFT 4
|
|
||||||
#define ID2_OAS_MASK 0xf
|
|
||||||
#define ID2_IAS_SHIFT 0
|
|
||||||
#define ID2_IAS_MASK 0xf
|
|
||||||
#define ID2_UBS_SHIFT 8
|
|
||||||
#define ID2_UBS_MASK 0xf
|
|
||||||
#define ID2_PTFS_4K (1 << 12)
|
|
||||||
#define ID2_PTFS_16K (1 << 13)
|
|
||||||
#define ID2_PTFS_64K (1 << 14)
|
|
||||||
#define ID2_VMID16 (1 << 15)
|
|
||||||
|
|
||||||
#define ID7_MAJOR_SHIFT 4
|
|
||||||
#define ID7_MAJOR_MASK 0xf
|
|
||||||
|
|
||||||
/* Global TLB invalidation */
|
|
||||||
#define ARM_SMMU_GR0_TLBIVMID 0x64
|
|
||||||
#define ARM_SMMU_GR0_TLBIALLNSNH 0x68
|
|
||||||
#define ARM_SMMU_GR0_TLBIALLH 0x6c
|
|
||||||
#define ARM_SMMU_GR0_sTLBGSYNC 0x70
|
|
||||||
#define ARM_SMMU_GR0_sTLBGSTATUS 0x74
|
|
||||||
#define sTLBGSTATUS_GSACTIVE (1 << 0)
|
|
||||||
|
|
||||||
/* Stream mapping registers */
|
|
||||||
#define ARM_SMMU_GR0_SMR(n) (0x800 + ((n) << 2))
|
|
||||||
#define SMR_VALID (1 << 31)
|
|
||||||
#define SMR_MASK_SHIFT 16
|
|
||||||
#define SMR_ID_SHIFT 0
|
|
||||||
|
|
||||||
#define ARM_SMMU_GR0_S2CR(n) (0xc00 + ((n) << 2))
|
|
||||||
#define S2CR_CBNDX_SHIFT 0
|
|
||||||
#define S2CR_CBNDX_MASK 0xff
|
|
||||||
#define S2CR_EXIDVALID (1 << 10)
|
|
||||||
#define S2CR_TYPE_SHIFT 16
|
|
||||||
#define S2CR_TYPE_MASK 0x3
|
|
||||||
enum arm_smmu_s2cr_type {
|
|
||||||
S2CR_TYPE_TRANS,
|
|
||||||
S2CR_TYPE_BYPASS,
|
|
||||||
S2CR_TYPE_FAULT,
|
|
||||||
};
|
|
||||||
|
|
||||||
#define S2CR_PRIVCFG_SHIFT 24
|
|
||||||
#define S2CR_PRIVCFG_MASK 0x3
|
|
||||||
enum arm_smmu_s2cr_privcfg {
|
|
||||||
S2CR_PRIVCFG_DEFAULT,
|
|
||||||
S2CR_PRIVCFG_DIPAN,
|
|
||||||
S2CR_PRIVCFG_UNPRIV,
|
|
||||||
S2CR_PRIVCFG_PRIV,
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Context bank attribute registers */
|
|
||||||
#define ARM_SMMU_GR1_CBAR(n) (0x0 + ((n) << 2))
|
|
||||||
#define CBAR_VMID_SHIFT 0
|
|
||||||
#define CBAR_VMID_MASK 0xff
|
|
||||||
#define CBAR_S1_BPSHCFG_SHIFT 8
|
|
||||||
#define CBAR_S1_BPSHCFG_MASK 3
|
|
||||||
#define CBAR_S1_BPSHCFG_NSH 3
|
|
||||||
#define CBAR_S1_MEMATTR_SHIFT 12
|
|
||||||
#define CBAR_S1_MEMATTR_MASK 0xf
|
|
||||||
#define CBAR_S1_MEMATTR_WB 0xf
|
|
||||||
#define CBAR_TYPE_SHIFT 16
|
|
||||||
#define CBAR_TYPE_MASK 0x3
|
|
||||||
#define CBAR_TYPE_S2_TRANS (0 << CBAR_TYPE_SHIFT)
|
|
||||||
#define CBAR_TYPE_S1_TRANS_S2_BYPASS (1 << CBAR_TYPE_SHIFT)
|
|
||||||
#define CBAR_TYPE_S1_TRANS_S2_FAULT (2 << CBAR_TYPE_SHIFT)
|
|
||||||
#define CBAR_TYPE_S1_TRANS_S2_TRANS (3 << CBAR_TYPE_SHIFT)
|
|
||||||
#define CBAR_IRPTNDX_SHIFT 24
|
|
||||||
#define CBAR_IRPTNDX_MASK 0xff
|
|
||||||
|
|
||||||
#define ARM_SMMU_GR1_CBFRSYNRA(n) (0x400 + ((n) << 2))
|
|
||||||
|
|
||||||
#define ARM_SMMU_GR1_CBA2R(n) (0x800 + ((n) << 2))
|
|
||||||
#define CBA2R_RW64_32BIT (0 << 0)
|
|
||||||
#define CBA2R_RW64_64BIT (1 << 0)
|
|
||||||
#define CBA2R_VMID_SHIFT 16
|
|
||||||
#define CBA2R_VMID_MASK 0xffff
|
|
||||||
|
|
||||||
#define ARM_SMMU_CB_SCTLR 0x0
|
|
||||||
#define ARM_SMMU_CB_ACTLR 0x4
|
|
||||||
#define ARM_SMMU_CB_RESUME 0x8
|
|
||||||
#define ARM_SMMU_CB_TTBCR2 0x10
|
|
||||||
#define ARM_SMMU_CB_TTBR0 0x20
|
|
||||||
#define ARM_SMMU_CB_TTBR1 0x28
|
|
||||||
#define ARM_SMMU_CB_TTBCR 0x30
|
|
||||||
#define ARM_SMMU_CB_CONTEXTIDR 0x34
|
|
||||||
#define ARM_SMMU_CB_S1_MAIR0 0x38
|
|
||||||
#define ARM_SMMU_CB_S1_MAIR1 0x3c
|
|
||||||
#define ARM_SMMU_CB_PAR 0x50
|
|
||||||
#define ARM_SMMU_CB_FSR 0x58
|
|
||||||
#define ARM_SMMU_CB_FAR 0x60
|
|
||||||
#define ARM_SMMU_CB_FSYNR0 0x68
|
|
||||||
#define ARM_SMMU_CB_S1_TLBIVA 0x600
|
|
||||||
#define ARM_SMMU_CB_S1_TLBIASID 0x610
|
|
||||||
#define ARM_SMMU_CB_S1_TLBIVAL 0x620
|
|
||||||
#define ARM_SMMU_CB_S2_TLBIIPAS2 0x630
|
|
||||||
#define ARM_SMMU_CB_S2_TLBIIPAS2L 0x638
|
|
||||||
#define ARM_SMMU_CB_TLBSYNC 0x7f0
|
|
||||||
#define ARM_SMMU_CB_TLBSTATUS 0x7f4
|
|
||||||
#define ARM_SMMU_CB_ATS1PR 0x800
|
|
||||||
#define ARM_SMMU_CB_ATSR 0x8f0
|
|
||||||
|
|
||||||
#define SCTLR_S1_ASIDPNE (1 << 12)
|
|
||||||
#define SCTLR_CFCFG (1 << 7)
|
|
||||||
#define SCTLR_CFIE (1 << 6)
|
|
||||||
#define SCTLR_CFRE (1 << 5)
|
|
||||||
#define SCTLR_E (1 << 4)
|
|
||||||
#define SCTLR_AFE (1 << 2)
|
|
||||||
#define SCTLR_TRE (1 << 1)
|
|
||||||
#define SCTLR_M (1 << 0)
|
|
||||||
|
|
||||||
#define CB_PAR_F (1 << 0)
|
|
||||||
|
|
||||||
#define ATSR_ACTIVE (1 << 0)
|
|
||||||
|
|
||||||
#define RESUME_RETRY (0 << 0)
|
|
||||||
#define RESUME_TERMINATE (1 << 0)
|
|
||||||
|
|
||||||
#define TTBCR2_SEP_SHIFT 15
|
|
||||||
#define TTBCR2_SEP_UPSTREAM (0x7 << TTBCR2_SEP_SHIFT)
|
|
||||||
#define TTBCR2_AS (1 << 4)
|
|
||||||
|
|
||||||
#define TTBRn_ASID_SHIFT 48
|
|
||||||
|
|
||||||
#define FSR_MULTI (1 << 31)
|
|
||||||
#define FSR_SS (1 << 30)
|
|
||||||
#define FSR_UUT (1 << 8)
|
|
||||||
#define FSR_ASF (1 << 7)
|
|
||||||
#define FSR_TLBLKF (1 << 6)
|
|
||||||
#define FSR_TLBMCF (1 << 5)
|
|
||||||
#define FSR_EF (1 << 4)
|
|
||||||
#define FSR_PF (1 << 3)
|
|
||||||
#define FSR_AFF (1 << 2)
|
|
||||||
#define FSR_TF (1 << 1)
|
|
||||||
|
|
||||||
#define FSR_IGN (FSR_AFF | FSR_ASF | \
|
|
||||||
FSR_TLBMCF | FSR_TLBLKF)
|
|
||||||
#define FSR_FAULT (FSR_MULTI | FSR_SS | FSR_UUT | \
|
|
||||||
FSR_EF | FSR_PF | FSR_TF | FSR_IGN)
|
|
||||||
|
|
||||||
#define FSYNR0_WNR (1 << 4)
|
|
||||||
|
|
||||||
#endif /* _ARM_SMMU_REGS_H */
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
402
drivers/iommu/arm-smmu.h
Normal file
402
drivers/iommu/arm-smmu.h
Normal file
@ -0,0 +1,402 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||||
|
/*
|
||||||
|
* IOMMU API for ARM architected SMMU implementations.
|
||||||
|
*
|
||||||
|
* Copyright (C) 2013 ARM Limited
|
||||||
|
*
|
||||||
|
* Author: Will Deacon <will.deacon@arm.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ARM_SMMU_H
|
||||||
|
#define _ARM_SMMU_H
|
||||||
|
|
||||||
|
#include <linux/atomic.h>
|
||||||
|
#include <linux/bits.h>
|
||||||
|
#include <linux/clk.h>
|
||||||
|
#include <linux/device.h>
|
||||||
|
#include <linux/io-64-nonatomic-hi-lo.h>
|
||||||
|
#include <linux/io-pgtable.h>
|
||||||
|
#include <linux/iommu.h>
|
||||||
|
#include <linux/mutex.h>
|
||||||
|
#include <linux/spinlock.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
/* Configuration registers */
|
||||||
|
#define ARM_SMMU_GR0_sCR0 0x0
|
||||||
|
#define sCR0_VMID16EN BIT(31)
|
||||||
|
#define sCR0_BSU GENMASK(15, 14)
|
||||||
|
#define sCR0_FB BIT(13)
|
||||||
|
#define sCR0_PTM BIT(12)
|
||||||
|
#define sCR0_VMIDPNE BIT(11)
|
||||||
|
#define sCR0_USFCFG BIT(10)
|
||||||
|
#define sCR0_GCFGFIE BIT(5)
|
||||||
|
#define sCR0_GCFGFRE BIT(4)
|
||||||
|
#define sCR0_EXIDENABLE BIT(3)
|
||||||
|
#define sCR0_GFIE BIT(2)
|
||||||
|
#define sCR0_GFRE BIT(1)
|
||||||
|
#define sCR0_CLIENTPD BIT(0)
|
||||||
|
|
||||||
|
/* Auxiliary Configuration register */
|
||||||
|
#define ARM_SMMU_GR0_sACR 0x10
|
||||||
|
|
||||||
|
/* Identification registers */
|
||||||
|
#define ARM_SMMU_GR0_ID0 0x20
|
||||||
|
#define ID0_S1TS BIT(30)
|
||||||
|
#define ID0_S2TS BIT(29)
|
||||||
|
#define ID0_NTS BIT(28)
|
||||||
|
#define ID0_SMS BIT(27)
|
||||||
|
#define ID0_ATOSNS BIT(26)
|
||||||
|
#define ID0_PTFS_NO_AARCH32 BIT(25)
|
||||||
|
#define ID0_PTFS_NO_AARCH32S BIT(24)
|
||||||
|
#define ID0_NUMIRPT GENMASK(23, 16)
|
||||||
|
#define ID0_CTTW BIT(14)
|
||||||
|
#define ID0_NUMSIDB GENMASK(12, 9)
|
||||||
|
#define ID0_EXIDS BIT(8)
|
||||||
|
#define ID0_NUMSMRG GENMASK(7, 0)
|
||||||
|
|
||||||
|
#define ARM_SMMU_GR0_ID1 0x24
|
||||||
|
#define ID1_PAGESIZE BIT(31)
|
||||||
|
#define ID1_NUMPAGENDXB GENMASK(30, 28)
|
||||||
|
#define ID1_NUMS2CB GENMASK(23, 16)
|
||||||
|
#define ID1_NUMCB GENMASK(7, 0)
|
||||||
|
|
||||||
|
#define ARM_SMMU_GR0_ID2 0x28
|
||||||
|
#define ID2_VMID16 BIT(15)
|
||||||
|
#define ID2_PTFS_64K BIT(14)
|
||||||
|
#define ID2_PTFS_16K BIT(13)
|
||||||
|
#define ID2_PTFS_4K BIT(12)
|
||||||
|
#define ID2_UBS GENMASK(11, 8)
|
||||||
|
#define ID2_OAS GENMASK(7, 4)
|
||||||
|
#define ID2_IAS GENMASK(3, 0)
|
||||||
|
|
||||||
|
#define ARM_SMMU_GR0_ID3 0x2c
|
||||||
|
#define ARM_SMMU_GR0_ID4 0x30
|
||||||
|
#define ARM_SMMU_GR0_ID5 0x34
|
||||||
|
#define ARM_SMMU_GR0_ID6 0x38
|
||||||
|
|
||||||
|
#define ARM_SMMU_GR0_ID7 0x3c
|
||||||
|
#define ID7_MAJOR GENMASK(7, 4)
|
||||||
|
#define ID7_MINOR GENMASK(3, 0)
|
||||||
|
|
||||||
|
#define ARM_SMMU_GR0_sGFSR 0x48
|
||||||
|
#define ARM_SMMU_GR0_sGFSYNR0 0x50
|
||||||
|
#define ARM_SMMU_GR0_sGFSYNR1 0x54
|
||||||
|
#define ARM_SMMU_GR0_sGFSYNR2 0x58
|
||||||
|
|
||||||
|
/* Global TLB invalidation */
|
||||||
|
#define ARM_SMMU_GR0_TLBIVMID 0x64
|
||||||
|
#define ARM_SMMU_GR0_TLBIALLNSNH 0x68
|
||||||
|
#define ARM_SMMU_GR0_TLBIALLH 0x6c
|
||||||
|
#define ARM_SMMU_GR0_sTLBGSYNC 0x70
|
||||||
|
|
||||||
|
#define ARM_SMMU_GR0_sTLBGSTATUS 0x74
|
||||||
|
#define sTLBGSTATUS_GSACTIVE BIT(0)
|
||||||
|
|
||||||
|
/* Stream mapping registers */
|
||||||
|
#define ARM_SMMU_GR0_SMR(n) (0x800 + ((n) << 2))
|
||||||
|
#define SMR_VALID BIT(31)
|
||||||
|
#define SMR_MASK GENMASK(31, 16)
|
||||||
|
#define SMR_ID GENMASK(15, 0)
|
||||||
|
|
||||||
|
#define ARM_SMMU_GR0_S2CR(n) (0xc00 + ((n) << 2))
|
||||||
|
#define S2CR_PRIVCFG GENMASK(25, 24)
|
||||||
|
enum arm_smmu_s2cr_privcfg {
|
||||||
|
S2CR_PRIVCFG_DEFAULT,
|
||||||
|
S2CR_PRIVCFG_DIPAN,
|
||||||
|
S2CR_PRIVCFG_UNPRIV,
|
||||||
|
S2CR_PRIVCFG_PRIV,
|
||||||
|
};
|
||||||
|
#define S2CR_TYPE GENMASK(17, 16)
|
||||||
|
enum arm_smmu_s2cr_type {
|
||||||
|
S2CR_TYPE_TRANS,
|
||||||
|
S2CR_TYPE_BYPASS,
|
||||||
|
S2CR_TYPE_FAULT,
|
||||||
|
};
|
||||||
|
#define S2CR_EXIDVALID BIT(10)
|
||||||
|
#define S2CR_CBNDX GENMASK(7, 0)
|
||||||
|
|
||||||
|
/* Context bank attribute registers */
|
||||||
|
#define ARM_SMMU_GR1_CBAR(n) (0x0 + ((n) << 2))
|
||||||
|
#define CBAR_IRPTNDX GENMASK(31, 24)
|
||||||
|
#define CBAR_TYPE GENMASK(17, 16)
|
||||||
|
enum arm_smmu_cbar_type {
|
||||||
|
CBAR_TYPE_S2_TRANS,
|
||||||
|
CBAR_TYPE_S1_TRANS_S2_BYPASS,
|
||||||
|
CBAR_TYPE_S1_TRANS_S2_FAULT,
|
||||||
|
CBAR_TYPE_S1_TRANS_S2_TRANS,
|
||||||
|
};
|
||||||
|
#define CBAR_S1_MEMATTR GENMASK(15, 12)
|
||||||
|
#define CBAR_S1_MEMATTR_WB 0xf
|
||||||
|
#define CBAR_S1_BPSHCFG GENMASK(9, 8)
|
||||||
|
#define CBAR_S1_BPSHCFG_NSH 3
|
||||||
|
#define CBAR_VMID GENMASK(7, 0)
|
||||||
|
|
||||||
|
#define ARM_SMMU_GR1_CBFRSYNRA(n) (0x400 + ((n) << 2))
|
||||||
|
|
||||||
|
#define ARM_SMMU_GR1_CBA2R(n) (0x800 + ((n) << 2))
|
||||||
|
#define CBA2R_VMID16 GENMASK(31, 16)
|
||||||
|
#define CBA2R_VA64 BIT(0)
|
||||||
|
|
||||||
|
#define ARM_SMMU_CB_SCTLR 0x0
|
||||||
|
#define SCTLR_S1_ASIDPNE BIT(12)
|
||||||
|
#define SCTLR_CFCFG BIT(7)
|
||||||
|
#define SCTLR_CFIE BIT(6)
|
||||||
|
#define SCTLR_CFRE BIT(5)
|
||||||
|
#define SCTLR_E BIT(4)
|
||||||
|
#define SCTLR_AFE BIT(2)
|
||||||
|
#define SCTLR_TRE BIT(1)
|
||||||
|
#define SCTLR_M BIT(0)
|
||||||
|
|
||||||
|
#define ARM_SMMU_CB_ACTLR 0x4
|
||||||
|
|
||||||
|
#define ARM_SMMU_CB_RESUME 0x8
|
||||||
|
#define RESUME_TERMINATE BIT(0)
|
||||||
|
|
||||||
|
#define ARM_SMMU_CB_TCR2 0x10
|
||||||
|
#define TCR2_SEP GENMASK(17, 15)
|
||||||
|
#define TCR2_SEP_UPSTREAM 0x7
|
||||||
|
#define TCR2_AS BIT(4)
|
||||||
|
|
||||||
|
#define ARM_SMMU_CB_TTBR0 0x20
|
||||||
|
#define ARM_SMMU_CB_TTBR1 0x28
|
||||||
|
#define TTBRn_ASID GENMASK_ULL(63, 48)
|
||||||
|
|
||||||
|
#define ARM_SMMU_CB_TCR 0x30
|
||||||
|
#define ARM_SMMU_CB_CONTEXTIDR 0x34
|
||||||
|
#define ARM_SMMU_CB_S1_MAIR0 0x38
|
||||||
|
#define ARM_SMMU_CB_S1_MAIR1 0x3c
|
||||||
|
|
||||||
|
#define ARM_SMMU_CB_PAR 0x50
|
||||||
|
#define CB_PAR_F BIT(0)
|
||||||
|
|
||||||
|
#define ARM_SMMU_CB_FSR 0x58
|
||||||
|
#define FSR_MULTI BIT(31)
|
||||||
|
#define FSR_SS BIT(30)
|
||||||
|
#define FSR_UUT BIT(8)
|
||||||
|
#define FSR_ASF BIT(7)
|
||||||
|
#define FSR_TLBLKF BIT(6)
|
||||||
|
#define FSR_TLBMCF BIT(5)
|
||||||
|
#define FSR_EF BIT(4)
|
||||||
|
#define FSR_PF BIT(3)
|
||||||
|
#define FSR_AFF BIT(2)
|
||||||
|
#define FSR_TF BIT(1)
|
||||||
|
|
||||||
|
#define FSR_IGN (FSR_AFF | FSR_ASF | \
|
||||||
|
FSR_TLBMCF | FSR_TLBLKF)
|
||||||
|
#define FSR_FAULT (FSR_MULTI | FSR_SS | FSR_UUT | \
|
||||||
|
FSR_EF | FSR_PF | FSR_TF | FSR_IGN)
|
||||||
|
|
||||||
|
#define ARM_SMMU_CB_FAR 0x60
|
||||||
|
|
||||||
|
#define ARM_SMMU_CB_FSYNR0 0x68
|
||||||
|
#define FSYNR0_WNR BIT(4)
|
||||||
|
|
||||||
|
#define ARM_SMMU_CB_S1_TLBIVA 0x600
|
||||||
|
#define ARM_SMMU_CB_S1_TLBIASID 0x610
|
||||||
|
#define ARM_SMMU_CB_S1_TLBIVAL 0x620
|
||||||
|
#define ARM_SMMU_CB_S2_TLBIIPAS2 0x630
|
||||||
|
#define ARM_SMMU_CB_S2_TLBIIPAS2L 0x638
|
||||||
|
#define ARM_SMMU_CB_TLBSYNC 0x7f0
|
||||||
|
#define ARM_SMMU_CB_TLBSTATUS 0x7f4
|
||||||
|
#define ARM_SMMU_CB_ATS1PR 0x800
|
||||||
|
|
||||||
|
#define ARM_SMMU_CB_ATSR 0x8f0
|
||||||
|
#define ATSR_ACTIVE BIT(0)
|
||||||
|
|
||||||
|
|
||||||
|
/* Maximum number of context banks per SMMU */
|
||||||
|
#define ARM_SMMU_MAX_CBS 128
|
||||||
|
|
||||||
|
|
||||||
|
/* Shared driver definitions */
|
||||||
|
enum arm_smmu_arch_version {
|
||||||
|
ARM_SMMU_V1,
|
||||||
|
ARM_SMMU_V1_64K,
|
||||||
|
ARM_SMMU_V2,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum arm_smmu_implementation {
|
||||||
|
GENERIC_SMMU,
|
||||||
|
ARM_MMU500,
|
||||||
|
CAVIUM_SMMUV2,
|
||||||
|
QCOM_SMMUV2,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct arm_smmu_device {
|
||||||
|
struct device *dev;
|
||||||
|
|
||||||
|
void __iomem *base;
|
||||||
|
unsigned int numpage;
|
||||||
|
unsigned int pgshift;
|
||||||
|
|
||||||
|
#define ARM_SMMU_FEAT_COHERENT_WALK (1 << 0)
|
||||||
|
#define ARM_SMMU_FEAT_STREAM_MATCH (1 << 1)
|
||||||
|
#define ARM_SMMU_FEAT_TRANS_S1 (1 << 2)
|
||||||
|
#define ARM_SMMU_FEAT_TRANS_S2 (1 << 3)
|
||||||
|
#define ARM_SMMU_FEAT_TRANS_NESTED (1 << 4)
|
||||||
|
#define ARM_SMMU_FEAT_TRANS_OPS (1 << 5)
|
||||||
|
#define ARM_SMMU_FEAT_VMID16 (1 << 6)
|
||||||
|
#define ARM_SMMU_FEAT_FMT_AARCH64_4K (1 << 7)
|
||||||
|
#define ARM_SMMU_FEAT_FMT_AARCH64_16K (1 << 8)
|
||||||
|
#define ARM_SMMU_FEAT_FMT_AARCH64_64K (1 << 9)
|
||||||
|
#define ARM_SMMU_FEAT_FMT_AARCH32_L (1 << 10)
|
||||||
|
#define ARM_SMMU_FEAT_FMT_AARCH32_S (1 << 11)
|
||||||
|
#define ARM_SMMU_FEAT_EXIDS (1 << 12)
|
||||||
|
u32 features;
|
||||||
|
|
||||||
|
enum arm_smmu_arch_version version;
|
||||||
|
enum arm_smmu_implementation model;
|
||||||
|
const struct arm_smmu_impl *impl;
|
||||||
|
|
||||||
|
u32 num_context_banks;
|
||||||
|
u32 num_s2_context_banks;
|
||||||
|
DECLARE_BITMAP(context_map, ARM_SMMU_MAX_CBS);
|
||||||
|
struct arm_smmu_cb *cbs;
|
||||||
|
atomic_t irptndx;
|
||||||
|
|
||||||
|
u32 num_mapping_groups;
|
||||||
|
u16 streamid_mask;
|
||||||
|
u16 smr_mask_mask;
|
||||||
|
struct arm_smmu_smr *smrs;
|
||||||
|
struct arm_smmu_s2cr *s2crs;
|
||||||
|
struct mutex stream_map_mutex;
|
||||||
|
|
||||||
|
unsigned long va_size;
|
||||||
|
unsigned long ipa_size;
|
||||||
|
unsigned long pa_size;
|
||||||
|
unsigned long pgsize_bitmap;
|
||||||
|
|
||||||
|
u32 num_global_irqs;
|
||||||
|
u32 num_context_irqs;
|
||||||
|
unsigned int *irqs;
|
||||||
|
struct clk_bulk_data *clks;
|
||||||
|
int num_clks;
|
||||||
|
|
||||||
|
spinlock_t global_sync_lock;
|
||||||
|
|
||||||
|
/* IOMMU core code handle */
|
||||||
|
struct iommu_device iommu;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum arm_smmu_context_fmt {
|
||||||
|
ARM_SMMU_CTX_FMT_NONE,
|
||||||
|
ARM_SMMU_CTX_FMT_AARCH64,
|
||||||
|
ARM_SMMU_CTX_FMT_AARCH32_L,
|
||||||
|
ARM_SMMU_CTX_FMT_AARCH32_S,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct arm_smmu_cfg {
|
||||||
|
u8 cbndx;
|
||||||
|
u8 irptndx;
|
||||||
|
union {
|
||||||
|
u16 asid;
|
||||||
|
u16 vmid;
|
||||||
|
};
|
||||||
|
enum arm_smmu_cbar_type cbar;
|
||||||
|
enum arm_smmu_context_fmt fmt;
|
||||||
|
};
|
||||||
|
#define INVALID_IRPTNDX 0xff
|
||||||
|
|
||||||
|
enum arm_smmu_domain_stage {
|
||||||
|
ARM_SMMU_DOMAIN_S1 = 0,
|
||||||
|
ARM_SMMU_DOMAIN_S2,
|
||||||
|
ARM_SMMU_DOMAIN_NESTED,
|
||||||
|
ARM_SMMU_DOMAIN_BYPASS,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct arm_smmu_flush_ops {
|
||||||
|
struct iommu_flush_ops tlb;
|
||||||
|
void (*tlb_inv_range)(unsigned long iova, size_t size, size_t granule,
|
||||||
|
bool leaf, void *cookie);
|
||||||
|
void (*tlb_sync)(void *cookie);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct arm_smmu_domain {
|
||||||
|
struct arm_smmu_device *smmu;
|
||||||
|
struct io_pgtable_ops *pgtbl_ops;
|
||||||
|
const struct arm_smmu_flush_ops *flush_ops;
|
||||||
|
struct arm_smmu_cfg cfg;
|
||||||
|
enum arm_smmu_domain_stage stage;
|
||||||
|
bool non_strict;
|
||||||
|
struct mutex init_mutex; /* Protects smmu pointer */
|
||||||
|
spinlock_t cb_lock; /* Serialises ATS1* ops and TLB syncs */
|
||||||
|
struct iommu_domain domain;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/* Implementation details, yay! */
|
||||||
|
struct arm_smmu_impl {
|
||||||
|
u32 (*read_reg)(struct arm_smmu_device *smmu, int page, int offset);
|
||||||
|
void (*write_reg)(struct arm_smmu_device *smmu, int page, int offset,
|
||||||
|
u32 val);
|
||||||
|
u64 (*read_reg64)(struct arm_smmu_device *smmu, int page, int offset);
|
||||||
|
void (*write_reg64)(struct arm_smmu_device *smmu, int page, int offset,
|
||||||
|
u64 val);
|
||||||
|
int (*cfg_probe)(struct arm_smmu_device *smmu);
|
||||||
|
int (*reset)(struct arm_smmu_device *smmu);
|
||||||
|
int (*init_context)(struct arm_smmu_domain *smmu_domain);
|
||||||
|
};
|
||||||
|
|
||||||
|
static inline void __iomem *arm_smmu_page(struct arm_smmu_device *smmu, int n)
|
||||||
|
{
|
||||||
|
return smmu->base + (n << smmu->pgshift);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u32 arm_smmu_readl(struct arm_smmu_device *smmu, int page, int offset)
|
||||||
|
{
|
||||||
|
if (smmu->impl && unlikely(smmu->impl->read_reg))
|
||||||
|
return smmu->impl->read_reg(smmu, page, offset);
|
||||||
|
return readl_relaxed(arm_smmu_page(smmu, page) + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void arm_smmu_writel(struct arm_smmu_device *smmu, int page,
|
||||||
|
int offset, u32 val)
|
||||||
|
{
|
||||||
|
if (smmu->impl && unlikely(smmu->impl->write_reg))
|
||||||
|
smmu->impl->write_reg(smmu, page, offset, val);
|
||||||
|
else
|
||||||
|
writel_relaxed(val, arm_smmu_page(smmu, page) + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline u64 arm_smmu_readq(struct arm_smmu_device *smmu, int page, int offset)
|
||||||
|
{
|
||||||
|
if (smmu->impl && unlikely(smmu->impl->read_reg64))
|
||||||
|
return smmu->impl->read_reg64(smmu, page, offset);
|
||||||
|
return readq_relaxed(arm_smmu_page(smmu, page) + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void arm_smmu_writeq(struct arm_smmu_device *smmu, int page,
|
||||||
|
int offset, u64 val)
|
||||||
|
{
|
||||||
|
if (smmu->impl && unlikely(smmu->impl->write_reg64))
|
||||||
|
smmu->impl->write_reg64(smmu, page, offset, val);
|
||||||
|
else
|
||||||
|
writeq_relaxed(val, arm_smmu_page(smmu, page) + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ARM_SMMU_GR0 0
|
||||||
|
#define ARM_SMMU_GR1 1
|
||||||
|
#define ARM_SMMU_CB(s, n) ((s)->numpage + (n))
|
||||||
|
|
||||||
|
#define arm_smmu_gr0_read(s, o) \
|
||||||
|
arm_smmu_readl((s), ARM_SMMU_GR0, (o))
|
||||||
|
#define arm_smmu_gr0_write(s, o, v) \
|
||||||
|
arm_smmu_writel((s), ARM_SMMU_GR0, (o), (v))
|
||||||
|
|
||||||
|
#define arm_smmu_gr1_read(s, o) \
|
||||||
|
arm_smmu_readl((s), ARM_SMMU_GR1, (o))
|
||||||
|
#define arm_smmu_gr1_write(s, o, v) \
|
||||||
|
arm_smmu_writel((s), ARM_SMMU_GR1, (o), (v))
|
||||||
|
|
||||||
|
#define arm_smmu_cb_read(s, n, o) \
|
||||||
|
arm_smmu_readl((s), ARM_SMMU_CB((s), (n)), (o))
|
||||||
|
#define arm_smmu_cb_write(s, n, o, v) \
|
||||||
|
arm_smmu_writel((s), ARM_SMMU_CB((s), (n)), (o), (v))
|
||||||
|
#define arm_smmu_cb_readq(s, n, o) \
|
||||||
|
arm_smmu_readq((s), ARM_SMMU_CB((s), (n)), (o))
|
||||||
|
#define arm_smmu_cb_writeq(s, n, o, v) \
|
||||||
|
arm_smmu_writeq((s), ARM_SMMU_CB((s), (n)), (o), (v))
|
||||||
|
|
||||||
|
struct arm_smmu_device *arm_smmu_impl_init(struct arm_smmu_device *smmu);
|
||||||
|
|
||||||
|
#endif /* _ARM_SMMU_H */
|
@ -303,13 +303,15 @@ static int iommu_dma_init_domain(struct iommu_domain *domain, dma_addr_t base,
|
|||||||
u64 size, struct device *dev)
|
u64 size, struct device *dev)
|
||||||
{
|
{
|
||||||
struct iommu_dma_cookie *cookie = domain->iova_cookie;
|
struct iommu_dma_cookie *cookie = domain->iova_cookie;
|
||||||
struct iova_domain *iovad = &cookie->iovad;
|
|
||||||
unsigned long order, base_pfn;
|
unsigned long order, base_pfn;
|
||||||
|
struct iova_domain *iovad;
|
||||||
int attr;
|
int attr;
|
||||||
|
|
||||||
if (!cookie || cookie->type != IOMMU_DMA_IOVA_COOKIE)
|
if (!cookie || cookie->type != IOMMU_DMA_IOVA_COOKIE)
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
iovad = &cookie->iovad;
|
||||||
|
|
||||||
/* Use the smallest supported page size for IOVA granularity */
|
/* Use the smallest supported page size for IOVA granularity */
|
||||||
order = __ffs(domain->pgsize_bitmap);
|
order = __ffs(domain->pgsize_bitmap);
|
||||||
base_pfn = max_t(unsigned long, 1, base >> order);
|
base_pfn = max_t(unsigned long, 1, base >> order);
|
||||||
@ -444,13 +446,18 @@ static void __iommu_dma_unmap(struct device *dev, dma_addr_t dma_addr,
|
|||||||
struct iommu_dma_cookie *cookie = domain->iova_cookie;
|
struct iommu_dma_cookie *cookie = domain->iova_cookie;
|
||||||
struct iova_domain *iovad = &cookie->iovad;
|
struct iova_domain *iovad = &cookie->iovad;
|
||||||
size_t iova_off = iova_offset(iovad, dma_addr);
|
size_t iova_off = iova_offset(iovad, dma_addr);
|
||||||
|
struct iommu_iotlb_gather iotlb_gather;
|
||||||
|
size_t unmapped;
|
||||||
|
|
||||||
dma_addr -= iova_off;
|
dma_addr -= iova_off;
|
||||||
size = iova_align(iovad, size + iova_off);
|
size = iova_align(iovad, size + iova_off);
|
||||||
|
iommu_iotlb_gather_init(&iotlb_gather);
|
||||||
|
|
||||||
|
unmapped = iommu_unmap_fast(domain, dma_addr, size, &iotlb_gather);
|
||||||
|
WARN_ON(unmapped != size);
|
||||||
|
|
||||||
WARN_ON(iommu_unmap_fast(domain, dma_addr, size) != size);
|
|
||||||
if (!cookie->fq_domain)
|
if (!cookie->fq_domain)
|
||||||
iommu_tlb_sync(domain);
|
iommu_tlb_sync(domain, &iotlb_gather);
|
||||||
iommu_dma_free_iova(cookie, dma_addr, size);
|
iommu_dma_free_iova(cookie, dma_addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1519,6 +1519,64 @@ static const char *dma_remap_fault_reasons[] =
|
|||||||
"PCE for translation request specifies blocking",
|
"PCE for translation request specifies blocking",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const char * const dma_remap_sm_fault_reasons[] = {
|
||||||
|
"SM: Invalid Root Table Address",
|
||||||
|
"SM: TTM 0 for request with PASID",
|
||||||
|
"SM: TTM 0 for page group request",
|
||||||
|
"Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x33-0x37 */
|
||||||
|
"SM: Error attempting to access Root Entry",
|
||||||
|
"SM: Present bit in Root Entry is clear",
|
||||||
|
"SM: Non-zero reserved field set in Root Entry",
|
||||||
|
"Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x3B-0x3F */
|
||||||
|
"SM: Error attempting to access Context Entry",
|
||||||
|
"SM: Present bit in Context Entry is clear",
|
||||||
|
"SM: Non-zero reserved field set in the Context Entry",
|
||||||
|
"SM: Invalid Context Entry",
|
||||||
|
"SM: DTE field in Context Entry is clear",
|
||||||
|
"SM: PASID Enable field in Context Entry is clear",
|
||||||
|
"SM: PASID is larger than the max in Context Entry",
|
||||||
|
"SM: PRE field in Context-Entry is clear",
|
||||||
|
"SM: RID_PASID field error in Context-Entry",
|
||||||
|
"Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x49-0x4F */
|
||||||
|
"SM: Error attempting to access the PASID Directory Entry",
|
||||||
|
"SM: Present bit in Directory Entry is clear",
|
||||||
|
"SM: Non-zero reserved field set in PASID Directory Entry",
|
||||||
|
"Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x53-0x57 */
|
||||||
|
"SM: Error attempting to access PASID Table Entry",
|
||||||
|
"SM: Present bit in PASID Table Entry is clear",
|
||||||
|
"SM: Non-zero reserved field set in PASID Table Entry",
|
||||||
|
"SM: Invalid Scalable-Mode PASID Table Entry",
|
||||||
|
"SM: ERE field is clear in PASID Table Entry",
|
||||||
|
"SM: SRE field is clear in PASID Table Entry",
|
||||||
|
"Unknown", "Unknown",/* 0x5E-0x5F */
|
||||||
|
"Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x60-0x67 */
|
||||||
|
"Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x68-0x6F */
|
||||||
|
"SM: Error attempting to access first-level paging entry",
|
||||||
|
"SM: Present bit in first-level paging entry is clear",
|
||||||
|
"SM: Non-zero reserved field set in first-level paging entry",
|
||||||
|
"SM: Error attempting to access FL-PML4 entry",
|
||||||
|
"SM: First-level entry address beyond MGAW in Nested translation",
|
||||||
|
"SM: Read permission error in FL-PML4 entry in Nested translation",
|
||||||
|
"SM: Read permission error in first-level paging entry in Nested translation",
|
||||||
|
"SM: Write permission error in first-level paging entry in Nested translation",
|
||||||
|
"SM: Error attempting to access second-level paging entry",
|
||||||
|
"SM: Read/Write permission error in second-level paging entry",
|
||||||
|
"SM: Non-zero reserved field set in second-level paging entry",
|
||||||
|
"SM: Invalid second-level page table pointer",
|
||||||
|
"SM: A/D bit update needed in second-level entry when set up in no snoop",
|
||||||
|
"Unknown", "Unknown", "Unknown", /* 0x7D-0x7F */
|
||||||
|
"SM: Address in first-level translation is not canonical",
|
||||||
|
"SM: U/S set 0 for first-level translation with user privilege",
|
||||||
|
"SM: No execute permission for request with PASID and ER=1",
|
||||||
|
"SM: Address beyond the DMA hardware max",
|
||||||
|
"SM: Second-level entry address beyond the max",
|
||||||
|
"SM: No write permission for Write/AtomicOp request",
|
||||||
|
"SM: No read permission for Read/AtomicOp request",
|
||||||
|
"SM: Invalid address-interrupt address",
|
||||||
|
"Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", "Unknown", /* 0x88-0x8F */
|
||||||
|
"SM: A/D bit update needed in first-level entry when set up in no snoop",
|
||||||
|
};
|
||||||
|
|
||||||
static const char *irq_remap_fault_reasons[] =
|
static const char *irq_remap_fault_reasons[] =
|
||||||
{
|
{
|
||||||
"Detected reserved fields in the decoded interrupt-remapped request",
|
"Detected reserved fields in the decoded interrupt-remapped request",
|
||||||
@ -1536,6 +1594,10 @@ static const char *dmar_get_fault_reason(u8 fault_reason, int *fault_type)
|
|||||||
ARRAY_SIZE(irq_remap_fault_reasons))) {
|
ARRAY_SIZE(irq_remap_fault_reasons))) {
|
||||||
*fault_type = INTR_REMAP;
|
*fault_type = INTR_REMAP;
|
||||||
return irq_remap_fault_reasons[fault_reason - 0x20];
|
return irq_remap_fault_reasons[fault_reason - 0x20];
|
||||||
|
} else if (fault_reason >= 0x30 && (fault_reason - 0x30 <
|
||||||
|
ARRAY_SIZE(dma_remap_sm_fault_reasons))) {
|
||||||
|
*fault_type = DMA_REMAP;
|
||||||
|
return dma_remap_sm_fault_reasons[fault_reason - 0x30];
|
||||||
} else if (fault_reason < ARRAY_SIZE(dma_remap_fault_reasons)) {
|
} else if (fault_reason < ARRAY_SIZE(dma_remap_fault_reasons)) {
|
||||||
*fault_type = DMA_REMAP;
|
*fault_type = DMA_REMAP;
|
||||||
return dma_remap_fault_reasons[fault_reason];
|
return dma_remap_fault_reasons[fault_reason];
|
||||||
@ -1611,7 +1673,8 @@ void dmar_msi_read(int irq, struct msi_msg *msg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static int dmar_fault_do_one(struct intel_iommu *iommu, int type,
|
static int dmar_fault_do_one(struct intel_iommu *iommu, int type,
|
||||||
u8 fault_reason, u16 source_id, unsigned long long addr)
|
u8 fault_reason, int pasid, u16 source_id,
|
||||||
|
unsigned long long addr)
|
||||||
{
|
{
|
||||||
const char *reason;
|
const char *reason;
|
||||||
int fault_type;
|
int fault_type;
|
||||||
@ -1624,10 +1687,11 @@ static int dmar_fault_do_one(struct intel_iommu *iommu, int type,
|
|||||||
PCI_FUNC(source_id & 0xFF), addr >> 48,
|
PCI_FUNC(source_id & 0xFF), addr >> 48,
|
||||||
fault_reason, reason);
|
fault_reason, reason);
|
||||||
else
|
else
|
||||||
pr_err("[%s] Request device [%02x:%02x.%d] fault addr %llx [fault reason %02d] %s\n",
|
pr_err("[%s] Request device [%02x:%02x.%d] PASID %x fault addr %llx [fault reason %02d] %s\n",
|
||||||
type ? "DMA Read" : "DMA Write",
|
type ? "DMA Read" : "DMA Write",
|
||||||
source_id >> 8, PCI_SLOT(source_id & 0xFF),
|
source_id >> 8, PCI_SLOT(source_id & 0xFF),
|
||||||
PCI_FUNC(source_id & 0xFF), addr, fault_reason, reason);
|
PCI_FUNC(source_id & 0xFF), pasid, addr,
|
||||||
|
fault_reason, reason);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1659,8 +1723,9 @@ irqreturn_t dmar_fault(int irq, void *dev_id)
|
|||||||
u8 fault_reason;
|
u8 fault_reason;
|
||||||
u16 source_id;
|
u16 source_id;
|
||||||
u64 guest_addr;
|
u64 guest_addr;
|
||||||
int type;
|
int type, pasid;
|
||||||
u32 data;
|
u32 data;
|
||||||
|
bool pasid_present;
|
||||||
|
|
||||||
/* highest 32 bits */
|
/* highest 32 bits */
|
||||||
data = readl(iommu->reg + reg +
|
data = readl(iommu->reg + reg +
|
||||||
@ -1672,10 +1737,12 @@ irqreturn_t dmar_fault(int irq, void *dev_id)
|
|||||||
fault_reason = dma_frcd_fault_reason(data);
|
fault_reason = dma_frcd_fault_reason(data);
|
||||||
type = dma_frcd_type(data);
|
type = dma_frcd_type(data);
|
||||||
|
|
||||||
|
pasid = dma_frcd_pasid_value(data);
|
||||||
data = readl(iommu->reg + reg +
|
data = readl(iommu->reg + reg +
|
||||||
fault_index * PRIMARY_FAULT_REG_LEN + 8);
|
fault_index * PRIMARY_FAULT_REG_LEN + 8);
|
||||||
source_id = dma_frcd_source_id(data);
|
source_id = dma_frcd_source_id(data);
|
||||||
|
|
||||||
|
pasid_present = dma_frcd_pasid_present(data);
|
||||||
guest_addr = dmar_readq(iommu->reg + reg +
|
guest_addr = dmar_readq(iommu->reg + reg +
|
||||||
fault_index * PRIMARY_FAULT_REG_LEN);
|
fault_index * PRIMARY_FAULT_REG_LEN);
|
||||||
guest_addr = dma_frcd_page_addr(guest_addr);
|
guest_addr = dma_frcd_page_addr(guest_addr);
|
||||||
@ -1688,7 +1755,9 @@ irqreturn_t dmar_fault(int irq, void *dev_id)
|
|||||||
raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
|
raw_spin_unlock_irqrestore(&iommu->register_lock, flag);
|
||||||
|
|
||||||
if (!ratelimited)
|
if (!ratelimited)
|
||||||
|
/* Using pasid -1 if pasid is not present */
|
||||||
dmar_fault_do_one(iommu, type, fault_reason,
|
dmar_fault_do_one(iommu, type, fault_reason,
|
||||||
|
pasid_present ? pasid : -1,
|
||||||
source_id, guest_addr);
|
source_id, guest_addr);
|
||||||
|
|
||||||
fault_index++;
|
fault_index++;
|
||||||
|
@ -566,7 +566,7 @@ static void sysmmu_tlb_invalidate_entry(struct sysmmu_drvdata *data,
|
|||||||
|
|
||||||
static const struct iommu_ops exynos_iommu_ops;
|
static const struct iommu_ops exynos_iommu_ops;
|
||||||
|
|
||||||
static int __init exynos_sysmmu_probe(struct platform_device *pdev)
|
static int exynos_sysmmu_probe(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
int irq, ret;
|
int irq, ret;
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
@ -583,10 +583,8 @@ static int __init exynos_sysmmu_probe(struct platform_device *pdev)
|
|||||||
return PTR_ERR(data->sfrbase);
|
return PTR_ERR(data->sfrbase);
|
||||||
|
|
||||||
irq = platform_get_irq(pdev, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
if (irq <= 0) {
|
if (irq <= 0)
|
||||||
dev_err(dev, "Unable to find IRQ resource\n");
|
|
||||||
return irq;
|
return irq;
|
||||||
}
|
|
||||||
|
|
||||||
ret = devm_request_irq(dev, irq, exynos_sysmmu_irq, 0,
|
ret = devm_request_irq(dev, irq, exynos_sysmmu_irq, 0,
|
||||||
dev_name(dev), data);
|
dev_name(dev), data);
|
||||||
@ -1130,7 +1128,8 @@ static void exynos_iommu_tlb_invalidate_entry(struct exynos_iommu_domain *domain
|
|||||||
}
|
}
|
||||||
|
|
||||||
static size_t exynos_iommu_unmap(struct iommu_domain *iommu_domain,
|
static size_t exynos_iommu_unmap(struct iommu_domain *iommu_domain,
|
||||||
unsigned long l_iova, size_t size)
|
unsigned long l_iova, size_t size,
|
||||||
|
struct iommu_iotlb_gather *gather)
|
||||||
{
|
{
|
||||||
struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain);
|
struct exynos_iommu_domain *domain = to_exynos_domain(iommu_domain);
|
||||||
sysmmu_iova_t iova = (sysmmu_iova_t)l_iova;
|
sysmmu_iova_t iova = (sysmmu_iova_t)l_iova;
|
||||||
|
@ -41,9 +41,11 @@
|
|||||||
#include <linux/dma-direct.h>
|
#include <linux/dma-direct.h>
|
||||||
#include <linux/crash_dump.h>
|
#include <linux/crash_dump.h>
|
||||||
#include <linux/numa.h>
|
#include <linux/numa.h>
|
||||||
|
#include <linux/swiotlb.h>
|
||||||
#include <asm/irq_remapping.h>
|
#include <asm/irq_remapping.h>
|
||||||
#include <asm/cacheflush.h>
|
#include <asm/cacheflush.h>
|
||||||
#include <asm/iommu.h>
|
#include <asm/iommu.h>
|
||||||
|
#include <trace/events/intel_iommu.h>
|
||||||
|
|
||||||
#include "irq_remapping.h"
|
#include "irq_remapping.h"
|
||||||
#include "intel-pasid.h"
|
#include "intel-pasid.h"
|
||||||
@ -346,6 +348,8 @@ static int domain_detach_iommu(struct dmar_domain *domain,
|
|||||||
static bool device_is_rmrr_locked(struct device *dev);
|
static bool device_is_rmrr_locked(struct device *dev);
|
||||||
static int intel_iommu_attach_device(struct iommu_domain *domain,
|
static int intel_iommu_attach_device(struct iommu_domain *domain,
|
||||||
struct device *dev);
|
struct device *dev);
|
||||||
|
static phys_addr_t intel_iommu_iova_to_phys(struct iommu_domain *domain,
|
||||||
|
dma_addr_t iova);
|
||||||
|
|
||||||
#ifdef CONFIG_INTEL_IOMMU_DEFAULT_ON
|
#ifdef CONFIG_INTEL_IOMMU_DEFAULT_ON
|
||||||
int dmar_disabled = 0;
|
int dmar_disabled = 0;
|
||||||
@ -362,6 +366,7 @@ static int dmar_forcedac;
|
|||||||
static int intel_iommu_strict;
|
static int intel_iommu_strict;
|
||||||
static int intel_iommu_superpage = 1;
|
static int intel_iommu_superpage = 1;
|
||||||
static int iommu_identity_mapping;
|
static int iommu_identity_mapping;
|
||||||
|
static int intel_no_bounce;
|
||||||
|
|
||||||
#define IDENTMAP_ALL 1
|
#define IDENTMAP_ALL 1
|
||||||
#define IDENTMAP_GFX 2
|
#define IDENTMAP_GFX 2
|
||||||
@ -375,6 +380,9 @@ EXPORT_SYMBOL_GPL(intel_iommu_gfx_mapped);
|
|||||||
static DEFINE_SPINLOCK(device_domain_lock);
|
static DEFINE_SPINLOCK(device_domain_lock);
|
||||||
static LIST_HEAD(device_domain_list);
|
static LIST_HEAD(device_domain_list);
|
||||||
|
|
||||||
|
#define device_needs_bounce(d) (!intel_no_bounce && dev_is_pci(d) && \
|
||||||
|
to_pci_dev(d)->untrusted)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Iterate over elements in device_domain_list and call the specified
|
* Iterate over elements in device_domain_list and call the specified
|
||||||
* callback @fn against each element.
|
* callback @fn against each element.
|
||||||
@ -457,6 +465,9 @@ static int __init intel_iommu_setup(char *str)
|
|||||||
printk(KERN_INFO
|
printk(KERN_INFO
|
||||||
"Intel-IOMMU: not forcing on after tboot. This could expose security risk for tboot\n");
|
"Intel-IOMMU: not forcing on after tboot. This could expose security risk for tboot\n");
|
||||||
intel_iommu_tboot_noforce = 1;
|
intel_iommu_tboot_noforce = 1;
|
||||||
|
} else if (!strncmp(str, "nobounce", 8)) {
|
||||||
|
pr_info("Intel-IOMMU: No bounce buffer. This could expose security risks of DMA attacks\n");
|
||||||
|
intel_no_bounce = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
str += strcspn(str, ",");
|
str += strcspn(str, ",");
|
||||||
@ -3296,7 +3307,7 @@ static int __init init_dmars(void)
|
|||||||
iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
|
iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (iommu_pass_through)
|
if (iommu_default_passthrough())
|
||||||
iommu_identity_mapping |= IDENTMAP_ALL;
|
iommu_identity_mapping |= IDENTMAP_ALL;
|
||||||
|
|
||||||
#ifdef CONFIG_INTEL_IOMMU_BROKEN_GFX_WA
|
#ifdef CONFIG_INTEL_IOMMU_BROKEN_GFX_WA
|
||||||
@ -3534,6 +3545,9 @@ static dma_addr_t __intel_map_single(struct device *dev, phys_addr_t paddr,
|
|||||||
|
|
||||||
start_paddr = (phys_addr_t)iova_pfn << PAGE_SHIFT;
|
start_paddr = (phys_addr_t)iova_pfn << PAGE_SHIFT;
|
||||||
start_paddr += paddr & ~PAGE_MASK;
|
start_paddr += paddr & ~PAGE_MASK;
|
||||||
|
|
||||||
|
trace_map_single(dev, start_paddr, paddr, size << VTD_PAGE_SHIFT);
|
||||||
|
|
||||||
return start_paddr;
|
return start_paddr;
|
||||||
|
|
||||||
error:
|
error:
|
||||||
@ -3589,10 +3603,7 @@ static void intel_unmap(struct device *dev, dma_addr_t dev_addr, size_t size)
|
|||||||
if (dev_is_pci(dev))
|
if (dev_is_pci(dev))
|
||||||
pdev = to_pci_dev(dev);
|
pdev = to_pci_dev(dev);
|
||||||
|
|
||||||
dev_dbg(dev, "Device unmapping: pfn %lx-%lx\n", start_pfn, last_pfn);
|
|
||||||
|
|
||||||
freelist = domain_unmap(domain, start_pfn, last_pfn);
|
freelist = domain_unmap(domain, start_pfn, last_pfn);
|
||||||
|
|
||||||
if (intel_iommu_strict || (pdev && pdev->untrusted) ||
|
if (intel_iommu_strict || (pdev && pdev->untrusted) ||
|
||||||
!has_iova_flush_queue(&domain->iovad)) {
|
!has_iova_flush_queue(&domain->iovad)) {
|
||||||
iommu_flush_iotlb_psi(iommu, domain, start_pfn,
|
iommu_flush_iotlb_psi(iommu, domain, start_pfn,
|
||||||
@ -3608,6 +3619,8 @@ static void intel_unmap(struct device *dev, dma_addr_t dev_addr, size_t size)
|
|||||||
* cpu used up by the iotlb flush operation...
|
* cpu used up by the iotlb flush operation...
|
||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trace_unmap_single(dev, dev_addr, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void intel_unmap_page(struct device *dev, dma_addr_t dev_addr,
|
static void intel_unmap_page(struct device *dev, dma_addr_t dev_addr,
|
||||||
@ -3698,6 +3711,8 @@ static void intel_unmap_sg(struct device *dev, struct scatterlist *sglist,
|
|||||||
}
|
}
|
||||||
|
|
||||||
intel_unmap(dev, startaddr, nrpages << VTD_PAGE_SHIFT);
|
intel_unmap(dev, startaddr, nrpages << VTD_PAGE_SHIFT);
|
||||||
|
|
||||||
|
trace_unmap_sg(dev, startaddr, nrpages << VTD_PAGE_SHIFT);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int intel_map_sg(struct device *dev, struct scatterlist *sglist, int nelems,
|
static int intel_map_sg(struct device *dev, struct scatterlist *sglist, int nelems,
|
||||||
@ -3754,6 +3769,9 @@ static int intel_map_sg(struct device *dev, struct scatterlist *sglist, int nele
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trace_map_sg(dev, iova_pfn << PAGE_SHIFT,
|
||||||
|
sg_phys(sglist), size << VTD_PAGE_SHIFT);
|
||||||
|
|
||||||
return nelems;
|
return nelems;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3769,6 +3787,252 @@ static const struct dma_map_ops intel_dma_ops = {
|
|||||||
.dma_supported = dma_direct_supported,
|
.dma_supported = dma_direct_supported,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
bounce_sync_single(struct device *dev, dma_addr_t addr, size_t size,
|
||||||
|
enum dma_data_direction dir, enum dma_sync_target target)
|
||||||
|
{
|
||||||
|
struct dmar_domain *domain;
|
||||||
|
phys_addr_t tlb_addr;
|
||||||
|
|
||||||
|
domain = find_domain(dev);
|
||||||
|
if (WARN_ON(!domain))
|
||||||
|
return;
|
||||||
|
|
||||||
|
tlb_addr = intel_iommu_iova_to_phys(&domain->domain, addr);
|
||||||
|
if (is_swiotlb_buffer(tlb_addr))
|
||||||
|
swiotlb_tbl_sync_single(dev, tlb_addr, size, dir, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
static dma_addr_t
|
||||||
|
bounce_map_single(struct device *dev, phys_addr_t paddr, size_t size,
|
||||||
|
enum dma_data_direction dir, unsigned long attrs,
|
||||||
|
u64 dma_mask)
|
||||||
|
{
|
||||||
|
size_t aligned_size = ALIGN(size, VTD_PAGE_SIZE);
|
||||||
|
struct dmar_domain *domain;
|
||||||
|
struct intel_iommu *iommu;
|
||||||
|
unsigned long iova_pfn;
|
||||||
|
unsigned long nrpages;
|
||||||
|
phys_addr_t tlb_addr;
|
||||||
|
int prot = 0;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
domain = find_domain(dev);
|
||||||
|
if (WARN_ON(dir == DMA_NONE || !domain))
|
||||||
|
return DMA_MAPPING_ERROR;
|
||||||
|
|
||||||
|
iommu = domain_get_iommu(domain);
|
||||||
|
if (WARN_ON(!iommu))
|
||||||
|
return DMA_MAPPING_ERROR;
|
||||||
|
|
||||||
|
nrpages = aligned_nrpages(0, size);
|
||||||
|
iova_pfn = intel_alloc_iova(dev, domain,
|
||||||
|
dma_to_mm_pfn(nrpages), dma_mask);
|
||||||
|
if (!iova_pfn)
|
||||||
|
return DMA_MAPPING_ERROR;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Check if DMAR supports zero-length reads on write only
|
||||||
|
* mappings..
|
||||||
|
*/
|
||||||
|
if (dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL ||
|
||||||
|
!cap_zlr(iommu->cap))
|
||||||
|
prot |= DMA_PTE_READ;
|
||||||
|
if (dir == DMA_FROM_DEVICE || dir == DMA_BIDIRECTIONAL)
|
||||||
|
prot |= DMA_PTE_WRITE;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If both the physical buffer start address and size are
|
||||||
|
* page aligned, we don't need to use a bounce page.
|
||||||
|
*/
|
||||||
|
if (!IS_ALIGNED(paddr | size, VTD_PAGE_SIZE)) {
|
||||||
|
tlb_addr = swiotlb_tbl_map_single(dev,
|
||||||
|
__phys_to_dma(dev, io_tlb_start),
|
||||||
|
paddr, size, aligned_size, dir, attrs);
|
||||||
|
if (tlb_addr == DMA_MAPPING_ERROR) {
|
||||||
|
goto swiotlb_error;
|
||||||
|
} else {
|
||||||
|
/* Cleanup the padding area. */
|
||||||
|
void *padding_start = phys_to_virt(tlb_addr);
|
||||||
|
size_t padding_size = aligned_size;
|
||||||
|
|
||||||
|
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
|
||||||
|
(dir == DMA_TO_DEVICE ||
|
||||||
|
dir == DMA_BIDIRECTIONAL)) {
|
||||||
|
padding_start += size;
|
||||||
|
padding_size -= size;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(padding_start, 0, padding_size);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
tlb_addr = paddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = domain_pfn_mapping(domain, mm_to_dma_pfn(iova_pfn),
|
||||||
|
tlb_addr >> VTD_PAGE_SHIFT, nrpages, prot);
|
||||||
|
if (ret)
|
||||||
|
goto mapping_error;
|
||||||
|
|
||||||
|
trace_bounce_map_single(dev, iova_pfn << PAGE_SHIFT, paddr, size);
|
||||||
|
|
||||||
|
return (phys_addr_t)iova_pfn << PAGE_SHIFT;
|
||||||
|
|
||||||
|
mapping_error:
|
||||||
|
if (is_swiotlb_buffer(tlb_addr))
|
||||||
|
swiotlb_tbl_unmap_single(dev, tlb_addr, size,
|
||||||
|
aligned_size, dir, attrs);
|
||||||
|
swiotlb_error:
|
||||||
|
free_iova_fast(&domain->iovad, iova_pfn, dma_to_mm_pfn(nrpages));
|
||||||
|
dev_err(dev, "Device bounce map: %zx@%llx dir %d --- failed\n",
|
||||||
|
size, (unsigned long long)paddr, dir);
|
||||||
|
|
||||||
|
return DMA_MAPPING_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
bounce_unmap_single(struct device *dev, dma_addr_t dev_addr, size_t size,
|
||||||
|
enum dma_data_direction dir, unsigned long attrs)
|
||||||
|
{
|
||||||
|
size_t aligned_size = ALIGN(size, VTD_PAGE_SIZE);
|
||||||
|
struct dmar_domain *domain;
|
||||||
|
phys_addr_t tlb_addr;
|
||||||
|
|
||||||
|
domain = find_domain(dev);
|
||||||
|
if (WARN_ON(!domain))
|
||||||
|
return;
|
||||||
|
|
||||||
|
tlb_addr = intel_iommu_iova_to_phys(&domain->domain, dev_addr);
|
||||||
|
if (WARN_ON(!tlb_addr))
|
||||||
|
return;
|
||||||
|
|
||||||
|
intel_unmap(dev, dev_addr, size);
|
||||||
|
if (is_swiotlb_buffer(tlb_addr))
|
||||||
|
swiotlb_tbl_unmap_single(dev, tlb_addr, size,
|
||||||
|
aligned_size, dir, attrs);
|
||||||
|
|
||||||
|
trace_bounce_unmap_single(dev, dev_addr, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static dma_addr_t
|
||||||
|
bounce_map_page(struct device *dev, struct page *page, unsigned long offset,
|
||||||
|
size_t size, enum dma_data_direction dir, unsigned long attrs)
|
||||||
|
{
|
||||||
|
return bounce_map_single(dev, page_to_phys(page) + offset,
|
||||||
|
size, dir, attrs, *dev->dma_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static dma_addr_t
|
||||||
|
bounce_map_resource(struct device *dev, phys_addr_t phys_addr, size_t size,
|
||||||
|
enum dma_data_direction dir, unsigned long attrs)
|
||||||
|
{
|
||||||
|
return bounce_map_single(dev, phys_addr, size,
|
||||||
|
dir, attrs, *dev->dma_mask);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
bounce_unmap_page(struct device *dev, dma_addr_t dev_addr, size_t size,
|
||||||
|
enum dma_data_direction dir, unsigned long attrs)
|
||||||
|
{
|
||||||
|
bounce_unmap_single(dev, dev_addr, size, dir, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
bounce_unmap_resource(struct device *dev, dma_addr_t dev_addr, size_t size,
|
||||||
|
enum dma_data_direction dir, unsigned long attrs)
|
||||||
|
{
|
||||||
|
bounce_unmap_single(dev, dev_addr, size, dir, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
bounce_unmap_sg(struct device *dev, struct scatterlist *sglist, int nelems,
|
||||||
|
enum dma_data_direction dir, unsigned long attrs)
|
||||||
|
{
|
||||||
|
struct scatterlist *sg;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for_each_sg(sglist, sg, nelems, i)
|
||||||
|
bounce_unmap_page(dev, sg->dma_address,
|
||||||
|
sg_dma_len(sg), dir, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
bounce_map_sg(struct device *dev, struct scatterlist *sglist, int nelems,
|
||||||
|
enum dma_data_direction dir, unsigned long attrs)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
struct scatterlist *sg;
|
||||||
|
|
||||||
|
for_each_sg(sglist, sg, nelems, i) {
|
||||||
|
sg->dma_address = bounce_map_page(dev, sg_page(sg),
|
||||||
|
sg->offset, sg->length,
|
||||||
|
dir, attrs);
|
||||||
|
if (sg->dma_address == DMA_MAPPING_ERROR)
|
||||||
|
goto out_unmap;
|
||||||
|
sg_dma_len(sg) = sg->length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nelems;
|
||||||
|
|
||||||
|
out_unmap:
|
||||||
|
bounce_unmap_sg(dev, sglist, i, dir, attrs | DMA_ATTR_SKIP_CPU_SYNC);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
bounce_sync_single_for_cpu(struct device *dev, dma_addr_t addr,
|
||||||
|
size_t size, enum dma_data_direction dir)
|
||||||
|
{
|
||||||
|
bounce_sync_single(dev, addr, size, dir, SYNC_FOR_CPU);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
bounce_sync_single_for_device(struct device *dev, dma_addr_t addr,
|
||||||
|
size_t size, enum dma_data_direction dir)
|
||||||
|
{
|
||||||
|
bounce_sync_single(dev, addr, size, dir, SYNC_FOR_DEVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
bounce_sync_sg_for_cpu(struct device *dev, struct scatterlist *sglist,
|
||||||
|
int nelems, enum dma_data_direction dir)
|
||||||
|
{
|
||||||
|
struct scatterlist *sg;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for_each_sg(sglist, sg, nelems, i)
|
||||||
|
bounce_sync_single(dev, sg_dma_address(sg),
|
||||||
|
sg_dma_len(sg), dir, SYNC_FOR_CPU);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
bounce_sync_sg_for_device(struct device *dev, struct scatterlist *sglist,
|
||||||
|
int nelems, enum dma_data_direction dir)
|
||||||
|
{
|
||||||
|
struct scatterlist *sg;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for_each_sg(sglist, sg, nelems, i)
|
||||||
|
bounce_sync_single(dev, sg_dma_address(sg),
|
||||||
|
sg_dma_len(sg), dir, SYNC_FOR_DEVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dma_map_ops bounce_dma_ops = {
|
||||||
|
.alloc = intel_alloc_coherent,
|
||||||
|
.free = intel_free_coherent,
|
||||||
|
.map_sg = bounce_map_sg,
|
||||||
|
.unmap_sg = bounce_unmap_sg,
|
||||||
|
.map_page = bounce_map_page,
|
||||||
|
.unmap_page = bounce_unmap_page,
|
||||||
|
.sync_single_for_cpu = bounce_sync_single_for_cpu,
|
||||||
|
.sync_single_for_device = bounce_sync_single_for_device,
|
||||||
|
.sync_sg_for_cpu = bounce_sync_sg_for_cpu,
|
||||||
|
.sync_sg_for_device = bounce_sync_sg_for_device,
|
||||||
|
.map_resource = bounce_map_resource,
|
||||||
|
.unmap_resource = bounce_unmap_resource,
|
||||||
|
.dma_supported = dma_direct_supported,
|
||||||
|
};
|
||||||
|
|
||||||
static inline int iommu_domain_cache_init(void)
|
static inline int iommu_domain_cache_init(void)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
@ -4569,22 +4833,20 @@ const struct attribute_group *intel_iommu_groups[] = {
|
|||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int __init platform_optin_force_iommu(void)
|
static inline bool has_untrusted_dev(void)
|
||||||
{
|
{
|
||||||
struct pci_dev *pdev = NULL;
|
struct pci_dev *pdev = NULL;
|
||||||
bool has_untrusted_dev = false;
|
|
||||||
|
|
||||||
if (!dmar_platform_optin() || no_platform_optin)
|
for_each_pci_dev(pdev)
|
||||||
return 0;
|
if (pdev->untrusted)
|
||||||
|
return true;
|
||||||
|
|
||||||
for_each_pci_dev(pdev) {
|
return false;
|
||||||
if (pdev->untrusted) {
|
}
|
||||||
has_untrusted_dev = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!has_untrusted_dev)
|
static int __init platform_optin_force_iommu(void)
|
||||||
|
{
|
||||||
|
if (!dmar_platform_optin() || no_platform_optin || !has_untrusted_dev())
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (no_iommu || dmar_disabled)
|
if (no_iommu || dmar_disabled)
|
||||||
@ -4598,9 +4860,6 @@ static int __init platform_optin_force_iommu(void)
|
|||||||
iommu_identity_mapping |= IDENTMAP_ALL;
|
iommu_identity_mapping |= IDENTMAP_ALL;
|
||||||
|
|
||||||
dmar_disabled = 0;
|
dmar_disabled = 0;
|
||||||
#if defined(CONFIG_X86) && defined(CONFIG_SWIOTLB)
|
|
||||||
swiotlb = 0;
|
|
||||||
#endif
|
|
||||||
no_iommu = 0;
|
no_iommu = 0;
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
@ -4740,7 +4999,14 @@ int __init intel_iommu_init(void)
|
|||||||
up_write(&dmar_global_lock);
|
up_write(&dmar_global_lock);
|
||||||
|
|
||||||
#if defined(CONFIG_X86) && defined(CONFIG_SWIOTLB)
|
#if defined(CONFIG_X86) && defined(CONFIG_SWIOTLB)
|
||||||
swiotlb = 0;
|
/*
|
||||||
|
* If the system has no untrusted device or the user has decided
|
||||||
|
* to disable the bounce page mechanisms, we don't need swiotlb.
|
||||||
|
* Mark this and the pre-allocated bounce pages will be released
|
||||||
|
* later.
|
||||||
|
*/
|
||||||
|
if (!has_untrusted_dev() || intel_no_bounce)
|
||||||
|
swiotlb = 0;
|
||||||
#endif
|
#endif
|
||||||
dma_ops = &intel_dma_ops;
|
dma_ops = &intel_dma_ops;
|
||||||
|
|
||||||
@ -5204,7 +5470,8 @@ static int intel_iommu_map(struct iommu_domain *domain,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static size_t intel_iommu_unmap(struct iommu_domain *domain,
|
static size_t intel_iommu_unmap(struct iommu_domain *domain,
|
||||||
unsigned long iova, size_t size)
|
unsigned long iova, size_t size,
|
||||||
|
struct iommu_iotlb_gather *gather)
|
||||||
{
|
{
|
||||||
struct dmar_domain *dmar_domain = to_dmar_domain(domain);
|
struct dmar_domain *dmar_domain = to_dmar_domain(domain);
|
||||||
struct page *freelist = NULL;
|
struct page *freelist = NULL;
|
||||||
@ -5360,6 +5627,11 @@ static int intel_iommu_add_device(struct device *dev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (device_needs_bounce(dev)) {
|
||||||
|
dev_info(dev, "Use Intel IOMMU bounce page dma_ops\n");
|
||||||
|
set_dma_ops(dev, &bounce_dma_ops);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5377,6 +5649,9 @@ static void intel_iommu_remove_device(struct device *dev)
|
|||||||
iommu_group_remove_device(dev);
|
iommu_group_remove_device(dev);
|
||||||
|
|
||||||
iommu_device_unlink(&iommu->iommu, dev);
|
iommu_device_unlink(&iommu->iommu, dev);
|
||||||
|
|
||||||
|
if (device_needs_bounce(dev))
|
||||||
|
set_dma_ops(dev, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void intel_iommu_get_resv_regions(struct device *device,
|
static void intel_iommu_get_resv_regions(struct device *device,
|
||||||
@ -5690,20 +5965,46 @@ const struct iommu_ops intel_iommu_ops = {
|
|||||||
.pgsize_bitmap = INTEL_IOMMU_PGSIZES,
|
.pgsize_bitmap = INTEL_IOMMU_PGSIZES,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void quirk_iommu_g4x_gfx(struct pci_dev *dev)
|
static void quirk_iommu_igfx(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
/* G4x/GM45 integrated gfx dmar support is totally busted. */
|
|
||||||
pci_info(dev, "Disabling IOMMU for graphics on this chipset\n");
|
pci_info(dev, "Disabling IOMMU for graphics on this chipset\n");
|
||||||
dmar_map_gfx = 0;
|
dmar_map_gfx = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_g4x_gfx);
|
/* G4x/GM45 integrated gfx dmar support is totally busted. */
|
||||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e00, quirk_iommu_g4x_gfx);
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2a40, quirk_iommu_igfx);
|
||||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e10, quirk_iommu_g4x_gfx);
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e00, quirk_iommu_igfx);
|
||||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e20, quirk_iommu_g4x_gfx);
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e10, quirk_iommu_igfx);
|
||||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e30, quirk_iommu_g4x_gfx);
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e20, quirk_iommu_igfx);
|
||||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e40, quirk_iommu_g4x_gfx);
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e30, quirk_iommu_igfx);
|
||||||
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e90, quirk_iommu_g4x_gfx);
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e40, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x2e90, quirk_iommu_igfx);
|
||||||
|
|
||||||
|
/* Broadwell igfx malfunctions with dmar */
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1606, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x160B, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x160E, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1602, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x160A, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x160D, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1616, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x161B, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x161E, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1612, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x161A, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x161D, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1626, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x162B, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x162E, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1622, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x162A, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x162D, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1636, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163B, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163E, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x1632, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163A, quirk_iommu_igfx);
|
||||||
|
DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x163D, quirk_iommu_igfx);
|
||||||
|
|
||||||
static void quirk_iommu_rwbf(struct pci_dev *dev)
|
static void quirk_iommu_rwbf(struct pci_dev *dev)
|
||||||
{
|
{
|
||||||
|
14
drivers/iommu/intel-trace.c
Normal file
14
drivers/iommu/intel-trace.c
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Intel IOMMU trace support
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019 Intel Corporation
|
||||||
|
*
|
||||||
|
* Author: Lu Baolu <baolu.lu@linux.intel.com>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/string.h>
|
||||||
|
#include <linux/types.h>
|
||||||
|
|
||||||
|
#define CREATE_TRACE_POINTS
|
||||||
|
#include <trace/events/intel_iommu.h>
|
@ -376,13 +376,13 @@ static int set_msi_sid_cb(struct pci_dev *pdev, u16 alias, void *opaque)
|
|||||||
{
|
{
|
||||||
struct set_msi_sid_data *data = opaque;
|
struct set_msi_sid_data *data = opaque;
|
||||||
|
|
||||||
|
if (data->count == 0 || PCI_BUS_NUM(alias) == PCI_BUS_NUM(data->alias))
|
||||||
|
data->busmatch_count++;
|
||||||
|
|
||||||
data->pdev = pdev;
|
data->pdev = pdev;
|
||||||
data->alias = alias;
|
data->alias = alias;
|
||||||
data->count++;
|
data->count++;
|
||||||
|
|
||||||
if (PCI_BUS_NUM(alias) == pdev->bus->number)
|
|
||||||
data->busmatch_count++;
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +112,9 @@
|
|||||||
#define ARM_V7S_TEX_MASK 0x7
|
#define ARM_V7S_TEX_MASK 0x7
|
||||||
#define ARM_V7S_ATTR_TEX(val) (((val) & ARM_V7S_TEX_MASK) << ARM_V7S_TEX_SHIFT)
|
#define ARM_V7S_ATTR_TEX(val) (((val) & ARM_V7S_TEX_MASK) << ARM_V7S_TEX_SHIFT)
|
||||||
|
|
||||||
#define ARM_V7S_ATTR_MTK_4GB BIT(9) /* MTK extend it for 4GB mode */
|
/* MediaTek extend the two bits for PA 32bit/33bit */
|
||||||
|
#define ARM_V7S_ATTR_MTK_PA_BIT32 BIT(9)
|
||||||
|
#define ARM_V7S_ATTR_MTK_PA_BIT33 BIT(4)
|
||||||
|
|
||||||
/* *well, except for TEX on level 2 large pages, of course :( */
|
/* *well, except for TEX on level 2 large pages, of course :( */
|
||||||
#define ARM_V7S_CONT_PAGE_TEX_SHIFT 6
|
#define ARM_V7S_CONT_PAGE_TEX_SHIFT 6
|
||||||
@ -169,18 +171,62 @@ struct arm_v7s_io_pgtable {
|
|||||||
spinlock_t split_lock;
|
spinlock_t split_lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool arm_v7s_pte_is_cont(arm_v7s_iopte pte, int lvl);
|
||||||
|
|
||||||
static dma_addr_t __arm_v7s_dma_addr(void *pages)
|
static dma_addr_t __arm_v7s_dma_addr(void *pages)
|
||||||
{
|
{
|
||||||
return (dma_addr_t)virt_to_phys(pages);
|
return (dma_addr_t)virt_to_phys(pages);
|
||||||
}
|
}
|
||||||
|
|
||||||
static arm_v7s_iopte *iopte_deref(arm_v7s_iopte pte, int lvl)
|
static bool arm_v7s_is_mtk_enabled(struct io_pgtable_cfg *cfg)
|
||||||
{
|
{
|
||||||
|
return IS_ENABLED(CONFIG_PHYS_ADDR_T_64BIT) &&
|
||||||
|
(cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_EXT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static arm_v7s_iopte paddr_to_iopte(phys_addr_t paddr, int lvl,
|
||||||
|
struct io_pgtable_cfg *cfg)
|
||||||
|
{
|
||||||
|
arm_v7s_iopte pte = paddr & ARM_V7S_LVL_MASK(lvl);
|
||||||
|
|
||||||
|
if (!arm_v7s_is_mtk_enabled(cfg))
|
||||||
|
return pte;
|
||||||
|
|
||||||
|
if (paddr & BIT_ULL(32))
|
||||||
|
pte |= ARM_V7S_ATTR_MTK_PA_BIT32;
|
||||||
|
if (paddr & BIT_ULL(33))
|
||||||
|
pte |= ARM_V7S_ATTR_MTK_PA_BIT33;
|
||||||
|
return pte;
|
||||||
|
}
|
||||||
|
|
||||||
|
static phys_addr_t iopte_to_paddr(arm_v7s_iopte pte, int lvl,
|
||||||
|
struct io_pgtable_cfg *cfg)
|
||||||
|
{
|
||||||
|
arm_v7s_iopte mask;
|
||||||
|
phys_addr_t paddr;
|
||||||
|
|
||||||
if (ARM_V7S_PTE_IS_TABLE(pte, lvl))
|
if (ARM_V7S_PTE_IS_TABLE(pte, lvl))
|
||||||
pte &= ARM_V7S_TABLE_MASK;
|
mask = ARM_V7S_TABLE_MASK;
|
||||||
|
else if (arm_v7s_pte_is_cont(pte, lvl))
|
||||||
|
mask = ARM_V7S_LVL_MASK(lvl) * ARM_V7S_CONT_PAGES;
|
||||||
else
|
else
|
||||||
pte &= ARM_V7S_LVL_MASK(lvl);
|
mask = ARM_V7S_LVL_MASK(lvl);
|
||||||
return phys_to_virt(pte);
|
|
||||||
|
paddr = pte & mask;
|
||||||
|
if (!arm_v7s_is_mtk_enabled(cfg))
|
||||||
|
return paddr;
|
||||||
|
|
||||||
|
if (pte & ARM_V7S_ATTR_MTK_PA_BIT32)
|
||||||
|
paddr |= BIT_ULL(32);
|
||||||
|
if (pte & ARM_V7S_ATTR_MTK_PA_BIT33)
|
||||||
|
paddr |= BIT_ULL(33);
|
||||||
|
return paddr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static arm_v7s_iopte *iopte_deref(arm_v7s_iopte pte, int lvl,
|
||||||
|
struct arm_v7s_io_pgtable *data)
|
||||||
|
{
|
||||||
|
return phys_to_virt(iopte_to_paddr(pte, lvl, &data->iop.cfg));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void *__arm_v7s_alloc_table(int lvl, gfp_t gfp,
|
static void *__arm_v7s_alloc_table(int lvl, gfp_t gfp,
|
||||||
@ -295,9 +341,6 @@ static arm_v7s_iopte arm_v7s_prot_to_pte(int prot, int lvl,
|
|||||||
if (lvl == 1 && (cfg->quirks & IO_PGTABLE_QUIRK_ARM_NS))
|
if (lvl == 1 && (cfg->quirks & IO_PGTABLE_QUIRK_ARM_NS))
|
||||||
pte |= ARM_V7S_ATTR_NS_SECTION;
|
pte |= ARM_V7S_ATTR_NS_SECTION;
|
||||||
|
|
||||||
if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_4GB)
|
|
||||||
pte |= ARM_V7S_ATTR_MTK_4GB;
|
|
||||||
|
|
||||||
return pte;
|
return pte;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -362,7 +405,8 @@ static bool arm_v7s_pte_is_cont(arm_v7s_iopte pte, int lvl)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t __arm_v7s_unmap(struct arm_v7s_io_pgtable *, unsigned long,
|
static size_t __arm_v7s_unmap(struct arm_v7s_io_pgtable *,
|
||||||
|
struct iommu_iotlb_gather *, unsigned long,
|
||||||
size_t, int, arm_v7s_iopte *);
|
size_t, int, arm_v7s_iopte *);
|
||||||
|
|
||||||
static int arm_v7s_init_pte(struct arm_v7s_io_pgtable *data,
|
static int arm_v7s_init_pte(struct arm_v7s_io_pgtable *data,
|
||||||
@ -383,7 +427,7 @@ static int arm_v7s_init_pte(struct arm_v7s_io_pgtable *data,
|
|||||||
size_t sz = ARM_V7S_BLOCK_SIZE(lvl);
|
size_t sz = ARM_V7S_BLOCK_SIZE(lvl);
|
||||||
|
|
||||||
tblp = ptep - ARM_V7S_LVL_IDX(iova, lvl);
|
tblp = ptep - ARM_V7S_LVL_IDX(iova, lvl);
|
||||||
if (WARN_ON(__arm_v7s_unmap(data, iova + i * sz,
|
if (WARN_ON(__arm_v7s_unmap(data, NULL, iova + i * sz,
|
||||||
sz, lvl, tblp) != sz))
|
sz, lvl, tblp) != sz))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
} else if (ptep[i]) {
|
} else if (ptep[i]) {
|
||||||
@ -396,7 +440,7 @@ static int arm_v7s_init_pte(struct arm_v7s_io_pgtable *data,
|
|||||||
if (num_entries > 1)
|
if (num_entries > 1)
|
||||||
pte = arm_v7s_pte_to_cont(pte, lvl);
|
pte = arm_v7s_pte_to_cont(pte, lvl);
|
||||||
|
|
||||||
pte |= paddr & ARM_V7S_LVL_MASK(lvl);
|
pte |= paddr_to_iopte(paddr, lvl, cfg);
|
||||||
|
|
||||||
__arm_v7s_set_pte(ptep, pte, num_entries, cfg);
|
__arm_v7s_set_pte(ptep, pte, num_entries, cfg);
|
||||||
return 0;
|
return 0;
|
||||||
@ -462,7 +506,7 @@ static int __arm_v7s_map(struct arm_v7s_io_pgtable *data, unsigned long iova,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (ARM_V7S_PTE_IS_TABLE(pte, lvl)) {
|
if (ARM_V7S_PTE_IS_TABLE(pte, lvl)) {
|
||||||
cptep = iopte_deref(pte, lvl);
|
cptep = iopte_deref(pte, lvl, data);
|
||||||
} else if (pte) {
|
} else if (pte) {
|
||||||
/* We require an unmap first */
|
/* We require an unmap first */
|
||||||
WARN_ON(!selftest_running);
|
WARN_ON(!selftest_running);
|
||||||
@ -484,7 +528,8 @@ static int arm_v7s_map(struct io_pgtable_ops *ops, unsigned long iova,
|
|||||||
if (!(prot & (IOMMU_READ | IOMMU_WRITE)))
|
if (!(prot & (IOMMU_READ | IOMMU_WRITE)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
if (WARN_ON(upper_32_bits(iova) || upper_32_bits(paddr)))
|
if (WARN_ON(iova >= (1ULL << data->iop.cfg.ias) ||
|
||||||
|
paddr >= (1ULL << data->iop.cfg.oas)))
|
||||||
return -ERANGE;
|
return -ERANGE;
|
||||||
|
|
||||||
ret = __arm_v7s_map(data, iova, paddr, size, prot, 1, data->pgd);
|
ret = __arm_v7s_map(data, iova, paddr, size, prot, 1, data->pgd);
|
||||||
@ -493,9 +538,8 @@ static int arm_v7s_map(struct io_pgtable_ops *ops, unsigned long iova,
|
|||||||
* a chance for anything to kick off a table walk for the new iova.
|
* a chance for anything to kick off a table walk for the new iova.
|
||||||
*/
|
*/
|
||||||
if (iop->cfg.quirks & IO_PGTABLE_QUIRK_TLBI_ON_MAP) {
|
if (iop->cfg.quirks & IO_PGTABLE_QUIRK_TLBI_ON_MAP) {
|
||||||
io_pgtable_tlb_add_flush(iop, iova, size,
|
io_pgtable_tlb_flush_walk(iop, iova, size,
|
||||||
ARM_V7S_BLOCK_SIZE(2), false);
|
ARM_V7S_BLOCK_SIZE(2));
|
||||||
io_pgtable_tlb_sync(iop);
|
|
||||||
} else {
|
} else {
|
||||||
wmb();
|
wmb();
|
||||||
}
|
}
|
||||||
@ -512,7 +556,8 @@ static void arm_v7s_free_pgtable(struct io_pgtable *iop)
|
|||||||
arm_v7s_iopte pte = data->pgd[i];
|
arm_v7s_iopte pte = data->pgd[i];
|
||||||
|
|
||||||
if (ARM_V7S_PTE_IS_TABLE(pte, 1))
|
if (ARM_V7S_PTE_IS_TABLE(pte, 1))
|
||||||
__arm_v7s_free_table(iopte_deref(pte, 1), 2, data);
|
__arm_v7s_free_table(iopte_deref(pte, 1, data),
|
||||||
|
2, data);
|
||||||
}
|
}
|
||||||
__arm_v7s_free_table(data->pgd, 1, data);
|
__arm_v7s_free_table(data->pgd, 1, data);
|
||||||
kmem_cache_destroy(data->l2_tables);
|
kmem_cache_destroy(data->l2_tables);
|
||||||
@ -541,12 +586,12 @@ static arm_v7s_iopte arm_v7s_split_cont(struct arm_v7s_io_pgtable *data,
|
|||||||
__arm_v7s_pte_sync(ptep, ARM_V7S_CONT_PAGES, &iop->cfg);
|
__arm_v7s_pte_sync(ptep, ARM_V7S_CONT_PAGES, &iop->cfg);
|
||||||
|
|
||||||
size *= ARM_V7S_CONT_PAGES;
|
size *= ARM_V7S_CONT_PAGES;
|
||||||
io_pgtable_tlb_add_flush(iop, iova, size, size, true);
|
io_pgtable_tlb_flush_leaf(iop, iova, size, size);
|
||||||
io_pgtable_tlb_sync(iop);
|
|
||||||
return pte;
|
return pte;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t arm_v7s_split_blk_unmap(struct arm_v7s_io_pgtable *data,
|
static size_t arm_v7s_split_blk_unmap(struct arm_v7s_io_pgtable *data,
|
||||||
|
struct iommu_iotlb_gather *gather,
|
||||||
unsigned long iova, size_t size,
|
unsigned long iova, size_t size,
|
||||||
arm_v7s_iopte blk_pte,
|
arm_v7s_iopte blk_pte,
|
||||||
arm_v7s_iopte *ptep)
|
arm_v7s_iopte *ptep)
|
||||||
@ -582,16 +627,16 @@ static size_t arm_v7s_split_blk_unmap(struct arm_v7s_io_pgtable *data,
|
|||||||
if (!ARM_V7S_PTE_IS_TABLE(pte, 1))
|
if (!ARM_V7S_PTE_IS_TABLE(pte, 1))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
tablep = iopte_deref(pte, 1);
|
tablep = iopte_deref(pte, 1, data);
|
||||||
return __arm_v7s_unmap(data, iova, size, 2, tablep);
|
return __arm_v7s_unmap(data, gather, iova, size, 2, tablep);
|
||||||
}
|
}
|
||||||
|
|
||||||
io_pgtable_tlb_add_flush(&data->iop, iova, size, size, true);
|
io_pgtable_tlb_add_page(&data->iop, gather, iova, size);
|
||||||
io_pgtable_tlb_sync(&data->iop);
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t __arm_v7s_unmap(struct arm_v7s_io_pgtable *data,
|
static size_t __arm_v7s_unmap(struct arm_v7s_io_pgtable *data,
|
||||||
|
struct iommu_iotlb_gather *gather,
|
||||||
unsigned long iova, size_t size, int lvl,
|
unsigned long iova, size_t size, int lvl,
|
||||||
arm_v7s_iopte *ptep)
|
arm_v7s_iopte *ptep)
|
||||||
{
|
{
|
||||||
@ -638,10 +683,9 @@ static size_t __arm_v7s_unmap(struct arm_v7s_io_pgtable *data,
|
|||||||
for (i = 0; i < num_entries; i++) {
|
for (i = 0; i < num_entries; i++) {
|
||||||
if (ARM_V7S_PTE_IS_TABLE(pte[i], lvl)) {
|
if (ARM_V7S_PTE_IS_TABLE(pte[i], lvl)) {
|
||||||
/* Also flush any partial walks */
|
/* Also flush any partial walks */
|
||||||
io_pgtable_tlb_add_flush(iop, iova, blk_size,
|
io_pgtable_tlb_flush_walk(iop, iova, blk_size,
|
||||||
ARM_V7S_BLOCK_SIZE(lvl + 1), false);
|
ARM_V7S_BLOCK_SIZE(lvl + 1));
|
||||||
io_pgtable_tlb_sync(iop);
|
ptep = iopte_deref(pte[i], lvl, data);
|
||||||
ptep = iopte_deref(pte[i], lvl);
|
|
||||||
__arm_v7s_free_table(ptep, lvl + 1, data);
|
__arm_v7s_free_table(ptep, lvl + 1, data);
|
||||||
} else if (iop->cfg.quirks & IO_PGTABLE_QUIRK_NON_STRICT) {
|
} else if (iop->cfg.quirks & IO_PGTABLE_QUIRK_NON_STRICT) {
|
||||||
/*
|
/*
|
||||||
@ -651,8 +695,7 @@ static size_t __arm_v7s_unmap(struct arm_v7s_io_pgtable *data,
|
|||||||
*/
|
*/
|
||||||
smp_wmb();
|
smp_wmb();
|
||||||
} else {
|
} else {
|
||||||
io_pgtable_tlb_add_flush(iop, iova, blk_size,
|
io_pgtable_tlb_add_page(iop, gather, iova, blk_size);
|
||||||
blk_size, true);
|
|
||||||
}
|
}
|
||||||
iova += blk_size;
|
iova += blk_size;
|
||||||
}
|
}
|
||||||
@ -662,23 +705,24 @@ static size_t __arm_v7s_unmap(struct arm_v7s_io_pgtable *data,
|
|||||||
* Insert a table at the next level to map the old region,
|
* Insert a table at the next level to map the old region,
|
||||||
* minus the part we want to unmap
|
* minus the part we want to unmap
|
||||||
*/
|
*/
|
||||||
return arm_v7s_split_blk_unmap(data, iova, size, pte[0], ptep);
|
return arm_v7s_split_blk_unmap(data, gather, iova, size, pte[0],
|
||||||
|
ptep);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Keep on walkin' */
|
/* Keep on walkin' */
|
||||||
ptep = iopte_deref(pte[0], lvl);
|
ptep = iopte_deref(pte[0], lvl, data);
|
||||||
return __arm_v7s_unmap(data, iova, size, lvl + 1, ptep);
|
return __arm_v7s_unmap(data, gather, iova, size, lvl + 1, ptep);
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t arm_v7s_unmap(struct io_pgtable_ops *ops, unsigned long iova,
|
static size_t arm_v7s_unmap(struct io_pgtable_ops *ops, unsigned long iova,
|
||||||
size_t size)
|
size_t size, struct iommu_iotlb_gather *gather)
|
||||||
{
|
{
|
||||||
struct arm_v7s_io_pgtable *data = io_pgtable_ops_to_data(ops);
|
struct arm_v7s_io_pgtable *data = io_pgtable_ops_to_data(ops);
|
||||||
|
|
||||||
if (WARN_ON(upper_32_bits(iova)))
|
if (WARN_ON(upper_32_bits(iova)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return __arm_v7s_unmap(data, iova, size, 1, data->pgd);
|
return __arm_v7s_unmap(data, gather, iova, size, 1, data->pgd);
|
||||||
}
|
}
|
||||||
|
|
||||||
static phys_addr_t arm_v7s_iova_to_phys(struct io_pgtable_ops *ops,
|
static phys_addr_t arm_v7s_iova_to_phys(struct io_pgtable_ops *ops,
|
||||||
@ -692,7 +736,7 @@ static phys_addr_t arm_v7s_iova_to_phys(struct io_pgtable_ops *ops,
|
|||||||
do {
|
do {
|
||||||
ptep += ARM_V7S_LVL_IDX(iova, ++lvl);
|
ptep += ARM_V7S_LVL_IDX(iova, ++lvl);
|
||||||
pte = READ_ONCE(*ptep);
|
pte = READ_ONCE(*ptep);
|
||||||
ptep = iopte_deref(pte, lvl);
|
ptep = iopte_deref(pte, lvl, data);
|
||||||
} while (ARM_V7S_PTE_IS_TABLE(pte, lvl));
|
} while (ARM_V7S_PTE_IS_TABLE(pte, lvl));
|
||||||
|
|
||||||
if (!ARM_V7S_PTE_IS_VALID(pte))
|
if (!ARM_V7S_PTE_IS_VALID(pte))
|
||||||
@ -701,7 +745,7 @@ static phys_addr_t arm_v7s_iova_to_phys(struct io_pgtable_ops *ops,
|
|||||||
mask = ARM_V7S_LVL_MASK(lvl);
|
mask = ARM_V7S_LVL_MASK(lvl);
|
||||||
if (arm_v7s_pte_is_cont(pte, lvl))
|
if (arm_v7s_pte_is_cont(pte, lvl))
|
||||||
mask *= ARM_V7S_CONT_PAGES;
|
mask *= ARM_V7S_CONT_PAGES;
|
||||||
return (pte & mask) | (iova & ~mask);
|
return iopte_to_paddr(pte, lvl, &data->iop.cfg) | (iova & ~mask);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct io_pgtable *arm_v7s_alloc_pgtable(struct io_pgtable_cfg *cfg,
|
static struct io_pgtable *arm_v7s_alloc_pgtable(struct io_pgtable_cfg *cfg,
|
||||||
@ -709,18 +753,21 @@ static struct io_pgtable *arm_v7s_alloc_pgtable(struct io_pgtable_cfg *cfg,
|
|||||||
{
|
{
|
||||||
struct arm_v7s_io_pgtable *data;
|
struct arm_v7s_io_pgtable *data;
|
||||||
|
|
||||||
if (cfg->ias > ARM_V7S_ADDR_BITS || cfg->oas > ARM_V7S_ADDR_BITS)
|
if (cfg->ias > ARM_V7S_ADDR_BITS)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (cfg->oas > (arm_v7s_is_mtk_enabled(cfg) ? 34 : ARM_V7S_ADDR_BITS))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (cfg->quirks & ~(IO_PGTABLE_QUIRK_ARM_NS |
|
if (cfg->quirks & ~(IO_PGTABLE_QUIRK_ARM_NS |
|
||||||
IO_PGTABLE_QUIRK_NO_PERMS |
|
IO_PGTABLE_QUIRK_NO_PERMS |
|
||||||
IO_PGTABLE_QUIRK_TLBI_ON_MAP |
|
IO_PGTABLE_QUIRK_TLBI_ON_MAP |
|
||||||
IO_PGTABLE_QUIRK_ARM_MTK_4GB |
|
IO_PGTABLE_QUIRK_ARM_MTK_EXT |
|
||||||
IO_PGTABLE_QUIRK_NON_STRICT))
|
IO_PGTABLE_QUIRK_NON_STRICT))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
/* If ARM_MTK_4GB is enabled, the NO_PERMS is also expected. */
|
/* If ARM_MTK_4GB is enabled, the NO_PERMS is also expected. */
|
||||||
if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_4GB &&
|
if (cfg->quirks & IO_PGTABLE_QUIRK_ARM_MTK_EXT &&
|
||||||
!(cfg->quirks & IO_PGTABLE_QUIRK_NO_PERMS))
|
!(cfg->quirks & IO_PGTABLE_QUIRK_NO_PERMS))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
@ -806,22 +853,24 @@ static void dummy_tlb_flush_all(void *cookie)
|
|||||||
WARN_ON(cookie != cfg_cookie);
|
WARN_ON(cookie != cfg_cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dummy_tlb_add_flush(unsigned long iova, size_t size,
|
static void dummy_tlb_flush(unsigned long iova, size_t size, size_t granule,
|
||||||
size_t granule, bool leaf, void *cookie)
|
void *cookie)
|
||||||
{
|
{
|
||||||
WARN_ON(cookie != cfg_cookie);
|
WARN_ON(cookie != cfg_cookie);
|
||||||
WARN_ON(!(size & cfg_cookie->pgsize_bitmap));
|
WARN_ON(!(size & cfg_cookie->pgsize_bitmap));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dummy_tlb_sync(void *cookie)
|
static void dummy_tlb_add_page(struct iommu_iotlb_gather *gather,
|
||||||
|
unsigned long iova, size_t granule, void *cookie)
|
||||||
{
|
{
|
||||||
WARN_ON(cookie != cfg_cookie);
|
dummy_tlb_flush(iova, granule, granule, cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct iommu_gather_ops dummy_tlb_ops = {
|
static const struct iommu_flush_ops dummy_tlb_ops = {
|
||||||
.tlb_flush_all = dummy_tlb_flush_all,
|
.tlb_flush_all = dummy_tlb_flush_all,
|
||||||
.tlb_add_flush = dummy_tlb_add_flush,
|
.tlb_flush_walk = dummy_tlb_flush,
|
||||||
.tlb_sync = dummy_tlb_sync,
|
.tlb_flush_leaf = dummy_tlb_flush,
|
||||||
|
.tlb_add_page = dummy_tlb_add_page,
|
||||||
};
|
};
|
||||||
|
|
||||||
#define __FAIL(ops) ({ \
|
#define __FAIL(ops) ({ \
|
||||||
@ -896,7 +945,7 @@ static int __init arm_v7s_do_selftests(void)
|
|||||||
size = 1UL << __ffs(cfg.pgsize_bitmap);
|
size = 1UL << __ffs(cfg.pgsize_bitmap);
|
||||||
while (i < loopnr) {
|
while (i < loopnr) {
|
||||||
iova_start = i * SZ_16M;
|
iova_start = i * SZ_16M;
|
||||||
if (ops->unmap(ops, iova_start + size, size) != size)
|
if (ops->unmap(ops, iova_start + size, size, NULL) != size)
|
||||||
return __FAIL(ops);
|
return __FAIL(ops);
|
||||||
|
|
||||||
/* Remap of partial unmap */
|
/* Remap of partial unmap */
|
||||||
@ -914,7 +963,7 @@ static int __init arm_v7s_do_selftests(void)
|
|||||||
for_each_set_bit(i, &cfg.pgsize_bitmap, BITS_PER_LONG) {
|
for_each_set_bit(i, &cfg.pgsize_bitmap, BITS_PER_LONG) {
|
||||||
size = 1UL << i;
|
size = 1UL << i;
|
||||||
|
|
||||||
if (ops->unmap(ops, iova, size) != size)
|
if (ops->unmap(ops, iova, size, NULL) != size)
|
||||||
return __FAIL(ops);
|
return __FAIL(ops);
|
||||||
|
|
||||||
if (ops->iova_to_phys(ops, iova + 42))
|
if (ops->iova_to_phys(ops, iova + 42))
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
#include <linux/atomic.h>
|
#include <linux/atomic.h>
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
#include <linux/io-pgtable.h>
|
#include <linux/io-pgtable.h>
|
||||||
#include <linux/iommu.h>
|
|
||||||
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
||||||
#include <linux/sizes.h>
|
#include <linux/sizes.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
@ -290,6 +289,7 @@ static void __arm_lpae_set_pte(arm_lpae_iopte *ptep, arm_lpae_iopte pte,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
|
static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
|
||||||
|
struct iommu_iotlb_gather *gather,
|
||||||
unsigned long iova, size_t size, int lvl,
|
unsigned long iova, size_t size, int lvl,
|
||||||
arm_lpae_iopte *ptep);
|
arm_lpae_iopte *ptep);
|
||||||
|
|
||||||
@ -335,8 +335,10 @@ static int arm_lpae_init_pte(struct arm_lpae_io_pgtable *data,
|
|||||||
size_t sz = ARM_LPAE_BLOCK_SIZE(lvl, data);
|
size_t sz = ARM_LPAE_BLOCK_SIZE(lvl, data);
|
||||||
|
|
||||||
tblp = ptep - ARM_LPAE_LVL_IDX(iova, lvl, data);
|
tblp = ptep - ARM_LPAE_LVL_IDX(iova, lvl, data);
|
||||||
if (WARN_ON(__arm_lpae_unmap(data, iova, sz, lvl, tblp) != sz))
|
if (__arm_lpae_unmap(data, NULL, iova, sz, lvl, tblp) != sz) {
|
||||||
|
WARN_ON(1);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
__arm_lpae_init_pte(data, paddr, prot, lvl, ptep);
|
__arm_lpae_init_pte(data, paddr, prot, lvl, ptep);
|
||||||
@ -537,6 +539,7 @@ static void arm_lpae_free_pgtable(struct io_pgtable *iop)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static size_t arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data,
|
static size_t arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data,
|
||||||
|
struct iommu_iotlb_gather *gather,
|
||||||
unsigned long iova, size_t size,
|
unsigned long iova, size_t size,
|
||||||
arm_lpae_iopte blk_pte, int lvl,
|
arm_lpae_iopte blk_pte, int lvl,
|
||||||
arm_lpae_iopte *ptep)
|
arm_lpae_iopte *ptep)
|
||||||
@ -582,15 +585,15 @@ static size_t arm_lpae_split_blk_unmap(struct arm_lpae_io_pgtable *data,
|
|||||||
|
|
||||||
tablep = iopte_deref(pte, data);
|
tablep = iopte_deref(pte, data);
|
||||||
} else if (unmap_idx >= 0) {
|
} else if (unmap_idx >= 0) {
|
||||||
io_pgtable_tlb_add_flush(&data->iop, iova, size, size, true);
|
io_pgtable_tlb_add_page(&data->iop, gather, iova, size);
|
||||||
io_pgtable_tlb_sync(&data->iop);
|
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
return __arm_lpae_unmap(data, iova, size, lvl, tablep);
|
return __arm_lpae_unmap(data, gather, iova, size, lvl, tablep);
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
|
static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
|
||||||
|
struct iommu_iotlb_gather *gather,
|
||||||
unsigned long iova, size_t size, int lvl,
|
unsigned long iova, size_t size, int lvl,
|
||||||
arm_lpae_iopte *ptep)
|
arm_lpae_iopte *ptep)
|
||||||
{
|
{
|
||||||
@ -612,9 +615,8 @@ static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
|
|||||||
|
|
||||||
if (!iopte_leaf(pte, lvl, iop->fmt)) {
|
if (!iopte_leaf(pte, lvl, iop->fmt)) {
|
||||||
/* Also flush any partial walks */
|
/* Also flush any partial walks */
|
||||||
io_pgtable_tlb_add_flush(iop, iova, size,
|
io_pgtable_tlb_flush_walk(iop, iova, size,
|
||||||
ARM_LPAE_GRANULE(data), false);
|
ARM_LPAE_GRANULE(data));
|
||||||
io_pgtable_tlb_sync(iop);
|
|
||||||
ptep = iopte_deref(pte, data);
|
ptep = iopte_deref(pte, data);
|
||||||
__arm_lpae_free_pgtable(data, lvl + 1, ptep);
|
__arm_lpae_free_pgtable(data, lvl + 1, ptep);
|
||||||
} else if (iop->cfg.quirks & IO_PGTABLE_QUIRK_NON_STRICT) {
|
} else if (iop->cfg.quirks & IO_PGTABLE_QUIRK_NON_STRICT) {
|
||||||
@ -625,7 +627,7 @@ static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
|
|||||||
*/
|
*/
|
||||||
smp_wmb();
|
smp_wmb();
|
||||||
} else {
|
} else {
|
||||||
io_pgtable_tlb_add_flush(iop, iova, size, size, true);
|
io_pgtable_tlb_add_page(iop, gather, iova, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return size;
|
return size;
|
||||||
@ -634,17 +636,17 @@ static size_t __arm_lpae_unmap(struct arm_lpae_io_pgtable *data,
|
|||||||
* Insert a table at the next level to map the old region,
|
* Insert a table at the next level to map the old region,
|
||||||
* minus the part we want to unmap
|
* minus the part we want to unmap
|
||||||
*/
|
*/
|
||||||
return arm_lpae_split_blk_unmap(data, iova, size, pte,
|
return arm_lpae_split_blk_unmap(data, gather, iova, size, pte,
|
||||||
lvl + 1, ptep);
|
lvl + 1, ptep);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Keep on walkin' */
|
/* Keep on walkin' */
|
||||||
ptep = iopte_deref(pte, data);
|
ptep = iopte_deref(pte, data);
|
||||||
return __arm_lpae_unmap(data, iova, size, lvl + 1, ptep);
|
return __arm_lpae_unmap(data, gather, iova, size, lvl + 1, ptep);
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t arm_lpae_unmap(struct io_pgtable_ops *ops, unsigned long iova,
|
static size_t arm_lpae_unmap(struct io_pgtable_ops *ops, unsigned long iova,
|
||||||
size_t size)
|
size_t size, struct iommu_iotlb_gather *gather)
|
||||||
{
|
{
|
||||||
struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);
|
struct arm_lpae_io_pgtable *data = io_pgtable_ops_to_data(ops);
|
||||||
arm_lpae_iopte *ptep = data->pgd;
|
arm_lpae_iopte *ptep = data->pgd;
|
||||||
@ -653,7 +655,7 @@ static size_t arm_lpae_unmap(struct io_pgtable_ops *ops, unsigned long iova,
|
|||||||
if (WARN_ON(iova >= (1ULL << data->iop.cfg.ias)))
|
if (WARN_ON(iova >= (1ULL << data->iop.cfg.ias)))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
return __arm_lpae_unmap(data, iova, size, lvl, ptep);
|
return __arm_lpae_unmap(data, gather, iova, size, lvl, ptep);
|
||||||
}
|
}
|
||||||
|
|
||||||
static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops,
|
static phys_addr_t arm_lpae_iova_to_phys(struct io_pgtable_ops *ops,
|
||||||
@ -1070,22 +1072,24 @@ static void dummy_tlb_flush_all(void *cookie)
|
|||||||
WARN_ON(cookie != cfg_cookie);
|
WARN_ON(cookie != cfg_cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dummy_tlb_add_flush(unsigned long iova, size_t size,
|
static void dummy_tlb_flush(unsigned long iova, size_t size, size_t granule,
|
||||||
size_t granule, bool leaf, void *cookie)
|
void *cookie)
|
||||||
{
|
{
|
||||||
WARN_ON(cookie != cfg_cookie);
|
WARN_ON(cookie != cfg_cookie);
|
||||||
WARN_ON(!(size & cfg_cookie->pgsize_bitmap));
|
WARN_ON(!(size & cfg_cookie->pgsize_bitmap));
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dummy_tlb_sync(void *cookie)
|
static void dummy_tlb_add_page(struct iommu_iotlb_gather *gather,
|
||||||
|
unsigned long iova, size_t granule, void *cookie)
|
||||||
{
|
{
|
||||||
WARN_ON(cookie != cfg_cookie);
|
dummy_tlb_flush(iova, granule, granule, cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct iommu_gather_ops dummy_tlb_ops __initconst = {
|
static const struct iommu_flush_ops dummy_tlb_ops __initconst = {
|
||||||
.tlb_flush_all = dummy_tlb_flush_all,
|
.tlb_flush_all = dummy_tlb_flush_all,
|
||||||
.tlb_add_flush = dummy_tlb_add_flush,
|
.tlb_flush_walk = dummy_tlb_flush,
|
||||||
.tlb_sync = dummy_tlb_sync,
|
.tlb_flush_leaf = dummy_tlb_flush,
|
||||||
|
.tlb_add_page = dummy_tlb_add_page,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void __init arm_lpae_dump_ops(struct io_pgtable_ops *ops)
|
static void __init arm_lpae_dump_ops(struct io_pgtable_ops *ops)
|
||||||
@ -1168,7 +1172,7 @@ static int __init arm_lpae_run_tests(struct io_pgtable_cfg *cfg)
|
|||||||
|
|
||||||
/* Partial unmap */
|
/* Partial unmap */
|
||||||
size = 1UL << __ffs(cfg->pgsize_bitmap);
|
size = 1UL << __ffs(cfg->pgsize_bitmap);
|
||||||
if (ops->unmap(ops, SZ_1G + size, size) != size)
|
if (ops->unmap(ops, SZ_1G + size, size, NULL) != size)
|
||||||
return __FAIL(ops, i);
|
return __FAIL(ops, i);
|
||||||
|
|
||||||
/* Remap of partial unmap */
|
/* Remap of partial unmap */
|
||||||
@ -1183,7 +1187,7 @@ static int __init arm_lpae_run_tests(struct io_pgtable_cfg *cfg)
|
|||||||
for_each_set_bit(j, &cfg->pgsize_bitmap, BITS_PER_LONG) {
|
for_each_set_bit(j, &cfg->pgsize_bitmap, BITS_PER_LONG) {
|
||||||
size = 1UL << j;
|
size = 1UL << j;
|
||||||
|
|
||||||
if (ops->unmap(ops, iova, size) != size)
|
if (ops->unmap(ops, iova, size, NULL) != size)
|
||||||
return __FAIL(ops, i);
|
return __FAIL(ops, i);
|
||||||
|
|
||||||
if (ops->iova_to_phys(ops, iova + 42))
|
if (ops->iova_to_phys(ops, iova + 42))
|
||||||
|
@ -26,12 +26,10 @@
|
|||||||
|
|
||||||
static struct kset *iommu_group_kset;
|
static struct kset *iommu_group_kset;
|
||||||
static DEFINE_IDA(iommu_group_ida);
|
static DEFINE_IDA(iommu_group_ida);
|
||||||
#ifdef CONFIG_IOMMU_DEFAULT_PASSTHROUGH
|
|
||||||
static unsigned int iommu_def_domain_type = IOMMU_DOMAIN_IDENTITY;
|
static unsigned int iommu_def_domain_type __read_mostly;
|
||||||
#else
|
|
||||||
static unsigned int iommu_def_domain_type = IOMMU_DOMAIN_DMA;
|
|
||||||
#endif
|
|
||||||
static bool iommu_dma_strict __read_mostly = true;
|
static bool iommu_dma_strict __read_mostly = true;
|
||||||
|
static u32 iommu_cmd_line __read_mostly;
|
||||||
|
|
||||||
struct iommu_group {
|
struct iommu_group {
|
||||||
struct kobject kobj;
|
struct kobject kobj;
|
||||||
@ -68,6 +66,18 @@ static const char * const iommu_group_resv_type_string[] = {
|
|||||||
[IOMMU_RESV_SW_MSI] = "msi",
|
[IOMMU_RESV_SW_MSI] = "msi",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#define IOMMU_CMD_LINE_DMA_API BIT(0)
|
||||||
|
|
||||||
|
static void iommu_set_cmd_line_dma_api(void)
|
||||||
|
{
|
||||||
|
iommu_cmd_line |= IOMMU_CMD_LINE_DMA_API;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool iommu_cmd_line_dma_api(void)
|
||||||
|
{
|
||||||
|
return !!(iommu_cmd_line & IOMMU_CMD_LINE_DMA_API);
|
||||||
|
}
|
||||||
|
|
||||||
#define IOMMU_GROUP_ATTR(_name, _mode, _show, _store) \
|
#define IOMMU_GROUP_ATTR(_name, _mode, _show, _store) \
|
||||||
struct iommu_group_attribute iommu_group_attr_##_name = \
|
struct iommu_group_attribute iommu_group_attr_##_name = \
|
||||||
__ATTR(_name, _mode, _show, _store)
|
__ATTR(_name, _mode, _show, _store)
|
||||||
@ -80,12 +90,55 @@ struct iommu_group_attribute iommu_group_attr_##_name = \
|
|||||||
static LIST_HEAD(iommu_device_list);
|
static LIST_HEAD(iommu_device_list);
|
||||||
static DEFINE_SPINLOCK(iommu_device_lock);
|
static DEFINE_SPINLOCK(iommu_device_lock);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use a function instead of an array here because the domain-type is a
|
||||||
|
* bit-field, so an array would waste memory.
|
||||||
|
*/
|
||||||
|
static const char *iommu_domain_type_str(unsigned int t)
|
||||||
|
{
|
||||||
|
switch (t) {
|
||||||
|
case IOMMU_DOMAIN_BLOCKED:
|
||||||
|
return "Blocked";
|
||||||
|
case IOMMU_DOMAIN_IDENTITY:
|
||||||
|
return "Passthrough";
|
||||||
|
case IOMMU_DOMAIN_UNMANAGED:
|
||||||
|
return "Unmanaged";
|
||||||
|
case IOMMU_DOMAIN_DMA:
|
||||||
|
return "Translated";
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __init iommu_subsys_init(void)
|
||||||
|
{
|
||||||
|
bool cmd_line = iommu_cmd_line_dma_api();
|
||||||
|
|
||||||
|
if (!cmd_line) {
|
||||||
|
if (IS_ENABLED(CONFIG_IOMMU_DEFAULT_PASSTHROUGH))
|
||||||
|
iommu_set_default_passthrough(false);
|
||||||
|
else
|
||||||
|
iommu_set_default_translated(false);
|
||||||
|
|
||||||
|
if (iommu_default_passthrough() && mem_encrypt_active()) {
|
||||||
|
pr_info("Memory encryption detected - Disabling default IOMMU Passthrough\n");
|
||||||
|
iommu_set_default_translated(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pr_info("Default domain type: %s %s\n",
|
||||||
|
iommu_domain_type_str(iommu_def_domain_type),
|
||||||
|
cmd_line ? "(set via kernel command line)" : "");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
subsys_initcall(iommu_subsys_init);
|
||||||
|
|
||||||
int iommu_device_register(struct iommu_device *iommu)
|
int iommu_device_register(struct iommu_device *iommu)
|
||||||
{
|
{
|
||||||
spin_lock(&iommu_device_lock);
|
spin_lock(&iommu_device_lock);
|
||||||
list_add_tail(&iommu->list, &iommu_device_list);
|
list_add_tail(&iommu->list, &iommu_device_list);
|
||||||
spin_unlock(&iommu_device_lock);
|
spin_unlock(&iommu_device_lock);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -165,7 +218,11 @@ static int __init iommu_set_def_domain_type(char *str)
|
|||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
|
||||||
iommu_def_domain_type = pt ? IOMMU_DOMAIN_IDENTITY : IOMMU_DOMAIN_DMA;
|
if (pt)
|
||||||
|
iommu_set_default_passthrough(true);
|
||||||
|
else
|
||||||
|
iommu_set_default_translated(true);
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
early_param("iommu.passthrough", iommu_set_def_domain_type);
|
early_param("iommu.passthrough", iommu_set_def_domain_type);
|
||||||
@ -229,60 +286,58 @@ static ssize_t iommu_group_show_name(struct iommu_group *group, char *buf)
|
|||||||
* @new: new region to insert
|
* @new: new region to insert
|
||||||
* @regions: list of regions
|
* @regions: list of regions
|
||||||
*
|
*
|
||||||
* The new element is sorted by address with respect to the other
|
* Elements are sorted by start address and overlapping segments
|
||||||
* regions of the same type. In case it overlaps with another
|
* of the same type are merged.
|
||||||
* region of the same type, regions are merged. In case it
|
|
||||||
* overlaps with another region of different type, regions are
|
|
||||||
* not merged.
|
|
||||||
*/
|
*/
|
||||||
static int iommu_insert_resv_region(struct iommu_resv_region *new,
|
int iommu_insert_resv_region(struct iommu_resv_region *new,
|
||||||
struct list_head *regions)
|
struct list_head *regions)
|
||||||
{
|
{
|
||||||
struct iommu_resv_region *region;
|
struct iommu_resv_region *iter, *tmp, *nr, *top;
|
||||||
phys_addr_t start = new->start;
|
LIST_HEAD(stack);
|
||||||
phys_addr_t end = new->start + new->length - 1;
|
|
||||||
struct list_head *pos = regions->next;
|
|
||||||
|
|
||||||
while (pos != regions) {
|
nr = iommu_alloc_resv_region(new->start, new->length,
|
||||||
struct iommu_resv_region *entry =
|
new->prot, new->type);
|
||||||
list_entry(pos, struct iommu_resv_region, list);
|
if (!nr)
|
||||||
phys_addr_t a = entry->start;
|
|
||||||
phys_addr_t b = entry->start + entry->length - 1;
|
|
||||||
int type = entry->type;
|
|
||||||
|
|
||||||
if (end < a) {
|
|
||||||
goto insert;
|
|
||||||
} else if (start > b) {
|
|
||||||
pos = pos->next;
|
|
||||||
} else if ((start >= a) && (end <= b)) {
|
|
||||||
if (new->type == type)
|
|
||||||
return 0;
|
|
||||||
else
|
|
||||||
pos = pos->next;
|
|
||||||
} else {
|
|
||||||
if (new->type == type) {
|
|
||||||
phys_addr_t new_start = min(a, start);
|
|
||||||
phys_addr_t new_end = max(b, end);
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
list_del(&entry->list);
|
|
||||||
entry->start = new_start;
|
|
||||||
entry->length = new_end - new_start + 1;
|
|
||||||
ret = iommu_insert_resv_region(entry, regions);
|
|
||||||
kfree(entry);
|
|
||||||
return ret;
|
|
||||||
} else {
|
|
||||||
pos = pos->next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
insert:
|
|
||||||
region = iommu_alloc_resv_region(new->start, new->length,
|
|
||||||
new->prot, new->type);
|
|
||||||
if (!region)
|
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
list_add_tail(®ion->list, pos);
|
/* First add the new element based on start address sorting */
|
||||||
|
list_for_each_entry(iter, regions, list) {
|
||||||
|
if (nr->start < iter->start ||
|
||||||
|
(nr->start == iter->start && nr->type <= iter->type))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
list_add_tail(&nr->list, &iter->list);
|
||||||
|
|
||||||
|
/* Merge overlapping segments of type nr->type in @regions, if any */
|
||||||
|
list_for_each_entry_safe(iter, tmp, regions, list) {
|
||||||
|
phys_addr_t top_end, iter_end = iter->start + iter->length - 1;
|
||||||
|
|
||||||
|
/* no merge needed on elements of different types than @nr */
|
||||||
|
if (iter->type != nr->type) {
|
||||||
|
list_move_tail(&iter->list, &stack);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* look for the last stack element of same type as @iter */
|
||||||
|
list_for_each_entry_reverse(top, &stack, list)
|
||||||
|
if (top->type == iter->type)
|
||||||
|
goto check_overlap;
|
||||||
|
|
||||||
|
list_move_tail(&iter->list, &stack);
|
||||||
|
continue;
|
||||||
|
|
||||||
|
check_overlap:
|
||||||
|
top_end = top->start + top->length - 1;
|
||||||
|
|
||||||
|
if (iter->start > top_end + 1) {
|
||||||
|
list_move_tail(&iter->list, &stack);
|
||||||
|
} else {
|
||||||
|
top->length = max(top_end, iter_end) - top->start + 1;
|
||||||
|
list_del(&iter->list);
|
||||||
|
kfree(iter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list_splice(&stack, regions);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1862,7 +1917,7 @@ EXPORT_SYMBOL_GPL(iommu_map);
|
|||||||
|
|
||||||
static size_t __iommu_unmap(struct iommu_domain *domain,
|
static size_t __iommu_unmap(struct iommu_domain *domain,
|
||||||
unsigned long iova, size_t size,
|
unsigned long iova, size_t size,
|
||||||
bool sync)
|
struct iommu_iotlb_gather *iotlb_gather)
|
||||||
{
|
{
|
||||||
const struct iommu_ops *ops = domain->ops;
|
const struct iommu_ops *ops = domain->ops;
|
||||||
size_t unmapped_page, unmapped = 0;
|
size_t unmapped_page, unmapped = 0;
|
||||||
@ -1899,13 +1954,10 @@ static size_t __iommu_unmap(struct iommu_domain *domain,
|
|||||||
while (unmapped < size) {
|
while (unmapped < size) {
|
||||||
size_t pgsize = iommu_pgsize(domain, iova, size - unmapped);
|
size_t pgsize = iommu_pgsize(domain, iova, size - unmapped);
|
||||||
|
|
||||||
unmapped_page = ops->unmap(domain, iova, pgsize);
|
unmapped_page = ops->unmap(domain, iova, pgsize, iotlb_gather);
|
||||||
if (!unmapped_page)
|
if (!unmapped_page)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (sync && ops->iotlb_range_add)
|
|
||||||
ops->iotlb_range_add(domain, iova, pgsize);
|
|
||||||
|
|
||||||
pr_debug("unmapped: iova 0x%lx size 0x%zx\n",
|
pr_debug("unmapped: iova 0x%lx size 0x%zx\n",
|
||||||
iova, unmapped_page);
|
iova, unmapped_page);
|
||||||
|
|
||||||
@ -1913,9 +1965,6 @@ static size_t __iommu_unmap(struct iommu_domain *domain,
|
|||||||
unmapped += unmapped_page;
|
unmapped += unmapped_page;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sync && ops->iotlb_sync)
|
|
||||||
ops->iotlb_sync(domain);
|
|
||||||
|
|
||||||
trace_unmap(orig_iova, size, unmapped);
|
trace_unmap(orig_iova, size, unmapped);
|
||||||
return unmapped;
|
return unmapped;
|
||||||
}
|
}
|
||||||
@ -1923,14 +1972,22 @@ static size_t __iommu_unmap(struct iommu_domain *domain,
|
|||||||
size_t iommu_unmap(struct iommu_domain *domain,
|
size_t iommu_unmap(struct iommu_domain *domain,
|
||||||
unsigned long iova, size_t size)
|
unsigned long iova, size_t size)
|
||||||
{
|
{
|
||||||
return __iommu_unmap(domain, iova, size, true);
|
struct iommu_iotlb_gather iotlb_gather;
|
||||||
|
size_t ret;
|
||||||
|
|
||||||
|
iommu_iotlb_gather_init(&iotlb_gather);
|
||||||
|
ret = __iommu_unmap(domain, iova, size, &iotlb_gather);
|
||||||
|
iommu_tlb_sync(domain, &iotlb_gather);
|
||||||
|
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(iommu_unmap);
|
EXPORT_SYMBOL_GPL(iommu_unmap);
|
||||||
|
|
||||||
size_t iommu_unmap_fast(struct iommu_domain *domain,
|
size_t iommu_unmap_fast(struct iommu_domain *domain,
|
||||||
unsigned long iova, size_t size)
|
unsigned long iova, size_t size,
|
||||||
|
struct iommu_iotlb_gather *iotlb_gather)
|
||||||
{
|
{
|
||||||
return __iommu_unmap(domain, iova, size, false);
|
return __iommu_unmap(domain, iova, size, iotlb_gather);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(iommu_unmap_fast);
|
EXPORT_SYMBOL_GPL(iommu_unmap_fast);
|
||||||
|
|
||||||
@ -2143,7 +2200,6 @@ request_default_domain_for_dev(struct device *dev, unsigned long type)
|
|||||||
|
|
||||||
mutex_lock(&group->mutex);
|
mutex_lock(&group->mutex);
|
||||||
|
|
||||||
/* Check if the default domain is already direct mapped */
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
if (group->default_domain && group->default_domain->type == type)
|
if (group->default_domain && group->default_domain->type == type)
|
||||||
goto out;
|
goto out;
|
||||||
@ -2153,7 +2209,6 @@ request_default_domain_for_dev(struct device *dev, unsigned long type)
|
|||||||
if (iommu_group_device_count(group) != 1)
|
if (iommu_group_device_count(group) != 1)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/* Allocate a direct mapped domain */
|
|
||||||
ret = -ENOMEM;
|
ret = -ENOMEM;
|
||||||
domain = __iommu_domain_alloc(dev->bus, type);
|
domain = __iommu_domain_alloc(dev->bus, type);
|
||||||
if (!domain)
|
if (!domain)
|
||||||
@ -2168,7 +2223,7 @@ request_default_domain_for_dev(struct device *dev, unsigned long type)
|
|||||||
|
|
||||||
iommu_group_create_direct_mappings(group, dev);
|
iommu_group_create_direct_mappings(group, dev);
|
||||||
|
|
||||||
/* Make the direct mapped domain the default for this group */
|
/* Make the domain the default for this group */
|
||||||
if (group->default_domain)
|
if (group->default_domain)
|
||||||
iommu_domain_free(group->default_domain);
|
iommu_domain_free(group->default_domain);
|
||||||
group->default_domain = domain;
|
group->default_domain = domain;
|
||||||
@ -2196,6 +2251,28 @@ int iommu_request_dma_domain_for_dev(struct device *dev)
|
|||||||
return request_default_domain_for_dev(dev, IOMMU_DOMAIN_DMA);
|
return request_default_domain_for_dev(dev, IOMMU_DOMAIN_DMA);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void iommu_set_default_passthrough(bool cmd_line)
|
||||||
|
{
|
||||||
|
if (cmd_line)
|
||||||
|
iommu_set_cmd_line_dma_api();
|
||||||
|
|
||||||
|
iommu_def_domain_type = IOMMU_DOMAIN_IDENTITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
void iommu_set_default_translated(bool cmd_line)
|
||||||
|
{
|
||||||
|
if (cmd_line)
|
||||||
|
iommu_set_cmd_line_dma_api();
|
||||||
|
|
||||||
|
iommu_def_domain_type = IOMMU_DOMAIN_DMA;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool iommu_default_passthrough(void)
|
||||||
|
{
|
||||||
|
return iommu_def_domain_type == IOMMU_DOMAIN_IDENTITY;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(iommu_default_passthrough);
|
||||||
|
|
||||||
const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode)
|
const struct iommu_ops *iommu_ops_from_fwnode(struct fwnode_handle *fwnode)
|
||||||
{
|
{
|
||||||
const struct iommu_ops *ops = NULL;
|
const struct iommu_ops *ops = NULL;
|
||||||
|
@ -577,7 +577,9 @@ void queue_iova(struct iova_domain *iovad,
|
|||||||
|
|
||||||
spin_unlock_irqrestore(&fq->lock, flags);
|
spin_unlock_irqrestore(&fq->lock, flags);
|
||||||
|
|
||||||
if (atomic_cmpxchg(&iovad->fq_timer_on, 0, 1) == 0)
|
/* Avoid false sharing as much as possible. */
|
||||||
|
if (!atomic_read(&iovad->fq_timer_on) &&
|
||||||
|
!atomic_cmpxchg(&iovad->fq_timer_on, 0, 1))
|
||||||
mod_timer(&iovad->fq_timer,
|
mod_timer(&iovad->fq_timer,
|
||||||
jiffies + msecs_to_jiffies(IOVA_FQ_TIMEOUT));
|
jiffies + msecs_to_jiffies(IOVA_FQ_TIMEOUT));
|
||||||
}
|
}
|
||||||
|
@ -49,6 +49,7 @@ struct ipmmu_features {
|
|||||||
bool setup_imbuscr;
|
bool setup_imbuscr;
|
||||||
bool twobit_imttbcr_sl0;
|
bool twobit_imttbcr_sl0;
|
||||||
bool reserved_context;
|
bool reserved_context;
|
||||||
|
bool cache_snoop;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ipmmu_vmsa_device {
|
struct ipmmu_vmsa_device {
|
||||||
@ -115,45 +116,44 @@ static struct ipmmu_vmsa_device *to_ipmmu(struct device *dev)
|
|||||||
#define IMTTBCR 0x0008
|
#define IMTTBCR 0x0008
|
||||||
#define IMTTBCR_EAE (1 << 31)
|
#define IMTTBCR_EAE (1 << 31)
|
||||||
#define IMTTBCR_PMB (1 << 30)
|
#define IMTTBCR_PMB (1 << 30)
|
||||||
#define IMTTBCR_SH1_NON_SHAREABLE (0 << 28)
|
#define IMTTBCR_SH1_NON_SHAREABLE (0 << 28) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_SH1_OUTER_SHAREABLE (2 << 28)
|
#define IMTTBCR_SH1_OUTER_SHAREABLE (2 << 28) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_SH1_INNER_SHAREABLE (3 << 28)
|
#define IMTTBCR_SH1_INNER_SHAREABLE (3 << 28) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_SH1_MASK (3 << 28)
|
#define IMTTBCR_SH1_MASK (3 << 28) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_ORGN1_NC (0 << 26)
|
#define IMTTBCR_ORGN1_NC (0 << 26) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_ORGN1_WB_WA (1 << 26)
|
#define IMTTBCR_ORGN1_WB_WA (1 << 26) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_ORGN1_WT (2 << 26)
|
#define IMTTBCR_ORGN1_WT (2 << 26) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_ORGN1_WB (3 << 26)
|
#define IMTTBCR_ORGN1_WB (3 << 26) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_ORGN1_MASK (3 << 26)
|
#define IMTTBCR_ORGN1_MASK (3 << 26) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_IRGN1_NC (0 << 24)
|
#define IMTTBCR_IRGN1_NC (0 << 24) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_IRGN1_WB_WA (1 << 24)
|
#define IMTTBCR_IRGN1_WB_WA (1 << 24) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_IRGN1_WT (2 << 24)
|
#define IMTTBCR_IRGN1_WT (2 << 24) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_IRGN1_WB (3 << 24)
|
#define IMTTBCR_IRGN1_WB (3 << 24) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_IRGN1_MASK (3 << 24)
|
#define IMTTBCR_IRGN1_MASK (3 << 24) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_TSZ1_MASK (7 << 16)
|
#define IMTTBCR_TSZ1_MASK (7 << 16)
|
||||||
#define IMTTBCR_TSZ1_SHIFT 16
|
#define IMTTBCR_TSZ1_SHIFT 16
|
||||||
#define IMTTBCR_SH0_NON_SHAREABLE (0 << 12)
|
#define IMTTBCR_SH0_NON_SHAREABLE (0 << 12) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_SH0_OUTER_SHAREABLE (2 << 12)
|
#define IMTTBCR_SH0_OUTER_SHAREABLE (2 << 12) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_SH0_INNER_SHAREABLE (3 << 12)
|
#define IMTTBCR_SH0_INNER_SHAREABLE (3 << 12) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_SH0_MASK (3 << 12)
|
#define IMTTBCR_SH0_MASK (3 << 12) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_ORGN0_NC (0 << 10)
|
#define IMTTBCR_ORGN0_NC (0 << 10) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_ORGN0_WB_WA (1 << 10)
|
#define IMTTBCR_ORGN0_WB_WA (1 << 10) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_ORGN0_WT (2 << 10)
|
#define IMTTBCR_ORGN0_WT (2 << 10) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_ORGN0_WB (3 << 10)
|
#define IMTTBCR_ORGN0_WB (3 << 10) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_ORGN0_MASK (3 << 10)
|
#define IMTTBCR_ORGN0_MASK (3 << 10) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_IRGN0_NC (0 << 8)
|
#define IMTTBCR_IRGN0_NC (0 << 8) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_IRGN0_WB_WA (1 << 8)
|
#define IMTTBCR_IRGN0_WB_WA (1 << 8) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_IRGN0_WT (2 << 8)
|
#define IMTTBCR_IRGN0_WT (2 << 8) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_IRGN0_WB (3 << 8)
|
#define IMTTBCR_IRGN0_WB (3 << 8) /* R-Car Gen2 only */
|
||||||
#define IMTTBCR_IRGN0_MASK (3 << 8)
|
#define IMTTBCR_IRGN0_MASK (3 << 8) /* R-Car Gen2 only */
|
||||||
|
#define IMTTBCR_SL0_TWOBIT_LVL_3 (0 << 6) /* R-Car Gen3 only */
|
||||||
|
#define IMTTBCR_SL0_TWOBIT_LVL_2 (1 << 6) /* R-Car Gen3 only */
|
||||||
|
#define IMTTBCR_SL0_TWOBIT_LVL_1 (2 << 6) /* R-Car Gen3 only */
|
||||||
#define IMTTBCR_SL0_LVL_2 (0 << 4)
|
#define IMTTBCR_SL0_LVL_2 (0 << 4)
|
||||||
#define IMTTBCR_SL0_LVL_1 (1 << 4)
|
#define IMTTBCR_SL0_LVL_1 (1 << 4)
|
||||||
#define IMTTBCR_TSZ0_MASK (7 << 0)
|
#define IMTTBCR_TSZ0_MASK (7 << 0)
|
||||||
#define IMTTBCR_TSZ0_SHIFT O
|
#define IMTTBCR_TSZ0_SHIFT O
|
||||||
|
|
||||||
#define IMTTBCR_SL0_TWOBIT_LVL_3 (0 << 6)
|
|
||||||
#define IMTTBCR_SL0_TWOBIT_LVL_2 (1 << 6)
|
|
||||||
#define IMTTBCR_SL0_TWOBIT_LVL_1 (2 << 6)
|
|
||||||
|
|
||||||
#define IMBUSCR 0x000c
|
#define IMBUSCR 0x000c
|
||||||
#define IMBUSCR_DVM (1 << 2)
|
#define IMBUSCR_DVM (1 << 2)
|
||||||
#define IMBUSCR_BUSSEL_SYS (0 << 0)
|
#define IMBUSCR_BUSSEL_SYS (0 << 0)
|
||||||
@ -361,16 +361,16 @@ static void ipmmu_tlb_flush_all(void *cookie)
|
|||||||
ipmmu_tlb_invalidate(domain);
|
ipmmu_tlb_invalidate(domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ipmmu_tlb_add_flush(unsigned long iova, size_t size,
|
static void ipmmu_tlb_flush(unsigned long iova, size_t size,
|
||||||
size_t granule, bool leaf, void *cookie)
|
size_t granule, void *cookie)
|
||||||
{
|
{
|
||||||
/* The hardware doesn't support selective TLB flush. */
|
ipmmu_tlb_flush_all(cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct iommu_gather_ops ipmmu_gather_ops = {
|
static const struct iommu_flush_ops ipmmu_flush_ops = {
|
||||||
.tlb_flush_all = ipmmu_tlb_flush_all,
|
.tlb_flush_all = ipmmu_tlb_flush_all,
|
||||||
.tlb_add_flush = ipmmu_tlb_add_flush,
|
.tlb_flush_walk = ipmmu_tlb_flush,
|
||||||
.tlb_sync = ipmmu_tlb_flush_all,
|
.tlb_flush_leaf = ipmmu_tlb_flush,
|
||||||
};
|
};
|
||||||
|
|
||||||
/* -----------------------------------------------------------------------------
|
/* -----------------------------------------------------------------------------
|
||||||
@ -422,17 +422,19 @@ static void ipmmu_domain_setup_context(struct ipmmu_vmsa_domain *domain)
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* TTBCR
|
* TTBCR
|
||||||
* We use long descriptors with inner-shareable WBWA tables and allocate
|
* We use long descriptors and allocate the whole 32-bit VA space to
|
||||||
* the whole 32-bit VA space to TTBR0.
|
* TTBR0.
|
||||||
*/
|
*/
|
||||||
if (domain->mmu->features->twobit_imttbcr_sl0)
|
if (domain->mmu->features->twobit_imttbcr_sl0)
|
||||||
tmp = IMTTBCR_SL0_TWOBIT_LVL_1;
|
tmp = IMTTBCR_SL0_TWOBIT_LVL_1;
|
||||||
else
|
else
|
||||||
tmp = IMTTBCR_SL0_LVL_1;
|
tmp = IMTTBCR_SL0_LVL_1;
|
||||||
|
|
||||||
ipmmu_ctx_write_root(domain, IMTTBCR, IMTTBCR_EAE |
|
if (domain->mmu->features->cache_snoop)
|
||||||
IMTTBCR_SH0_INNER_SHAREABLE | IMTTBCR_ORGN0_WB_WA |
|
tmp |= IMTTBCR_SH0_INNER_SHAREABLE | IMTTBCR_ORGN0_WB_WA |
|
||||||
IMTTBCR_IRGN0_WB_WA | tmp);
|
IMTTBCR_IRGN0_WB_WA;
|
||||||
|
|
||||||
|
ipmmu_ctx_write_root(domain, IMTTBCR, IMTTBCR_EAE | tmp);
|
||||||
|
|
||||||
/* MAIR0 */
|
/* MAIR0 */
|
||||||
ipmmu_ctx_write_root(domain, IMMAIR0,
|
ipmmu_ctx_write_root(domain, IMMAIR0,
|
||||||
@ -480,7 +482,7 @@ static int ipmmu_domain_init_context(struct ipmmu_vmsa_domain *domain)
|
|||||||
domain->cfg.pgsize_bitmap = SZ_1G | SZ_2M | SZ_4K;
|
domain->cfg.pgsize_bitmap = SZ_1G | SZ_2M | SZ_4K;
|
||||||
domain->cfg.ias = 32;
|
domain->cfg.ias = 32;
|
||||||
domain->cfg.oas = 40;
|
domain->cfg.oas = 40;
|
||||||
domain->cfg.tlb = &ipmmu_gather_ops;
|
domain->cfg.tlb = &ipmmu_flush_ops;
|
||||||
domain->io_domain.geometry.aperture_end = DMA_BIT_MASK(32);
|
domain->io_domain.geometry.aperture_end = DMA_BIT_MASK(32);
|
||||||
domain->io_domain.geometry.force_aperture = true;
|
domain->io_domain.geometry.force_aperture = true;
|
||||||
/*
|
/*
|
||||||
@ -733,14 +735,14 @@ static int ipmmu_map(struct iommu_domain *io_domain, unsigned long iova,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static size_t ipmmu_unmap(struct iommu_domain *io_domain, unsigned long iova,
|
static size_t ipmmu_unmap(struct iommu_domain *io_domain, unsigned long iova,
|
||||||
size_t size)
|
size_t size, struct iommu_iotlb_gather *gather)
|
||||||
{
|
{
|
||||||
struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain);
|
struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain);
|
||||||
|
|
||||||
return domain->iop->unmap(domain->iop, iova, size);
|
return domain->iop->unmap(domain->iop, iova, size, gather);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ipmmu_iotlb_sync(struct iommu_domain *io_domain)
|
static void ipmmu_flush_iotlb_all(struct iommu_domain *io_domain)
|
||||||
{
|
{
|
||||||
struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain);
|
struct ipmmu_vmsa_domain *domain = to_vmsa_domain(io_domain);
|
||||||
|
|
||||||
@ -748,6 +750,12 @@ static void ipmmu_iotlb_sync(struct iommu_domain *io_domain)
|
|||||||
ipmmu_tlb_flush_all(domain);
|
ipmmu_tlb_flush_all(domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void ipmmu_iotlb_sync(struct iommu_domain *io_domain,
|
||||||
|
struct iommu_iotlb_gather *gather)
|
||||||
|
{
|
||||||
|
ipmmu_flush_iotlb_all(io_domain);
|
||||||
|
}
|
||||||
|
|
||||||
static phys_addr_t ipmmu_iova_to_phys(struct iommu_domain *io_domain,
|
static phys_addr_t ipmmu_iova_to_phys(struct iommu_domain *io_domain,
|
||||||
dma_addr_t iova)
|
dma_addr_t iova)
|
||||||
{
|
{
|
||||||
@ -957,7 +965,7 @@ static const struct iommu_ops ipmmu_ops = {
|
|||||||
.detach_dev = ipmmu_detach_device,
|
.detach_dev = ipmmu_detach_device,
|
||||||
.map = ipmmu_map,
|
.map = ipmmu_map,
|
||||||
.unmap = ipmmu_unmap,
|
.unmap = ipmmu_unmap,
|
||||||
.flush_iotlb_all = ipmmu_iotlb_sync,
|
.flush_iotlb_all = ipmmu_flush_iotlb_all,
|
||||||
.iotlb_sync = ipmmu_iotlb_sync,
|
.iotlb_sync = ipmmu_iotlb_sync,
|
||||||
.iova_to_phys = ipmmu_iova_to_phys,
|
.iova_to_phys = ipmmu_iova_to_phys,
|
||||||
.add_device = ipmmu_add_device,
|
.add_device = ipmmu_add_device,
|
||||||
@ -988,6 +996,7 @@ static const struct ipmmu_features ipmmu_features_default = {
|
|||||||
.setup_imbuscr = true,
|
.setup_imbuscr = true,
|
||||||
.twobit_imttbcr_sl0 = false,
|
.twobit_imttbcr_sl0 = false,
|
||||||
.reserved_context = false,
|
.reserved_context = false,
|
||||||
|
.cache_snoop = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct ipmmu_features ipmmu_features_rcar_gen3 = {
|
static const struct ipmmu_features ipmmu_features_rcar_gen3 = {
|
||||||
@ -998,6 +1007,7 @@ static const struct ipmmu_features ipmmu_features_rcar_gen3 = {
|
|||||||
.setup_imbuscr = false,
|
.setup_imbuscr = false,
|
||||||
.twobit_imttbcr_sl0 = true,
|
.twobit_imttbcr_sl0 = true,
|
||||||
.reserved_context = true,
|
.reserved_context = true,
|
||||||
|
.cache_snoop = false,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id ipmmu_of_ids[] = {
|
static const struct of_device_id ipmmu_of_ids[] = {
|
||||||
|
@ -168,20 +168,29 @@ static void __flush_iotlb_range(unsigned long iova, size_t size,
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __flush_iotlb_sync(void *cookie)
|
static void __flush_iotlb_walk(unsigned long iova, size_t size,
|
||||||
|
size_t granule, void *cookie)
|
||||||
{
|
{
|
||||||
/*
|
__flush_iotlb_range(iova, size, granule, false, cookie);
|
||||||
* Nothing is needed here, the barrier to guarantee
|
|
||||||
* completion of the tlb sync operation is implicitly
|
|
||||||
* taken care when the iommu client does a writel before
|
|
||||||
* kick starting the other master.
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct iommu_gather_ops msm_iommu_gather_ops = {
|
static void __flush_iotlb_leaf(unsigned long iova, size_t size,
|
||||||
|
size_t granule, void *cookie)
|
||||||
|
{
|
||||||
|
__flush_iotlb_range(iova, size, granule, true, cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __flush_iotlb_page(struct iommu_iotlb_gather *gather,
|
||||||
|
unsigned long iova, size_t granule, void *cookie)
|
||||||
|
{
|
||||||
|
__flush_iotlb_range(iova, granule, granule, true, cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iommu_flush_ops msm_iommu_flush_ops = {
|
||||||
.tlb_flush_all = __flush_iotlb,
|
.tlb_flush_all = __flush_iotlb,
|
||||||
.tlb_add_flush = __flush_iotlb_range,
|
.tlb_flush_walk = __flush_iotlb_walk,
|
||||||
.tlb_sync = __flush_iotlb_sync,
|
.tlb_flush_leaf = __flush_iotlb_leaf,
|
||||||
|
.tlb_add_page = __flush_iotlb_page,
|
||||||
};
|
};
|
||||||
|
|
||||||
static int msm_iommu_alloc_ctx(unsigned long *map, int start, int end)
|
static int msm_iommu_alloc_ctx(unsigned long *map, int start, int end)
|
||||||
@ -345,7 +354,7 @@ static int msm_iommu_domain_config(struct msm_priv *priv)
|
|||||||
.pgsize_bitmap = msm_iommu_ops.pgsize_bitmap,
|
.pgsize_bitmap = msm_iommu_ops.pgsize_bitmap,
|
||||||
.ias = 32,
|
.ias = 32,
|
||||||
.oas = 32,
|
.oas = 32,
|
||||||
.tlb = &msm_iommu_gather_ops,
|
.tlb = &msm_iommu_flush_ops,
|
||||||
.iommu_dev = priv->dev,
|
.iommu_dev = priv->dev,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -509,13 +518,13 @@ static int msm_iommu_map(struct iommu_domain *domain, unsigned long iova,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static size_t msm_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
|
static size_t msm_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
|
||||||
size_t len)
|
size_t len, struct iommu_iotlb_gather *gather)
|
||||||
{
|
{
|
||||||
struct msm_priv *priv = to_msm_priv(domain);
|
struct msm_priv *priv = to_msm_priv(domain);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
|
||||||
spin_lock_irqsave(&priv->pgtlock, flags);
|
spin_lock_irqsave(&priv->pgtlock, flags);
|
||||||
len = priv->iop->unmap(priv->iop, iova, len);
|
len = priv->iop->unmap(priv->iop, iova, len, gather);
|
||||||
spin_unlock_irqrestore(&priv->pgtlock, flags);
|
spin_unlock_irqrestore(&priv->pgtlock, flags);
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
@ -691,6 +700,13 @@ static struct iommu_ops msm_iommu_ops = {
|
|||||||
.detach_dev = msm_iommu_detach_dev,
|
.detach_dev = msm_iommu_detach_dev,
|
||||||
.map = msm_iommu_map,
|
.map = msm_iommu_map,
|
||||||
.unmap = msm_iommu_unmap,
|
.unmap = msm_iommu_unmap,
|
||||||
|
/*
|
||||||
|
* Nothing is needed here, the barrier to guarantee
|
||||||
|
* completion of the tlb sync operation is implicitly
|
||||||
|
* taken care when the iommu client does a writel before
|
||||||
|
* kick starting the other master.
|
||||||
|
*/
|
||||||
|
.iotlb_sync = NULL,
|
||||||
.iova_to_phys = msm_iommu_iova_to_phys,
|
.iova_to_phys = msm_iommu_iova_to_phys,
|
||||||
.add_device = msm_iommu_add_device,
|
.add_device = msm_iommu_add_device,
|
||||||
.remove_device = msm_iommu_remove_device,
|
.remove_device = msm_iommu_remove_device,
|
||||||
@ -750,7 +766,6 @@ static int msm_iommu_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
iommu->irq = platform_get_irq(pdev, 0);
|
iommu->irq = platform_get_irq(pdev, 0);
|
||||||
if (iommu->irq < 0) {
|
if (iommu->irq < 0) {
|
||||||
dev_err(iommu->dev, "could not get iommu irq\n");
|
|
||||||
ret = -ENODEV;
|
ret = -ENODEV;
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#include "mtk_iommu.h"
|
#include "mtk_iommu.h"
|
||||||
|
|
||||||
#define REG_MMU_PT_BASE_ADDR 0x000
|
#define REG_MMU_PT_BASE_ADDR 0x000
|
||||||
|
#define MMU_PT_ADDR_MASK GENMASK(31, 7)
|
||||||
|
|
||||||
#define REG_MMU_INVALIDATE 0x020
|
#define REG_MMU_INVALIDATE 0x020
|
||||||
#define F_ALL_INVLD 0x2
|
#define F_ALL_INVLD 0x2
|
||||||
@ -44,12 +45,9 @@
|
|||||||
#define REG_MMU_DCM_DIS 0x050
|
#define REG_MMU_DCM_DIS 0x050
|
||||||
|
|
||||||
#define REG_MMU_CTRL_REG 0x110
|
#define REG_MMU_CTRL_REG 0x110
|
||||||
|
#define F_MMU_TF_PROT_TO_PROGRAM_ADDR (2 << 4)
|
||||||
#define F_MMU_PREFETCH_RT_REPLACE_MOD BIT(4)
|
#define F_MMU_PREFETCH_RT_REPLACE_MOD BIT(4)
|
||||||
#define F_MMU_TF_PROTECT_SEL_SHIFT(data) \
|
#define F_MMU_TF_PROT_TO_PROGRAM_ADDR_MT8173 (2 << 5)
|
||||||
((data)->m4u_plat == M4U_MT2712 ? 4 : 5)
|
|
||||||
/* It's named by F_MMU_TF_PROT_SEL in mt2712. */
|
|
||||||
#define F_MMU_TF_PROTECT_SEL(prot, data) \
|
|
||||||
(((prot) & 0x3) << F_MMU_TF_PROTECT_SEL_SHIFT(data))
|
|
||||||
|
|
||||||
#define REG_MMU_IVRP_PADDR 0x114
|
#define REG_MMU_IVRP_PADDR 0x114
|
||||||
|
|
||||||
@ -66,26 +64,32 @@
|
|||||||
#define F_INT_CLR_BIT BIT(12)
|
#define F_INT_CLR_BIT BIT(12)
|
||||||
|
|
||||||
#define REG_MMU_INT_MAIN_CONTROL 0x124
|
#define REG_MMU_INT_MAIN_CONTROL 0x124
|
||||||
#define F_INT_TRANSLATION_FAULT BIT(0)
|
/* mmu0 | mmu1 */
|
||||||
#define F_INT_MAIN_MULTI_HIT_FAULT BIT(1)
|
#define F_INT_TRANSLATION_FAULT (BIT(0) | BIT(7))
|
||||||
#define F_INT_INVALID_PA_FAULT BIT(2)
|
#define F_INT_MAIN_MULTI_HIT_FAULT (BIT(1) | BIT(8))
|
||||||
#define F_INT_ENTRY_REPLACEMENT_FAULT BIT(3)
|
#define F_INT_INVALID_PA_FAULT (BIT(2) | BIT(9))
|
||||||
#define F_INT_TLB_MISS_FAULT BIT(4)
|
#define F_INT_ENTRY_REPLACEMENT_FAULT (BIT(3) | BIT(10))
|
||||||
#define F_INT_MISS_TRANSACTION_FIFO_FAULT BIT(5)
|
#define F_INT_TLB_MISS_FAULT (BIT(4) | BIT(11))
|
||||||
#define F_INT_PRETETCH_TRANSATION_FIFO_FAULT BIT(6)
|
#define F_INT_MISS_TRANSACTION_FIFO_FAULT (BIT(5) | BIT(12))
|
||||||
|
#define F_INT_PRETETCH_TRANSATION_FIFO_FAULT (BIT(6) | BIT(13))
|
||||||
|
|
||||||
#define REG_MMU_CPE_DONE 0x12C
|
#define REG_MMU_CPE_DONE 0x12C
|
||||||
|
|
||||||
#define REG_MMU_FAULT_ST1 0x134
|
#define REG_MMU_FAULT_ST1 0x134
|
||||||
|
#define F_REG_MMU0_FAULT_MASK GENMASK(6, 0)
|
||||||
|
#define F_REG_MMU1_FAULT_MASK GENMASK(13, 7)
|
||||||
|
|
||||||
#define REG_MMU_FAULT_VA 0x13c
|
#define REG_MMU0_FAULT_VA 0x13c
|
||||||
#define F_MMU_FAULT_VA_WRITE_BIT BIT(1)
|
#define F_MMU_FAULT_VA_WRITE_BIT BIT(1)
|
||||||
#define F_MMU_FAULT_VA_LAYER_BIT BIT(0)
|
#define F_MMU_FAULT_VA_LAYER_BIT BIT(0)
|
||||||
|
|
||||||
#define REG_MMU_INVLD_PA 0x140
|
#define REG_MMU0_INVLD_PA 0x140
|
||||||
#define REG_MMU_INT_ID 0x150
|
#define REG_MMU1_FAULT_VA 0x144
|
||||||
#define F_MMU0_INT_ID_LARB_ID(a) (((a) >> 7) & 0x7)
|
#define REG_MMU1_INVLD_PA 0x148
|
||||||
#define F_MMU0_INT_ID_PORT_ID(a) (((a) >> 2) & 0x1f)
|
#define REG_MMU0_INT_ID 0x150
|
||||||
|
#define REG_MMU1_INT_ID 0x154
|
||||||
|
#define F_MMU_INT_ID_LARB_ID(a) (((a) >> 7) & 0x7)
|
||||||
|
#define F_MMU_INT_ID_PORT_ID(a) (((a) >> 2) & 0x1f)
|
||||||
|
|
||||||
#define MTK_PROTECT_PA_ALIGN 128
|
#define MTK_PROTECT_PA_ALIGN 128
|
||||||
|
|
||||||
@ -107,6 +111,30 @@ struct mtk_iommu_domain {
|
|||||||
|
|
||||||
static const struct iommu_ops mtk_iommu_ops;
|
static const struct iommu_ops mtk_iommu_ops;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In M4U 4GB mode, the physical address is remapped as below:
|
||||||
|
*
|
||||||
|
* CPU Physical address:
|
||||||
|
* ====================
|
||||||
|
*
|
||||||
|
* 0 1G 2G 3G 4G 5G
|
||||||
|
* |---A---|---B---|---C---|---D---|---E---|
|
||||||
|
* +--I/O--+------------Memory-------------+
|
||||||
|
*
|
||||||
|
* IOMMU output physical address:
|
||||||
|
* =============================
|
||||||
|
*
|
||||||
|
* 4G 5G 6G 7G 8G
|
||||||
|
* |---E---|---B---|---C---|---D---|
|
||||||
|
* +------------Memory-------------+
|
||||||
|
*
|
||||||
|
* The Region 'A'(I/O) can NOT be mapped by M4U; For Region 'B'/'C'/'D', the
|
||||||
|
* bit32 of the CPU physical address always is needed to set, and for Region
|
||||||
|
* 'E', the CPU physical address keep as is.
|
||||||
|
* Additionally, The iommu consumers always use the CPU phyiscal address.
|
||||||
|
*/
|
||||||
|
#define MTK_IOMMU_4GB_MODE_REMAP_BASE 0x140000000UL
|
||||||
|
|
||||||
static LIST_HEAD(m4ulist); /* List all the M4U HWs */
|
static LIST_HEAD(m4ulist); /* List all the M4U HWs */
|
||||||
|
|
||||||
#define for_each_m4u(data) list_for_each_entry(data, &m4ulist, list)
|
#define for_each_m4u(data) list_for_each_entry(data, &m4ulist, list)
|
||||||
@ -188,10 +216,32 @@ static void mtk_iommu_tlb_sync(void *cookie)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct iommu_gather_ops mtk_iommu_gather_ops = {
|
static void mtk_iommu_tlb_flush_walk(unsigned long iova, size_t size,
|
||||||
|
size_t granule, void *cookie)
|
||||||
|
{
|
||||||
|
mtk_iommu_tlb_add_flush_nosync(iova, size, granule, false, cookie);
|
||||||
|
mtk_iommu_tlb_sync(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mtk_iommu_tlb_flush_leaf(unsigned long iova, size_t size,
|
||||||
|
size_t granule, void *cookie)
|
||||||
|
{
|
||||||
|
mtk_iommu_tlb_add_flush_nosync(iova, size, granule, true, cookie);
|
||||||
|
mtk_iommu_tlb_sync(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mtk_iommu_tlb_flush_page_nosync(struct iommu_iotlb_gather *gather,
|
||||||
|
unsigned long iova, size_t granule,
|
||||||
|
void *cookie)
|
||||||
|
{
|
||||||
|
mtk_iommu_tlb_add_flush_nosync(iova, granule, granule, true, cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iommu_flush_ops mtk_iommu_flush_ops = {
|
||||||
.tlb_flush_all = mtk_iommu_tlb_flush_all,
|
.tlb_flush_all = mtk_iommu_tlb_flush_all,
|
||||||
.tlb_add_flush = mtk_iommu_tlb_add_flush_nosync,
|
.tlb_flush_walk = mtk_iommu_tlb_flush_walk,
|
||||||
.tlb_sync = mtk_iommu_tlb_sync,
|
.tlb_flush_leaf = mtk_iommu_tlb_flush_leaf,
|
||||||
|
.tlb_add_page = mtk_iommu_tlb_flush_page_nosync,
|
||||||
};
|
};
|
||||||
|
|
||||||
static irqreturn_t mtk_iommu_isr(int irq, void *dev_id)
|
static irqreturn_t mtk_iommu_isr(int irq, void *dev_id)
|
||||||
@ -204,13 +254,21 @@ static irqreturn_t mtk_iommu_isr(int irq, void *dev_id)
|
|||||||
|
|
||||||
/* Read error info from registers */
|
/* Read error info from registers */
|
||||||
int_state = readl_relaxed(data->base + REG_MMU_FAULT_ST1);
|
int_state = readl_relaxed(data->base + REG_MMU_FAULT_ST1);
|
||||||
fault_iova = readl_relaxed(data->base + REG_MMU_FAULT_VA);
|
if (int_state & F_REG_MMU0_FAULT_MASK) {
|
||||||
|
regval = readl_relaxed(data->base + REG_MMU0_INT_ID);
|
||||||
|
fault_iova = readl_relaxed(data->base + REG_MMU0_FAULT_VA);
|
||||||
|
fault_pa = readl_relaxed(data->base + REG_MMU0_INVLD_PA);
|
||||||
|
} else {
|
||||||
|
regval = readl_relaxed(data->base + REG_MMU1_INT_ID);
|
||||||
|
fault_iova = readl_relaxed(data->base + REG_MMU1_FAULT_VA);
|
||||||
|
fault_pa = readl_relaxed(data->base + REG_MMU1_INVLD_PA);
|
||||||
|
}
|
||||||
layer = fault_iova & F_MMU_FAULT_VA_LAYER_BIT;
|
layer = fault_iova & F_MMU_FAULT_VA_LAYER_BIT;
|
||||||
write = fault_iova & F_MMU_FAULT_VA_WRITE_BIT;
|
write = fault_iova & F_MMU_FAULT_VA_WRITE_BIT;
|
||||||
fault_pa = readl_relaxed(data->base + REG_MMU_INVLD_PA);
|
fault_larb = F_MMU_INT_ID_LARB_ID(regval);
|
||||||
regval = readl_relaxed(data->base + REG_MMU_INT_ID);
|
fault_port = F_MMU_INT_ID_PORT_ID(regval);
|
||||||
fault_larb = F_MMU0_INT_ID_LARB_ID(regval);
|
|
||||||
fault_port = F_MMU0_INT_ID_PORT_ID(regval);
|
fault_larb = data->plat_data->larbid_remap[fault_larb];
|
||||||
|
|
||||||
if (report_iommu_fault(&dom->domain, data->dev, fault_iova,
|
if (report_iommu_fault(&dom->domain, data->dev, fault_iova,
|
||||||
write ? IOMMU_FAULT_WRITE : IOMMU_FAULT_READ)) {
|
write ? IOMMU_FAULT_WRITE : IOMMU_FAULT_READ)) {
|
||||||
@ -242,7 +300,7 @@ static void mtk_iommu_config(struct mtk_iommu_data *data,
|
|||||||
for (i = 0; i < fwspec->num_ids; ++i) {
|
for (i = 0; i < fwspec->num_ids; ++i) {
|
||||||
larbid = MTK_M4U_TO_LARB(fwspec->ids[i]);
|
larbid = MTK_M4U_TO_LARB(fwspec->ids[i]);
|
||||||
portid = MTK_M4U_TO_PORT(fwspec->ids[i]);
|
portid = MTK_M4U_TO_PORT(fwspec->ids[i]);
|
||||||
larb_mmu = &data->smi_imu.larb_imu[larbid];
|
larb_mmu = &data->larb_imu[larbid];
|
||||||
|
|
||||||
dev_dbg(dev, "%s iommu port: %d\n",
|
dev_dbg(dev, "%s iommu port: %d\n",
|
||||||
enable ? "enable" : "disable", portid);
|
enable ? "enable" : "disable", portid);
|
||||||
@ -263,17 +321,15 @@ static int mtk_iommu_domain_finalise(struct mtk_iommu_domain *dom)
|
|||||||
dom->cfg = (struct io_pgtable_cfg) {
|
dom->cfg = (struct io_pgtable_cfg) {
|
||||||
.quirks = IO_PGTABLE_QUIRK_ARM_NS |
|
.quirks = IO_PGTABLE_QUIRK_ARM_NS |
|
||||||
IO_PGTABLE_QUIRK_NO_PERMS |
|
IO_PGTABLE_QUIRK_NO_PERMS |
|
||||||
IO_PGTABLE_QUIRK_TLBI_ON_MAP,
|
IO_PGTABLE_QUIRK_TLBI_ON_MAP |
|
||||||
|
IO_PGTABLE_QUIRK_ARM_MTK_EXT,
|
||||||
.pgsize_bitmap = mtk_iommu_ops.pgsize_bitmap,
|
.pgsize_bitmap = mtk_iommu_ops.pgsize_bitmap,
|
||||||
.ias = 32,
|
.ias = 32,
|
||||||
.oas = 32,
|
.oas = 34,
|
||||||
.tlb = &mtk_iommu_gather_ops,
|
.tlb = &mtk_iommu_flush_ops,
|
||||||
.iommu_dev = data->dev,
|
.iommu_dev = data->dev,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (data->enable_4GB)
|
|
||||||
dom->cfg.quirks |= IO_PGTABLE_QUIRK_ARM_MTK_4GB;
|
|
||||||
|
|
||||||
dom->iop = alloc_io_pgtable_ops(ARM_V7S, &dom->cfg, data);
|
dom->iop = alloc_io_pgtable_ops(ARM_V7S, &dom->cfg, data);
|
||||||
if (!dom->iop) {
|
if (!dom->iop) {
|
||||||
dev_err(data->dev, "Failed to alloc io pgtable\n");
|
dev_err(data->dev, "Failed to alloc io pgtable\n");
|
||||||
@ -336,7 +392,7 @@ static int mtk_iommu_attach_device(struct iommu_domain *domain,
|
|||||||
/* Update the pgtable base address register of the M4U HW */
|
/* Update the pgtable base address register of the M4U HW */
|
||||||
if (!data->m4u_dom) {
|
if (!data->m4u_dom) {
|
||||||
data->m4u_dom = dom;
|
data->m4u_dom = dom;
|
||||||
writel(dom->cfg.arm_v7s_cfg.ttbr[0],
|
writel(dom->cfg.arm_v7s_cfg.ttbr[0] & MMU_PT_ADDR_MASK,
|
||||||
data->base + REG_MMU_PT_BASE_ADDR);
|
data->base + REG_MMU_PT_BASE_ADDR);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -359,32 +415,43 @@ static int mtk_iommu_map(struct iommu_domain *domain, unsigned long iova,
|
|||||||
phys_addr_t paddr, size_t size, int prot)
|
phys_addr_t paddr, size_t size, int prot)
|
||||||
{
|
{
|
||||||
struct mtk_iommu_domain *dom = to_mtk_domain(domain);
|
struct mtk_iommu_domain *dom = to_mtk_domain(domain);
|
||||||
|
struct mtk_iommu_data *data = mtk_iommu_get_m4u_data();
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
/* The "4GB mode" M4U physically can not use the lower remap of Dram. */
|
||||||
|
if (data->enable_4GB)
|
||||||
|
paddr |= BIT_ULL(32);
|
||||||
|
|
||||||
spin_lock_irqsave(&dom->pgtlock, flags);
|
spin_lock_irqsave(&dom->pgtlock, flags);
|
||||||
ret = dom->iop->map(dom->iop, iova, paddr & DMA_BIT_MASK(32),
|
ret = dom->iop->map(dom->iop, iova, paddr, size, prot);
|
||||||
size, prot);
|
|
||||||
spin_unlock_irqrestore(&dom->pgtlock, flags);
|
spin_unlock_irqrestore(&dom->pgtlock, flags);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t mtk_iommu_unmap(struct iommu_domain *domain,
|
static size_t mtk_iommu_unmap(struct iommu_domain *domain,
|
||||||
unsigned long iova, size_t size)
|
unsigned long iova, size_t size,
|
||||||
|
struct iommu_iotlb_gather *gather)
|
||||||
{
|
{
|
||||||
struct mtk_iommu_domain *dom = to_mtk_domain(domain);
|
struct mtk_iommu_domain *dom = to_mtk_domain(domain);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
size_t unmapsz;
|
size_t unmapsz;
|
||||||
|
|
||||||
spin_lock_irqsave(&dom->pgtlock, flags);
|
spin_lock_irqsave(&dom->pgtlock, flags);
|
||||||
unmapsz = dom->iop->unmap(dom->iop, iova, size);
|
unmapsz = dom->iop->unmap(dom->iop, iova, size, gather);
|
||||||
spin_unlock_irqrestore(&dom->pgtlock, flags);
|
spin_unlock_irqrestore(&dom->pgtlock, flags);
|
||||||
|
|
||||||
return unmapsz;
|
return unmapsz;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mtk_iommu_iotlb_sync(struct iommu_domain *domain)
|
static void mtk_iommu_flush_iotlb_all(struct iommu_domain *domain)
|
||||||
|
{
|
||||||
|
mtk_iommu_tlb_sync(mtk_iommu_get_m4u_data());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mtk_iommu_iotlb_sync(struct iommu_domain *domain,
|
||||||
|
struct iommu_iotlb_gather *gather)
|
||||||
{
|
{
|
||||||
mtk_iommu_tlb_sync(mtk_iommu_get_m4u_data());
|
mtk_iommu_tlb_sync(mtk_iommu_get_m4u_data());
|
||||||
}
|
}
|
||||||
@ -401,8 +468,8 @@ static phys_addr_t mtk_iommu_iova_to_phys(struct iommu_domain *domain,
|
|||||||
pa = dom->iop->iova_to_phys(dom->iop, iova);
|
pa = dom->iop->iova_to_phys(dom->iop, iova);
|
||||||
spin_unlock_irqrestore(&dom->pgtlock, flags);
|
spin_unlock_irqrestore(&dom->pgtlock, flags);
|
||||||
|
|
||||||
if (data->enable_4GB)
|
if (data->enable_4GB && pa >= MTK_IOMMU_4GB_MODE_REMAP_BASE)
|
||||||
pa |= BIT_ULL(32);
|
pa &= ~BIT_ULL(32);
|
||||||
|
|
||||||
return pa;
|
return pa;
|
||||||
}
|
}
|
||||||
@ -490,7 +557,7 @@ static const struct iommu_ops mtk_iommu_ops = {
|
|||||||
.detach_dev = mtk_iommu_detach_device,
|
.detach_dev = mtk_iommu_detach_device,
|
||||||
.map = mtk_iommu_map,
|
.map = mtk_iommu_map,
|
||||||
.unmap = mtk_iommu_unmap,
|
.unmap = mtk_iommu_unmap,
|
||||||
.flush_iotlb_all = mtk_iommu_iotlb_sync,
|
.flush_iotlb_all = mtk_iommu_flush_iotlb_all,
|
||||||
.iotlb_sync = mtk_iommu_iotlb_sync,
|
.iotlb_sync = mtk_iommu_iotlb_sync,
|
||||||
.iova_to_phys = mtk_iommu_iova_to_phys,
|
.iova_to_phys = mtk_iommu_iova_to_phys,
|
||||||
.add_device = mtk_iommu_add_device,
|
.add_device = mtk_iommu_add_device,
|
||||||
@ -511,9 +578,11 @@ static int mtk_iommu_hw_init(const struct mtk_iommu_data *data)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
regval = F_MMU_TF_PROTECT_SEL(2, data);
|
if (data->plat_data->m4u_plat == M4U_MT8173)
|
||||||
if (data->m4u_plat == M4U_MT8173)
|
regval = F_MMU_PREFETCH_RT_REPLACE_MOD |
|
||||||
regval |= F_MMU_PREFETCH_RT_REPLACE_MOD;
|
F_MMU_TF_PROT_TO_PROGRAM_ADDR_MT8173;
|
||||||
|
else
|
||||||
|
regval = F_MMU_TF_PROT_TO_PROGRAM_ADDR;
|
||||||
writel_relaxed(regval, data->base + REG_MMU_CTRL_REG);
|
writel_relaxed(regval, data->base + REG_MMU_CTRL_REG);
|
||||||
|
|
||||||
regval = F_L2_MULIT_HIT_EN |
|
regval = F_L2_MULIT_HIT_EN |
|
||||||
@ -533,14 +602,14 @@ static int mtk_iommu_hw_init(const struct mtk_iommu_data *data)
|
|||||||
F_INT_PRETETCH_TRANSATION_FIFO_FAULT;
|
F_INT_PRETETCH_TRANSATION_FIFO_FAULT;
|
||||||
writel_relaxed(regval, data->base + REG_MMU_INT_MAIN_CONTROL);
|
writel_relaxed(regval, data->base + REG_MMU_INT_MAIN_CONTROL);
|
||||||
|
|
||||||
if (data->m4u_plat == M4U_MT8173)
|
if (data->plat_data->m4u_plat == M4U_MT8173)
|
||||||
regval = (data->protect_base >> 1) | (data->enable_4GB << 31);
|
regval = (data->protect_base >> 1) | (data->enable_4GB << 31);
|
||||||
else
|
else
|
||||||
regval = lower_32_bits(data->protect_base) |
|
regval = lower_32_bits(data->protect_base) |
|
||||||
upper_32_bits(data->protect_base);
|
upper_32_bits(data->protect_base);
|
||||||
writel_relaxed(regval, data->base + REG_MMU_IVRP_PADDR);
|
writel_relaxed(regval, data->base + REG_MMU_IVRP_PADDR);
|
||||||
|
|
||||||
if (data->enable_4GB && data->m4u_plat != M4U_MT8173) {
|
if (data->enable_4GB && data->plat_data->has_vld_pa_rng) {
|
||||||
/*
|
/*
|
||||||
* If 4GB mode is enabled, the validate PA range is from
|
* If 4GB mode is enabled, the validate PA range is from
|
||||||
* 0x1_0000_0000 to 0x1_ffff_ffff. here record bit[32:30].
|
* 0x1_0000_0000 to 0x1_ffff_ffff. here record bit[32:30].
|
||||||
@ -550,8 +619,7 @@ static int mtk_iommu_hw_init(const struct mtk_iommu_data *data)
|
|||||||
}
|
}
|
||||||
writel_relaxed(0, data->base + REG_MMU_DCM_DIS);
|
writel_relaxed(0, data->base + REG_MMU_DCM_DIS);
|
||||||
|
|
||||||
/* It's MISC control register whose default value is ok except mt8173.*/
|
if (data->plat_data->reset_axi)
|
||||||
if (data->m4u_plat == M4U_MT8173)
|
|
||||||
writel_relaxed(0, data->base + REG_MMU_STANDARD_AXI_MODE);
|
writel_relaxed(0, data->base + REG_MMU_STANDARD_AXI_MODE);
|
||||||
|
|
||||||
if (devm_request_irq(data->dev, data->irq, mtk_iommu_isr, 0,
|
if (devm_request_irq(data->dev, data->irq, mtk_iommu_isr, 0,
|
||||||
@ -584,7 +652,7 @@ static int mtk_iommu_probe(struct platform_device *pdev)
|
|||||||
if (!data)
|
if (!data)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
data->dev = dev;
|
data->dev = dev;
|
||||||
data->m4u_plat = (enum mtk_iommu_plat)of_device_get_match_data(dev);
|
data->plat_data = of_device_get_match_data(dev);
|
||||||
|
|
||||||
/* Protect memory. HW will access here while translation fault.*/
|
/* Protect memory. HW will access here while translation fault.*/
|
||||||
protect = devm_kzalloc(dev, MTK_PROTECT_PA_ALIGN * 2, GFP_KERNEL);
|
protect = devm_kzalloc(dev, MTK_PROTECT_PA_ALIGN * 2, GFP_KERNEL);
|
||||||
@ -594,6 +662,8 @@ static int mtk_iommu_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
/* Whether the current dram is over 4GB */
|
/* Whether the current dram is over 4GB */
|
||||||
data->enable_4GB = !!(max_pfn > (BIT_ULL(32) >> PAGE_SHIFT));
|
data->enable_4GB = !!(max_pfn > (BIT_ULL(32) >> PAGE_SHIFT));
|
||||||
|
if (!data->plat_data->has_4gb_mode)
|
||||||
|
data->enable_4GB = false;
|
||||||
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
data->base = devm_ioremap_resource(dev, res);
|
data->base = devm_ioremap_resource(dev, res);
|
||||||
@ -605,15 +675,16 @@ static int mtk_iommu_probe(struct platform_device *pdev)
|
|||||||
if (data->irq < 0)
|
if (data->irq < 0)
|
||||||
return data->irq;
|
return data->irq;
|
||||||
|
|
||||||
data->bclk = devm_clk_get(dev, "bclk");
|
if (data->plat_data->has_bclk) {
|
||||||
if (IS_ERR(data->bclk))
|
data->bclk = devm_clk_get(dev, "bclk");
|
||||||
return PTR_ERR(data->bclk);
|
if (IS_ERR(data->bclk))
|
||||||
|
return PTR_ERR(data->bclk);
|
||||||
|
}
|
||||||
|
|
||||||
larb_nr = of_count_phandle_with_args(dev->of_node,
|
larb_nr = of_count_phandle_with_args(dev->of_node,
|
||||||
"mediatek,larbs", NULL);
|
"mediatek,larbs", NULL);
|
||||||
if (larb_nr < 0)
|
if (larb_nr < 0)
|
||||||
return larb_nr;
|
return larb_nr;
|
||||||
data->smi_imu.larb_nr = larb_nr;
|
|
||||||
|
|
||||||
for (i = 0; i < larb_nr; i++) {
|
for (i = 0; i < larb_nr; i++) {
|
||||||
struct device_node *larbnode;
|
struct device_node *larbnode;
|
||||||
@ -638,7 +709,7 @@ static int mtk_iommu_probe(struct platform_device *pdev)
|
|||||||
of_node_put(larbnode);
|
of_node_put(larbnode);
|
||||||
return -EPROBE_DEFER;
|
return -EPROBE_DEFER;
|
||||||
}
|
}
|
||||||
data->smi_imu.larb_imu[id].dev = &plarbdev->dev;
|
data->larb_imu[id].dev = &plarbdev->dev;
|
||||||
|
|
||||||
component_match_add_release(dev, &match, release_of,
|
component_match_add_release(dev, &match, release_of,
|
||||||
compare_of, larbnode);
|
compare_of, larbnode);
|
||||||
@ -699,6 +770,7 @@ static int __maybe_unused mtk_iommu_suspend(struct device *dev)
|
|||||||
reg->int_control0 = readl_relaxed(base + REG_MMU_INT_CONTROL0);
|
reg->int_control0 = readl_relaxed(base + REG_MMU_INT_CONTROL0);
|
||||||
reg->int_main_control = readl_relaxed(base + REG_MMU_INT_MAIN_CONTROL);
|
reg->int_main_control = readl_relaxed(base + REG_MMU_INT_MAIN_CONTROL);
|
||||||
reg->ivrp_paddr = readl_relaxed(base + REG_MMU_IVRP_PADDR);
|
reg->ivrp_paddr = readl_relaxed(base + REG_MMU_IVRP_PADDR);
|
||||||
|
reg->vld_pa_rng = readl_relaxed(base + REG_MMU_VLD_PA_RNG);
|
||||||
clk_disable_unprepare(data->bclk);
|
clk_disable_unprepare(data->bclk);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -707,6 +779,7 @@ static int __maybe_unused mtk_iommu_resume(struct device *dev)
|
|||||||
{
|
{
|
||||||
struct mtk_iommu_data *data = dev_get_drvdata(dev);
|
struct mtk_iommu_data *data = dev_get_drvdata(dev);
|
||||||
struct mtk_iommu_suspend_reg *reg = &data->reg;
|
struct mtk_iommu_suspend_reg *reg = &data->reg;
|
||||||
|
struct mtk_iommu_domain *m4u_dom = data->m4u_dom;
|
||||||
void __iomem *base = data->base;
|
void __iomem *base = data->base;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
@ -722,8 +795,9 @@ static int __maybe_unused mtk_iommu_resume(struct device *dev)
|
|||||||
writel_relaxed(reg->int_control0, base + REG_MMU_INT_CONTROL0);
|
writel_relaxed(reg->int_control0, base + REG_MMU_INT_CONTROL0);
|
||||||
writel_relaxed(reg->int_main_control, base + REG_MMU_INT_MAIN_CONTROL);
|
writel_relaxed(reg->int_main_control, base + REG_MMU_INT_MAIN_CONTROL);
|
||||||
writel_relaxed(reg->ivrp_paddr, base + REG_MMU_IVRP_PADDR);
|
writel_relaxed(reg->ivrp_paddr, base + REG_MMU_IVRP_PADDR);
|
||||||
if (data->m4u_dom)
|
writel_relaxed(reg->vld_pa_rng, base + REG_MMU_VLD_PA_RNG);
|
||||||
writel(data->m4u_dom->cfg.arm_v7s_cfg.ttbr[0],
|
if (m4u_dom)
|
||||||
|
writel(m4u_dom->cfg.arm_v7s_cfg.ttbr[0] & MMU_PT_ADDR_MASK,
|
||||||
base + REG_MMU_PT_BASE_ADDR);
|
base + REG_MMU_PT_BASE_ADDR);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -732,9 +806,32 @@ static const struct dev_pm_ops mtk_iommu_pm_ops = {
|
|||||||
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mtk_iommu_suspend, mtk_iommu_resume)
|
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mtk_iommu_suspend, mtk_iommu_resume)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct mtk_iommu_plat_data mt2712_data = {
|
||||||
|
.m4u_plat = M4U_MT2712,
|
||||||
|
.has_4gb_mode = true,
|
||||||
|
.has_bclk = true,
|
||||||
|
.has_vld_pa_rng = true,
|
||||||
|
.larbid_remap = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct mtk_iommu_plat_data mt8173_data = {
|
||||||
|
.m4u_plat = M4U_MT8173,
|
||||||
|
.has_4gb_mode = true,
|
||||||
|
.has_bclk = true,
|
||||||
|
.reset_axi = true,
|
||||||
|
.larbid_remap = {0, 1, 2, 3, 4, 5}, /* Linear mapping. */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct mtk_iommu_plat_data mt8183_data = {
|
||||||
|
.m4u_plat = M4U_MT8183,
|
||||||
|
.reset_axi = true,
|
||||||
|
.larbid_remap = {0, 4, 5, 6, 7, 2, 3, 1},
|
||||||
|
};
|
||||||
|
|
||||||
static const struct of_device_id mtk_iommu_of_ids[] = {
|
static const struct of_device_id mtk_iommu_of_ids[] = {
|
||||||
{ .compatible = "mediatek,mt2712-m4u", .data = (void *)M4U_MT2712},
|
{ .compatible = "mediatek,mt2712-m4u", .data = &mt2712_data},
|
||||||
{ .compatible = "mediatek,mt8173-m4u", .data = (void *)M4U_MT8173},
|
{ .compatible = "mediatek,mt8173-m4u", .data = &mt8173_data},
|
||||||
|
{ .compatible = "mediatek,mt8183-m4u", .data = &mt8183_data},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,12 +24,25 @@ struct mtk_iommu_suspend_reg {
|
|||||||
u32 int_control0;
|
u32 int_control0;
|
||||||
u32 int_main_control;
|
u32 int_main_control;
|
||||||
u32 ivrp_paddr;
|
u32 ivrp_paddr;
|
||||||
|
u32 vld_pa_rng;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum mtk_iommu_plat {
|
enum mtk_iommu_plat {
|
||||||
M4U_MT2701,
|
M4U_MT2701,
|
||||||
M4U_MT2712,
|
M4U_MT2712,
|
||||||
M4U_MT8173,
|
M4U_MT8173,
|
||||||
|
M4U_MT8183,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mtk_iommu_plat_data {
|
||||||
|
enum mtk_iommu_plat m4u_plat;
|
||||||
|
bool has_4gb_mode;
|
||||||
|
|
||||||
|
/* HW will use the EMI clock if there isn't the "bclk". */
|
||||||
|
bool has_bclk;
|
||||||
|
bool has_vld_pa_rng;
|
||||||
|
bool reset_axi;
|
||||||
|
unsigned char larbid_remap[MTK_LARB_NR_MAX];
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mtk_iommu_domain;
|
struct mtk_iommu_domain;
|
||||||
@ -43,14 +56,14 @@ struct mtk_iommu_data {
|
|||||||
struct mtk_iommu_suspend_reg reg;
|
struct mtk_iommu_suspend_reg reg;
|
||||||
struct mtk_iommu_domain *m4u_dom;
|
struct mtk_iommu_domain *m4u_dom;
|
||||||
struct iommu_group *m4u_group;
|
struct iommu_group *m4u_group;
|
||||||
struct mtk_smi_iommu smi_imu; /* SMI larb iommu info */
|
|
||||||
bool enable_4GB;
|
bool enable_4GB;
|
||||||
bool tlb_flush_active;
|
bool tlb_flush_active;
|
||||||
|
|
||||||
struct iommu_device iommu;
|
struct iommu_device iommu;
|
||||||
enum mtk_iommu_plat m4u_plat;
|
const struct mtk_iommu_plat_data *plat_data;
|
||||||
|
|
||||||
struct list_head list;
|
struct list_head list;
|
||||||
|
struct mtk_smi_larb_iommu larb_imu[MTK_LARB_NR_MAX];
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline int compare_of(struct device *dev, void *data)
|
static inline int compare_of(struct device *dev, void *data)
|
||||||
@ -67,14 +80,14 @@ static inline int mtk_iommu_bind(struct device *dev)
|
|||||||
{
|
{
|
||||||
struct mtk_iommu_data *data = dev_get_drvdata(dev);
|
struct mtk_iommu_data *data = dev_get_drvdata(dev);
|
||||||
|
|
||||||
return component_bind_all(dev, &data->smi_imu);
|
return component_bind_all(dev, &data->larb_imu);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void mtk_iommu_unbind(struct device *dev)
|
static inline void mtk_iommu_unbind(struct device *dev)
|
||||||
{
|
{
|
||||||
struct mtk_iommu_data *data = dev_get_drvdata(dev);
|
struct mtk_iommu_data *data = dev_get_drvdata(dev);
|
||||||
|
|
||||||
component_unbind_all(dev, &data->smi_imu);
|
component_unbind_all(dev, &data->larb_imu);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -206,7 +206,7 @@ static void mtk_iommu_config(struct mtk_iommu_data *data,
|
|||||||
for (i = 0; i < fwspec->num_ids; ++i) {
|
for (i = 0; i < fwspec->num_ids; ++i) {
|
||||||
larbid = mt2701_m4u_to_larb(fwspec->ids[i]);
|
larbid = mt2701_m4u_to_larb(fwspec->ids[i]);
|
||||||
portid = mt2701_m4u_to_port(fwspec->ids[i]);
|
portid = mt2701_m4u_to_port(fwspec->ids[i]);
|
||||||
larb_mmu = &data->smi_imu.larb_imu[larbid];
|
larb_mmu = &data->larb_imu[larbid];
|
||||||
|
|
||||||
dev_dbg(dev, "%s iommu port: %d\n",
|
dev_dbg(dev, "%s iommu port: %d\n",
|
||||||
enable ? "enable" : "disable", portid);
|
enable ? "enable" : "disable", portid);
|
||||||
@ -324,7 +324,8 @@ static int mtk_iommu_map(struct iommu_domain *domain, unsigned long iova,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static size_t mtk_iommu_unmap(struct iommu_domain *domain,
|
static size_t mtk_iommu_unmap(struct iommu_domain *domain,
|
||||||
unsigned long iova, size_t size)
|
unsigned long iova, size_t size,
|
||||||
|
struct iommu_iotlb_gather *gather)
|
||||||
{
|
{
|
||||||
struct mtk_iommu_domain *dom = to_mtk_domain(domain);
|
struct mtk_iommu_domain *dom = to_mtk_domain(domain);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
@ -610,14 +611,12 @@ static int mtk_iommu_probe(struct platform_device *pdev)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data->smi_imu.larb_imu[larb_nr].dev = &plarbdev->dev;
|
data->larb_imu[larb_nr].dev = &plarbdev->dev;
|
||||||
component_match_add_release(dev, &match, release_of,
|
component_match_add_release(dev, &match, release_of,
|
||||||
compare_of, larb_spec.np);
|
compare_of, larb_spec.np);
|
||||||
larb_nr++;
|
larb_nr++;
|
||||||
}
|
}
|
||||||
|
|
||||||
data->smi_imu.larb_nr = larb_nr;
|
|
||||||
|
|
||||||
platform_set_drvdata(pdev, data);
|
platform_set_drvdata(pdev, data);
|
||||||
|
|
||||||
ret = mtk_iommu_hw_init(data);
|
ret = mtk_iommu_hw_init(data);
|
||||||
|
@ -35,6 +35,15 @@
|
|||||||
|
|
||||||
static const struct iommu_ops omap_iommu_ops;
|
static const struct iommu_ops omap_iommu_ops;
|
||||||
|
|
||||||
|
struct orphan_dev {
|
||||||
|
struct device *dev;
|
||||||
|
struct list_head node;
|
||||||
|
};
|
||||||
|
|
||||||
|
static LIST_HEAD(orphan_dev_list);
|
||||||
|
|
||||||
|
static DEFINE_SPINLOCK(orphan_lock);
|
||||||
|
|
||||||
#define to_iommu(dev) ((struct omap_iommu *)dev_get_drvdata(dev))
|
#define to_iommu(dev) ((struct omap_iommu *)dev_get_drvdata(dev))
|
||||||
|
|
||||||
/* bitmap of the page sizes currently supported */
|
/* bitmap of the page sizes currently supported */
|
||||||
@ -53,6 +62,8 @@ static const struct iommu_ops omap_iommu_ops;
|
|||||||
static struct platform_driver omap_iommu_driver;
|
static struct platform_driver omap_iommu_driver;
|
||||||
static struct kmem_cache *iopte_cachep;
|
static struct kmem_cache *iopte_cachep;
|
||||||
|
|
||||||
|
static int _omap_iommu_add_device(struct device *dev);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* to_omap_domain - Get struct omap_iommu_domain from generic iommu_domain
|
* to_omap_domain - Get struct omap_iommu_domain from generic iommu_domain
|
||||||
* @dom: generic iommu domain handle
|
* @dom: generic iommu domain handle
|
||||||
@ -65,6 +76,9 @@ static struct omap_iommu_domain *to_omap_domain(struct iommu_domain *dom)
|
|||||||
/**
|
/**
|
||||||
* omap_iommu_save_ctx - Save registers for pm off-mode support
|
* omap_iommu_save_ctx - Save registers for pm off-mode support
|
||||||
* @dev: client device
|
* @dev: client device
|
||||||
|
*
|
||||||
|
* This should be treated as an deprecated API. It is preserved only
|
||||||
|
* to maintain existing functionality for OMAP3 ISP driver.
|
||||||
**/
|
**/
|
||||||
void omap_iommu_save_ctx(struct device *dev)
|
void omap_iommu_save_ctx(struct device *dev)
|
||||||
{
|
{
|
||||||
@ -92,6 +106,9 @@ EXPORT_SYMBOL_GPL(omap_iommu_save_ctx);
|
|||||||
/**
|
/**
|
||||||
* omap_iommu_restore_ctx - Restore registers for pm off-mode support
|
* omap_iommu_restore_ctx - Restore registers for pm off-mode support
|
||||||
* @dev: client device
|
* @dev: client device
|
||||||
|
*
|
||||||
|
* This should be treated as an deprecated API. It is preserved only
|
||||||
|
* to maintain existing functionality for OMAP3 ISP driver.
|
||||||
**/
|
**/
|
||||||
void omap_iommu_restore_ctx(struct device *dev)
|
void omap_iommu_restore_ctx(struct device *dev)
|
||||||
{
|
{
|
||||||
@ -186,36 +203,18 @@ static void omap2_iommu_disable(struct omap_iommu *obj)
|
|||||||
|
|
||||||
static int iommu_enable(struct omap_iommu *obj)
|
static int iommu_enable(struct omap_iommu *obj)
|
||||||
{
|
{
|
||||||
int err;
|
int ret;
|
||||||
struct platform_device *pdev = to_platform_device(obj->dev);
|
|
||||||
struct iommu_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
|
||||||
|
|
||||||
if (pdata && pdata->deassert_reset) {
|
ret = pm_runtime_get_sync(obj->dev);
|
||||||
err = pdata->deassert_reset(pdev, pdata->reset_name);
|
if (ret < 0)
|
||||||
if (err) {
|
pm_runtime_put_noidle(obj->dev);
|
||||||
dev_err(obj->dev, "deassert_reset failed: %d\n", err);
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pm_runtime_get_sync(obj->dev);
|
return ret < 0 ? ret : 0;
|
||||||
|
|
||||||
err = omap2_iommu_enable(obj);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void iommu_disable(struct omap_iommu *obj)
|
static void iommu_disable(struct omap_iommu *obj)
|
||||||
{
|
{
|
||||||
struct platform_device *pdev = to_platform_device(obj->dev);
|
|
||||||
struct iommu_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
|
||||||
|
|
||||||
omap2_iommu_disable(obj);
|
|
||||||
|
|
||||||
pm_runtime_put_sync(obj->dev);
|
pm_runtime_put_sync(obj->dev);
|
||||||
|
|
||||||
if (pdata && pdata->assert_reset)
|
|
||||||
pdata->assert_reset(pdev, pdata->reset_name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -901,15 +900,219 @@ static void omap_iommu_detach(struct omap_iommu *obj)
|
|||||||
|
|
||||||
dma_unmap_single(obj->dev, obj->pd_dma, IOPGD_TABLE_SIZE,
|
dma_unmap_single(obj->dev, obj->pd_dma, IOPGD_TABLE_SIZE,
|
||||||
DMA_TO_DEVICE);
|
DMA_TO_DEVICE);
|
||||||
iommu_disable(obj);
|
|
||||||
obj->pd_dma = 0;
|
obj->pd_dma = 0;
|
||||||
obj->iopgd = NULL;
|
obj->iopgd = NULL;
|
||||||
|
iommu_disable(obj);
|
||||||
|
|
||||||
spin_unlock(&obj->iommu_lock);
|
spin_unlock(&obj->iommu_lock);
|
||||||
|
|
||||||
dev_dbg(obj->dev, "%s: %s\n", __func__, obj->name);
|
dev_dbg(obj->dev, "%s: %s\n", __func__, obj->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void omap_iommu_save_tlb_entries(struct omap_iommu *obj)
|
||||||
|
{
|
||||||
|
struct iotlb_lock lock;
|
||||||
|
struct cr_regs cr;
|
||||||
|
struct cr_regs *tmp;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* check if there are any locked tlbs to save */
|
||||||
|
iotlb_lock_get(obj, &lock);
|
||||||
|
obj->num_cr_ctx = lock.base;
|
||||||
|
if (!obj->num_cr_ctx)
|
||||||
|
return;
|
||||||
|
|
||||||
|
tmp = obj->cr_ctx;
|
||||||
|
for_each_iotlb_cr(obj, obj->num_cr_ctx, i, cr)
|
||||||
|
* tmp++ = cr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void omap_iommu_restore_tlb_entries(struct omap_iommu *obj)
|
||||||
|
{
|
||||||
|
struct iotlb_lock l;
|
||||||
|
struct cr_regs *tmp;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
/* no locked tlbs to restore */
|
||||||
|
if (!obj->num_cr_ctx)
|
||||||
|
return;
|
||||||
|
|
||||||
|
l.base = 0;
|
||||||
|
tmp = obj->cr_ctx;
|
||||||
|
for (i = 0; i < obj->num_cr_ctx; i++, tmp++) {
|
||||||
|
l.vict = i;
|
||||||
|
iotlb_lock_set(obj, &l);
|
||||||
|
iotlb_load_cr(obj, tmp);
|
||||||
|
}
|
||||||
|
l.base = obj->num_cr_ctx;
|
||||||
|
l.vict = i;
|
||||||
|
iotlb_lock_set(obj, &l);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* omap_iommu_domain_deactivate - deactivate attached iommu devices
|
||||||
|
* @domain: iommu domain attached to the target iommu device
|
||||||
|
*
|
||||||
|
* This API allows the client devices of IOMMU devices to suspend
|
||||||
|
* the IOMMUs they control at runtime, after they are idled and
|
||||||
|
* suspended all activity. System Suspend will leverage the PM
|
||||||
|
* driver late callbacks.
|
||||||
|
**/
|
||||||
|
int omap_iommu_domain_deactivate(struct iommu_domain *domain)
|
||||||
|
{
|
||||||
|
struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
|
||||||
|
struct omap_iommu_device *iommu;
|
||||||
|
struct omap_iommu *oiommu;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!omap_domain->dev)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
iommu = omap_domain->iommus;
|
||||||
|
iommu += (omap_domain->num_iommus - 1);
|
||||||
|
for (i = 0; i < omap_domain->num_iommus; i++, iommu--) {
|
||||||
|
oiommu = iommu->iommu_dev;
|
||||||
|
pm_runtime_put_sync(oiommu->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(omap_iommu_domain_deactivate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* omap_iommu_domain_activate - activate attached iommu devices
|
||||||
|
* @domain: iommu domain attached to the target iommu device
|
||||||
|
*
|
||||||
|
* This API allows the client devices of IOMMU devices to resume the
|
||||||
|
* IOMMUs they control at runtime, before they can resume operations.
|
||||||
|
* System Resume will leverage the PM driver late callbacks.
|
||||||
|
**/
|
||||||
|
int omap_iommu_domain_activate(struct iommu_domain *domain)
|
||||||
|
{
|
||||||
|
struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
|
||||||
|
struct omap_iommu_device *iommu;
|
||||||
|
struct omap_iommu *oiommu;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
if (!omap_domain->dev)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
iommu = omap_domain->iommus;
|
||||||
|
for (i = 0; i < omap_domain->num_iommus; i++, iommu++) {
|
||||||
|
oiommu = iommu->iommu_dev;
|
||||||
|
pm_runtime_get_sync(oiommu->dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(omap_iommu_domain_activate);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* omap_iommu_runtime_suspend - disable an iommu device
|
||||||
|
* @dev: iommu device
|
||||||
|
*
|
||||||
|
* This function performs all that is necessary to disable an
|
||||||
|
* IOMMU device, either during final detachment from a client
|
||||||
|
* device, or during system/runtime suspend of the device. This
|
||||||
|
* includes programming all the appropriate IOMMU registers, and
|
||||||
|
* managing the associated omap_hwmod's state and the device's
|
||||||
|
* reset line. This function also saves the context of any
|
||||||
|
* locked TLBs if suspending.
|
||||||
|
**/
|
||||||
|
static __maybe_unused int omap_iommu_runtime_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
|
struct iommu_platform_data *pdata = dev_get_platdata(dev);
|
||||||
|
struct omap_iommu *obj = to_iommu(dev);
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* save the TLBs only during suspend, and not for power down */
|
||||||
|
if (obj->domain && obj->iopgd)
|
||||||
|
omap_iommu_save_tlb_entries(obj);
|
||||||
|
|
||||||
|
omap2_iommu_disable(obj);
|
||||||
|
|
||||||
|
if (pdata && pdata->device_idle)
|
||||||
|
pdata->device_idle(pdev);
|
||||||
|
|
||||||
|
if (pdata && pdata->assert_reset)
|
||||||
|
pdata->assert_reset(pdev, pdata->reset_name);
|
||||||
|
|
||||||
|
if (pdata && pdata->set_pwrdm_constraint) {
|
||||||
|
ret = pdata->set_pwrdm_constraint(pdev, false, &obj->pwrst);
|
||||||
|
if (ret) {
|
||||||
|
dev_warn(obj->dev, "pwrdm_constraint failed to be reset, status = %d\n",
|
||||||
|
ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* omap_iommu_runtime_resume - enable an iommu device
|
||||||
|
* @dev: iommu device
|
||||||
|
*
|
||||||
|
* This function performs all that is necessary to enable an
|
||||||
|
* IOMMU device, either during initial attachment to a client
|
||||||
|
* device, or during system/runtime resume of the device. This
|
||||||
|
* includes programming all the appropriate IOMMU registers, and
|
||||||
|
* managing the associated omap_hwmod's state and the device's
|
||||||
|
* reset line. The function also restores any locked TLBs if
|
||||||
|
* resuming after a suspend.
|
||||||
|
**/
|
||||||
|
static __maybe_unused int omap_iommu_runtime_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct platform_device *pdev = to_platform_device(dev);
|
||||||
|
struct iommu_platform_data *pdata = dev_get_platdata(dev);
|
||||||
|
struct omap_iommu *obj = to_iommu(dev);
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
if (pdata && pdata->set_pwrdm_constraint) {
|
||||||
|
ret = pdata->set_pwrdm_constraint(pdev, true, &obj->pwrst);
|
||||||
|
if (ret) {
|
||||||
|
dev_warn(obj->dev, "pwrdm_constraint failed to be set, status = %d\n",
|
||||||
|
ret);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pdata && pdata->deassert_reset) {
|
||||||
|
ret = pdata->deassert_reset(pdev, pdata->reset_name);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(dev, "deassert_reset failed: %d\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pdata && pdata->device_enable)
|
||||||
|
pdata->device_enable(pdev);
|
||||||
|
|
||||||
|
/* restore the TLBs only during resume, and not for power up */
|
||||||
|
if (obj->domain)
|
||||||
|
omap_iommu_restore_tlb_entries(obj);
|
||||||
|
|
||||||
|
ret = omap2_iommu_enable(obj);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* omap_iommu_suspend_prepare - prepare() dev_pm_ops implementation
|
||||||
|
* @dev: iommu device
|
||||||
|
*
|
||||||
|
* This function performs the necessary checks to determine if the IOMMU
|
||||||
|
* device needs suspending or not. The function checks if the runtime_pm
|
||||||
|
* status of the device is suspended, and returns 1 in that case. This
|
||||||
|
* results in the PM core to skip invoking any of the Sleep PM callbacks
|
||||||
|
* (suspend, suspend_late, resume, resume_early etc).
|
||||||
|
*/
|
||||||
|
static int omap_iommu_prepare(struct device *dev)
|
||||||
|
{
|
||||||
|
if (pm_runtime_status_suspended(dev))
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static bool omap_iommu_can_register(struct platform_device *pdev)
|
static bool omap_iommu_can_register(struct platform_device *pdev)
|
||||||
{
|
{
|
||||||
struct device_node *np = pdev->dev.of_node;
|
struct device_node *np = pdev->dev.of_node;
|
||||||
@ -974,6 +1177,7 @@ static int omap_iommu_probe(struct platform_device *pdev)
|
|||||||
struct omap_iommu *obj;
|
struct omap_iommu *obj;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
struct device_node *of = pdev->dev.of_node;
|
struct device_node *of = pdev->dev.of_node;
|
||||||
|
struct orphan_dev *orphan_dev, *tmp;
|
||||||
|
|
||||||
if (!of) {
|
if (!of) {
|
||||||
pr_err("%s: only DT-based devices are supported\n", __func__);
|
pr_err("%s: only DT-based devices are supported\n", __func__);
|
||||||
@ -984,6 +1188,15 @@ static int omap_iommu_probe(struct platform_device *pdev)
|
|||||||
if (!obj)
|
if (!obj)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* self-manage the ordering dependencies between omap_device_enable/idle
|
||||||
|
* and omap_device_assert/deassert_hardreset API
|
||||||
|
*/
|
||||||
|
if (pdev->dev.pm_domain) {
|
||||||
|
dev_dbg(&pdev->dev, "device pm_domain is being reset\n");
|
||||||
|
pdev->dev.pm_domain = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
obj->name = dev_name(&pdev->dev);
|
obj->name = dev_name(&pdev->dev);
|
||||||
obj->nr_tlb_entries = 32;
|
obj->nr_tlb_entries = 32;
|
||||||
err = of_property_read_u32(of, "ti,#tlb-entries", &obj->nr_tlb_entries);
|
err = of_property_read_u32(of, "ti,#tlb-entries", &obj->nr_tlb_entries);
|
||||||
@ -996,6 +1209,11 @@ static int omap_iommu_probe(struct platform_device *pdev)
|
|||||||
|
|
||||||
obj->dev = &pdev->dev;
|
obj->dev = &pdev->dev;
|
||||||
obj->ctx = (void *)obj + sizeof(*obj);
|
obj->ctx = (void *)obj + sizeof(*obj);
|
||||||
|
obj->cr_ctx = devm_kzalloc(&pdev->dev,
|
||||||
|
sizeof(*obj->cr_ctx) * obj->nr_tlb_entries,
|
||||||
|
GFP_KERNEL);
|
||||||
|
if (!obj->cr_ctx)
|
||||||
|
return -ENOMEM;
|
||||||
|
|
||||||
spin_lock_init(&obj->iommu_lock);
|
spin_lock_init(&obj->iommu_lock);
|
||||||
spin_lock_init(&obj->page_table_lock);
|
spin_lock_init(&obj->page_table_lock);
|
||||||
@ -1036,13 +1254,20 @@ static int omap_iommu_probe(struct platform_device *pdev)
|
|||||||
goto out_sysfs;
|
goto out_sysfs;
|
||||||
}
|
}
|
||||||
|
|
||||||
pm_runtime_irq_safe(obj->dev);
|
|
||||||
pm_runtime_enable(obj->dev);
|
pm_runtime_enable(obj->dev);
|
||||||
|
|
||||||
omap_iommu_debugfs_add(obj);
|
omap_iommu_debugfs_add(obj);
|
||||||
|
|
||||||
dev_info(&pdev->dev, "%s registered\n", obj->name);
|
dev_info(&pdev->dev, "%s registered\n", obj->name);
|
||||||
|
|
||||||
|
list_for_each_entry_safe(orphan_dev, tmp, &orphan_dev_list, node) {
|
||||||
|
err = _omap_iommu_add_device(orphan_dev->dev);
|
||||||
|
if (!err) {
|
||||||
|
list_del(&orphan_dev->node);
|
||||||
|
kfree(orphan_dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
out_sysfs:
|
out_sysfs:
|
||||||
@ -1072,6 +1297,14 @@ static int omap_iommu_remove(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const struct dev_pm_ops omap_iommu_pm_ops = {
|
||||||
|
.prepare = omap_iommu_prepare,
|
||||||
|
SET_LATE_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
|
||||||
|
pm_runtime_force_resume)
|
||||||
|
SET_RUNTIME_PM_OPS(omap_iommu_runtime_suspend,
|
||||||
|
omap_iommu_runtime_resume, NULL)
|
||||||
|
};
|
||||||
|
|
||||||
static const struct of_device_id omap_iommu_of_match[] = {
|
static const struct of_device_id omap_iommu_of_match[] = {
|
||||||
{ .compatible = "ti,omap2-iommu" },
|
{ .compatible = "ti,omap2-iommu" },
|
||||||
{ .compatible = "ti,omap4-iommu" },
|
{ .compatible = "ti,omap4-iommu" },
|
||||||
@ -1085,6 +1318,7 @@ static struct platform_driver omap_iommu_driver = {
|
|||||||
.remove = omap_iommu_remove,
|
.remove = omap_iommu_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "omap-iommu",
|
.name = "omap-iommu",
|
||||||
|
.pm = &omap_iommu_pm_ops,
|
||||||
.of_match_table = of_match_ptr(omap_iommu_of_match),
|
.of_match_table = of_match_ptr(omap_iommu_of_match),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@ -1149,7 +1383,7 @@ static int omap_iommu_map(struct iommu_domain *domain, unsigned long da,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static size_t omap_iommu_unmap(struct iommu_domain *domain, unsigned long da,
|
static size_t omap_iommu_unmap(struct iommu_domain *domain, unsigned long da,
|
||||||
size_t size)
|
size_t size, struct iommu_iotlb_gather *gather)
|
||||||
{
|
{
|
||||||
struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
|
struct omap_iommu_domain *omap_domain = to_omap_domain(domain);
|
||||||
struct device *dev = omap_domain->dev;
|
struct device *dev = omap_domain->dev;
|
||||||
@ -1423,7 +1657,7 @@ static phys_addr_t omap_iommu_iova_to_phys(struct iommu_domain *domain,
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int omap_iommu_add_device(struct device *dev)
|
static int _omap_iommu_add_device(struct device *dev)
|
||||||
{
|
{
|
||||||
struct omap_iommu_arch_data *arch_data, *tmp;
|
struct omap_iommu_arch_data *arch_data, *tmp;
|
||||||
struct omap_iommu *oiommu;
|
struct omap_iommu *oiommu;
|
||||||
@ -1432,6 +1666,8 @@ static int omap_iommu_add_device(struct device *dev)
|
|||||||
struct platform_device *pdev;
|
struct platform_device *pdev;
|
||||||
int num_iommus, i;
|
int num_iommus, i;
|
||||||
int ret;
|
int ret;
|
||||||
|
struct orphan_dev *orphan_dev;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Allocate the archdata iommu structure for DT-based devices.
|
* Allocate the archdata iommu structure for DT-based devices.
|
||||||
@ -1463,10 +1699,26 @@ static int omap_iommu_add_device(struct device *dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
pdev = of_find_device_by_node(np);
|
pdev = of_find_device_by_node(np);
|
||||||
if (WARN_ON(!pdev)) {
|
if (!pdev) {
|
||||||
of_node_put(np);
|
of_node_put(np);
|
||||||
kfree(arch_data);
|
kfree(arch_data);
|
||||||
return -EINVAL;
|
spin_lock_irqsave(&orphan_lock, flags);
|
||||||
|
list_for_each_entry(orphan_dev, &orphan_dev_list,
|
||||||
|
node) {
|
||||||
|
if (orphan_dev->dev == dev)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
spin_unlock_irqrestore(&orphan_lock, flags);
|
||||||
|
|
||||||
|
if (orphan_dev && orphan_dev->dev == dev)
|
||||||
|
return -EPROBE_DEFER;
|
||||||
|
|
||||||
|
orphan_dev = kzalloc(sizeof(*orphan_dev), GFP_KERNEL);
|
||||||
|
orphan_dev->dev = dev;
|
||||||
|
spin_lock_irqsave(&orphan_lock, flags);
|
||||||
|
list_add(&orphan_dev->node, &orphan_dev_list);
|
||||||
|
spin_unlock_irqrestore(&orphan_lock, flags);
|
||||||
|
return -EPROBE_DEFER;
|
||||||
}
|
}
|
||||||
|
|
||||||
oiommu = platform_get_drvdata(pdev);
|
oiommu = platform_get_drvdata(pdev);
|
||||||
@ -1477,6 +1729,7 @@ static int omap_iommu_add_device(struct device *dev)
|
|||||||
}
|
}
|
||||||
|
|
||||||
tmp->iommu_dev = oiommu;
|
tmp->iommu_dev = oiommu;
|
||||||
|
tmp->dev = &pdev->dev;
|
||||||
|
|
||||||
of_node_put(np);
|
of_node_put(np);
|
||||||
}
|
}
|
||||||
@ -1511,6 +1764,17 @@ static int omap_iommu_add_device(struct device *dev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int omap_iommu_add_device(struct device *dev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = _omap_iommu_add_device(dev);
|
||||||
|
if (ret == -EPROBE_DEFER)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static void omap_iommu_remove_device(struct device *dev)
|
static void omap_iommu_remove_device(struct device *dev)
|
||||||
{
|
{
|
||||||
struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
|
struct omap_iommu_arch_data *arch_data = dev->archdata.iommu;
|
||||||
@ -1554,7 +1818,7 @@ static const struct iommu_ops omap_iommu_ops = {
|
|||||||
static int __init omap_iommu_init(void)
|
static int __init omap_iommu_init(void)
|
||||||
{
|
{
|
||||||
struct kmem_cache *p;
|
struct kmem_cache *p;
|
||||||
const unsigned long flags = SLAB_HWCACHE_ALIGN;
|
const slab_flags_t flags = SLAB_HWCACHE_ALIGN;
|
||||||
size_t align = 1 << 10; /* L2 pagetable alignement */
|
size_t align = 1 << 10; /* L2 pagetable alignement */
|
||||||
struct device_node *np;
|
struct device_node *np;
|
||||||
int ret;
|
int ret;
|
||||||
|
@ -73,16 +73,22 @@ struct omap_iommu {
|
|||||||
|
|
||||||
void *ctx; /* iommu context: registres saved area */
|
void *ctx; /* iommu context: registres saved area */
|
||||||
|
|
||||||
|
struct cr_regs *cr_ctx;
|
||||||
|
u32 num_cr_ctx;
|
||||||
|
|
||||||
int has_bus_err_back;
|
int has_bus_err_back;
|
||||||
u32 id;
|
u32 id;
|
||||||
|
|
||||||
struct iommu_device iommu;
|
struct iommu_device iommu;
|
||||||
struct iommu_group *group;
|
struct iommu_group *group;
|
||||||
|
|
||||||
|
u8 pwrst;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct omap_iommu_arch_data - omap iommu private data
|
* struct omap_iommu_arch_data - omap iommu private data
|
||||||
* @iommu_dev: handle of the iommu device
|
* @iommu_dev: handle of the OMAP iommu device
|
||||||
|
* @dev: handle of the iommu device
|
||||||
*
|
*
|
||||||
* This is an omap iommu private data object, which binds an iommu user
|
* This is an omap iommu private data object, which binds an iommu user
|
||||||
* to its iommu device. This object should be placed at the iommu user's
|
* to its iommu device. This object should be placed at the iommu user's
|
||||||
@ -91,6 +97,7 @@ struct omap_iommu {
|
|||||||
*/
|
*/
|
||||||
struct omap_iommu_arch_data {
|
struct omap_iommu_arch_data {
|
||||||
struct omap_iommu *iommu_dev;
|
struct omap_iommu *iommu_dev;
|
||||||
|
struct device *dev;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct cr_regs {
|
struct cr_regs {
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <linux/atomic.h>
|
#include <linux/atomic.h>
|
||||||
|
#include <linux/bitfield.h>
|
||||||
#include <linux/clk.h>
|
#include <linux/clk.h>
|
||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <linux/dma-iommu.h>
|
#include <linux/dma-iommu.h>
|
||||||
@ -32,7 +33,7 @@
|
|||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
#include <linux/spinlock.h>
|
#include <linux/spinlock.h>
|
||||||
|
|
||||||
#include "arm-smmu-regs.h"
|
#include "arm-smmu.h"
|
||||||
|
|
||||||
#define SMMU_INTR_SEL_NS 0x2000
|
#define SMMU_INTR_SEL_NS 0x2000
|
||||||
|
|
||||||
@ -155,7 +156,7 @@ static void qcom_iommu_tlb_inv_range_nosync(unsigned long iova, size_t size,
|
|||||||
struct qcom_iommu_ctx *ctx = to_ctx(fwspec, fwspec->ids[i]);
|
struct qcom_iommu_ctx *ctx = to_ctx(fwspec, fwspec->ids[i]);
|
||||||
size_t s = size;
|
size_t s = size;
|
||||||
|
|
||||||
iova &= ~12UL;
|
iova = (iova >> 12) << 12;
|
||||||
iova |= ctx->asid;
|
iova |= ctx->asid;
|
||||||
do {
|
do {
|
||||||
iommu_writel(ctx, reg, iova);
|
iommu_writel(ctx, reg, iova);
|
||||||
@ -164,10 +165,32 @@ static void qcom_iommu_tlb_inv_range_nosync(unsigned long iova, size_t size,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const struct iommu_gather_ops qcom_gather_ops = {
|
static void qcom_iommu_tlb_flush_walk(unsigned long iova, size_t size,
|
||||||
|
size_t granule, void *cookie)
|
||||||
|
{
|
||||||
|
qcom_iommu_tlb_inv_range_nosync(iova, size, granule, false, cookie);
|
||||||
|
qcom_iommu_tlb_sync(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qcom_iommu_tlb_flush_leaf(unsigned long iova, size_t size,
|
||||||
|
size_t granule, void *cookie)
|
||||||
|
{
|
||||||
|
qcom_iommu_tlb_inv_range_nosync(iova, size, granule, true, cookie);
|
||||||
|
qcom_iommu_tlb_sync(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void qcom_iommu_tlb_add_page(struct iommu_iotlb_gather *gather,
|
||||||
|
unsigned long iova, size_t granule,
|
||||||
|
void *cookie)
|
||||||
|
{
|
||||||
|
qcom_iommu_tlb_inv_range_nosync(iova, granule, granule, true, cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct iommu_flush_ops qcom_flush_ops = {
|
||||||
.tlb_flush_all = qcom_iommu_tlb_inv_context,
|
.tlb_flush_all = qcom_iommu_tlb_inv_context,
|
||||||
.tlb_add_flush = qcom_iommu_tlb_inv_range_nosync,
|
.tlb_flush_walk = qcom_iommu_tlb_flush_walk,
|
||||||
.tlb_sync = qcom_iommu_tlb_sync,
|
.tlb_flush_leaf = qcom_iommu_tlb_flush_leaf,
|
||||||
|
.tlb_add_page = qcom_iommu_tlb_add_page,
|
||||||
};
|
};
|
||||||
|
|
||||||
static irqreturn_t qcom_iommu_fault(int irq, void *dev)
|
static irqreturn_t qcom_iommu_fault(int irq, void *dev)
|
||||||
@ -215,7 +238,7 @@ static int qcom_iommu_init_domain(struct iommu_domain *domain,
|
|||||||
.pgsize_bitmap = qcom_iommu_ops.pgsize_bitmap,
|
.pgsize_bitmap = qcom_iommu_ops.pgsize_bitmap,
|
||||||
.ias = 32,
|
.ias = 32,
|
||||||
.oas = 40,
|
.oas = 40,
|
||||||
.tlb = &qcom_gather_ops,
|
.tlb = &qcom_flush_ops,
|
||||||
.iommu_dev = qcom_iommu->dev,
|
.iommu_dev = qcom_iommu->dev,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -247,16 +270,16 @@ static int qcom_iommu_init_domain(struct iommu_domain *domain,
|
|||||||
/* TTBRs */
|
/* TTBRs */
|
||||||
iommu_writeq(ctx, ARM_SMMU_CB_TTBR0,
|
iommu_writeq(ctx, ARM_SMMU_CB_TTBR0,
|
||||||
pgtbl_cfg.arm_lpae_s1_cfg.ttbr[0] |
|
pgtbl_cfg.arm_lpae_s1_cfg.ttbr[0] |
|
||||||
((u64)ctx->asid << TTBRn_ASID_SHIFT));
|
FIELD_PREP(TTBRn_ASID, ctx->asid));
|
||||||
iommu_writeq(ctx, ARM_SMMU_CB_TTBR1,
|
iommu_writeq(ctx, ARM_SMMU_CB_TTBR1,
|
||||||
pgtbl_cfg.arm_lpae_s1_cfg.ttbr[1] |
|
pgtbl_cfg.arm_lpae_s1_cfg.ttbr[1] |
|
||||||
((u64)ctx->asid << TTBRn_ASID_SHIFT));
|
FIELD_PREP(TTBRn_ASID, ctx->asid));
|
||||||
|
|
||||||
/* TTBCR */
|
/* TCR */
|
||||||
iommu_writel(ctx, ARM_SMMU_CB_TTBCR2,
|
iommu_writel(ctx, ARM_SMMU_CB_TCR2,
|
||||||
(pgtbl_cfg.arm_lpae_s1_cfg.tcr >> 32) |
|
(pgtbl_cfg.arm_lpae_s1_cfg.tcr >> 32) |
|
||||||
TTBCR2_SEP_UPSTREAM);
|
FIELD_PREP(TCR2_SEP, TCR2_SEP_UPSTREAM));
|
||||||
iommu_writel(ctx, ARM_SMMU_CB_TTBCR,
|
iommu_writel(ctx, ARM_SMMU_CB_TCR,
|
||||||
pgtbl_cfg.arm_lpae_s1_cfg.tcr);
|
pgtbl_cfg.arm_lpae_s1_cfg.tcr);
|
||||||
|
|
||||||
/* MAIRs (stage-1 only) */
|
/* MAIRs (stage-1 only) */
|
||||||
@ -417,7 +440,7 @@ static int qcom_iommu_map(struct iommu_domain *domain, unsigned long iova,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static size_t qcom_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
|
static size_t qcom_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
|
||||||
size_t size)
|
size_t size, struct iommu_iotlb_gather *gather)
|
||||||
{
|
{
|
||||||
size_t ret;
|
size_t ret;
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
@ -434,14 +457,14 @@ static size_t qcom_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
|
|||||||
*/
|
*/
|
||||||
pm_runtime_get_sync(qcom_domain->iommu->dev);
|
pm_runtime_get_sync(qcom_domain->iommu->dev);
|
||||||
spin_lock_irqsave(&qcom_domain->pgtbl_lock, flags);
|
spin_lock_irqsave(&qcom_domain->pgtbl_lock, flags);
|
||||||
ret = ops->unmap(ops, iova, size);
|
ret = ops->unmap(ops, iova, size, gather);
|
||||||
spin_unlock_irqrestore(&qcom_domain->pgtbl_lock, flags);
|
spin_unlock_irqrestore(&qcom_domain->pgtbl_lock, flags);
|
||||||
pm_runtime_put_sync(qcom_domain->iommu->dev);
|
pm_runtime_put_sync(qcom_domain->iommu->dev);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void qcom_iommu_iotlb_sync(struct iommu_domain *domain)
|
static void qcom_iommu_flush_iotlb_all(struct iommu_domain *domain)
|
||||||
{
|
{
|
||||||
struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain);
|
struct qcom_iommu_domain *qcom_domain = to_qcom_iommu_domain(domain);
|
||||||
struct io_pgtable *pgtable = container_of(qcom_domain->pgtbl_ops,
|
struct io_pgtable *pgtable = container_of(qcom_domain->pgtbl_ops,
|
||||||
@ -454,6 +477,12 @@ static void qcom_iommu_iotlb_sync(struct iommu_domain *domain)
|
|||||||
pm_runtime_put_sync(qcom_domain->iommu->dev);
|
pm_runtime_put_sync(qcom_domain->iommu->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void qcom_iommu_iotlb_sync(struct iommu_domain *domain,
|
||||||
|
struct iommu_iotlb_gather *gather)
|
||||||
|
{
|
||||||
|
qcom_iommu_flush_iotlb_all(domain);
|
||||||
|
}
|
||||||
|
|
||||||
static phys_addr_t qcom_iommu_iova_to_phys(struct iommu_domain *domain,
|
static phys_addr_t qcom_iommu_iova_to_phys(struct iommu_domain *domain,
|
||||||
dma_addr_t iova)
|
dma_addr_t iova)
|
||||||
{
|
{
|
||||||
@ -581,7 +610,7 @@ static const struct iommu_ops qcom_iommu_ops = {
|
|||||||
.detach_dev = qcom_iommu_detach_dev,
|
.detach_dev = qcom_iommu_detach_dev,
|
||||||
.map = qcom_iommu_map,
|
.map = qcom_iommu_map,
|
||||||
.unmap = qcom_iommu_unmap,
|
.unmap = qcom_iommu_unmap,
|
||||||
.flush_iotlb_all = qcom_iommu_iotlb_sync,
|
.flush_iotlb_all = qcom_iommu_flush_iotlb_all,
|
||||||
.iotlb_sync = qcom_iommu_iotlb_sync,
|
.iotlb_sync = qcom_iommu_iotlb_sync,
|
||||||
.iova_to_phys = qcom_iommu_iova_to_phys,
|
.iova_to_phys = qcom_iommu_iova_to_phys,
|
||||||
.add_device = qcom_iommu_add_device,
|
.add_device = qcom_iommu_add_device,
|
||||||
@ -696,10 +725,8 @@ static int qcom_iommu_ctx_probe(struct platform_device *pdev)
|
|||||||
return PTR_ERR(ctx->base);
|
return PTR_ERR(ctx->base);
|
||||||
|
|
||||||
irq = platform_get_irq(pdev, 0);
|
irq = platform_get_irq(pdev, 0);
|
||||||
if (irq < 0) {
|
if (irq < 0)
|
||||||
dev_err(dev, "failed to get irq\n");
|
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
|
||||||
|
|
||||||
/* clear IRQs before registering fault handler, just in case the
|
/* clear IRQs before registering fault handler, just in case the
|
||||||
* boot-loader left us a surprise:
|
* boot-loader left us a surprise:
|
||||||
@ -775,7 +802,7 @@ static int qcom_iommu_device_probe(struct platform_device *pdev)
|
|||||||
struct qcom_iommu_dev *qcom_iommu;
|
struct qcom_iommu_dev *qcom_iommu;
|
||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
int ret, sz, max_asid = 0;
|
int ret, max_asid = 0;
|
||||||
|
|
||||||
/* find the max asid (which is 1:1 to ctx bank idx), so we know how
|
/* find the max asid (which is 1:1 to ctx bank idx), so we know how
|
||||||
* many child ctx devices we have:
|
* many child ctx devices we have:
|
||||||
@ -783,9 +810,8 @@ static int qcom_iommu_device_probe(struct platform_device *pdev)
|
|||||||
for_each_child_of_node(dev->of_node, child)
|
for_each_child_of_node(dev->of_node, child)
|
||||||
max_asid = max(max_asid, get_asid(child));
|
max_asid = max(max_asid, get_asid(child));
|
||||||
|
|
||||||
sz = sizeof(*qcom_iommu) + (max_asid * sizeof(qcom_iommu->ctxs[0]));
|
qcom_iommu = devm_kzalloc(dev, struct_size(qcom_iommu, ctxs, max_asid),
|
||||||
|
GFP_KERNEL);
|
||||||
qcom_iommu = devm_kzalloc(dev, sz, GFP_KERNEL);
|
|
||||||
if (!qcom_iommu)
|
if (!qcom_iommu)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
qcom_iommu->num_ctxs = max_asid;
|
qcom_iommu->num_ctxs = max_asid;
|
||||||
|
@ -794,7 +794,7 @@ static int rk_iommu_map(struct iommu_domain *domain, unsigned long _iova,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static size_t rk_iommu_unmap(struct iommu_domain *domain, unsigned long _iova,
|
static size_t rk_iommu_unmap(struct iommu_domain *domain, unsigned long _iova,
|
||||||
size_t size)
|
size_t size, struct iommu_iotlb_gather *gather)
|
||||||
{
|
{
|
||||||
struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
|
struct rk_iommu_domain *rk_domain = to_rk_domain(domain);
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
@ -314,7 +314,8 @@ static phys_addr_t s390_iommu_iova_to_phys(struct iommu_domain *domain,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static size_t s390_iommu_unmap(struct iommu_domain *domain,
|
static size_t s390_iommu_unmap(struct iommu_domain *domain,
|
||||||
unsigned long iova, size_t size)
|
unsigned long iova, size_t size,
|
||||||
|
struct iommu_iotlb_gather *gather)
|
||||||
{
|
{
|
||||||
struct s390_domain *s390_domain = to_s390_domain(domain);
|
struct s390_domain *s390_domain = to_s390_domain(domain);
|
||||||
int flags = ZPCI_PTE_INVALID;
|
int flags = ZPCI_PTE_INVALID;
|
||||||
|
@ -207,7 +207,7 @@ static inline int __gart_iommu_unmap(struct gart_device *gart,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static size_t gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
|
static size_t gart_iommu_unmap(struct iommu_domain *domain, unsigned long iova,
|
||||||
size_t bytes)
|
size_t bytes, struct iommu_iotlb_gather *gather)
|
||||||
{
|
{
|
||||||
struct gart_device *gart = gart_handle;
|
struct gart_device *gart = gart_handle;
|
||||||
int err;
|
int err;
|
||||||
@ -273,11 +273,17 @@ static int gart_iommu_of_xlate(struct device *dev,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void gart_iommu_sync(struct iommu_domain *domain)
|
static void gart_iommu_sync_map(struct iommu_domain *domain)
|
||||||
{
|
{
|
||||||
FLUSH_GART_REGS(gart_handle);
|
FLUSH_GART_REGS(gart_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void gart_iommu_sync(struct iommu_domain *domain,
|
||||||
|
struct iommu_iotlb_gather *gather)
|
||||||
|
{
|
||||||
|
gart_iommu_sync_map(domain);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct iommu_ops gart_iommu_ops = {
|
static const struct iommu_ops gart_iommu_ops = {
|
||||||
.capable = gart_iommu_capable,
|
.capable = gart_iommu_capable,
|
||||||
.domain_alloc = gart_iommu_domain_alloc,
|
.domain_alloc = gart_iommu_domain_alloc,
|
||||||
@ -292,7 +298,7 @@ static const struct iommu_ops gart_iommu_ops = {
|
|||||||
.iova_to_phys = gart_iommu_iova_to_phys,
|
.iova_to_phys = gart_iommu_iova_to_phys,
|
||||||
.pgsize_bitmap = GART_IOMMU_PGSIZES,
|
.pgsize_bitmap = GART_IOMMU_PGSIZES,
|
||||||
.of_xlate = gart_iommu_of_xlate,
|
.of_xlate = gart_iommu_of_xlate,
|
||||||
.iotlb_sync_map = gart_iommu_sync,
|
.iotlb_sync_map = gart_iommu_sync_map,
|
||||||
.iotlb_sync = gart_iommu_sync,
|
.iotlb_sync = gart_iommu_sync,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -680,7 +680,7 @@ static int tegra_smmu_map(struct iommu_domain *domain, unsigned long iova,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
|
static size_t tegra_smmu_unmap(struct iommu_domain *domain, unsigned long iova,
|
||||||
size_t size)
|
size_t size, struct iommu_iotlb_gather *gather)
|
||||||
{
|
{
|
||||||
struct tegra_smmu_as *as = to_smmu_as(domain);
|
struct tegra_smmu_as *as = to_smmu_as(domain);
|
||||||
dma_addr_t pte_dma;
|
dma_addr_t pte_dma;
|
||||||
|
@ -751,7 +751,7 @@ static int viommu_map(struct iommu_domain *domain, unsigned long iova,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static size_t viommu_unmap(struct iommu_domain *domain, unsigned long iova,
|
static size_t viommu_unmap(struct iommu_domain *domain, unsigned long iova,
|
||||||
size_t size)
|
size_t size, struct iommu_iotlb_gather *gather)
|
||||||
{
|
{
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
size_t unmapped;
|
size_t unmapped;
|
||||||
@ -797,7 +797,8 @@ static phys_addr_t viommu_iova_to_phys(struct iommu_domain *domain,
|
|||||||
return paddr;
|
return paddr;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void viommu_iotlb_sync(struct iommu_domain *domain)
|
static void viommu_iotlb_sync(struct iommu_domain *domain,
|
||||||
|
struct iommu_iotlb_gather *gather)
|
||||||
{
|
{
|
||||||
struct viommu_domain *vdomain = to_viommu_domain(domain);
|
struct viommu_domain *vdomain = to_viommu_domain(domain);
|
||||||
|
|
||||||
|
@ -41,17 +41,40 @@
|
|||||||
#define SMI_LARB_NONSEC_CON(id) (0x380 + ((id) * 4))
|
#define SMI_LARB_NONSEC_CON(id) (0x380 + ((id) * 4))
|
||||||
#define F_MMU_EN BIT(0)
|
#define F_MMU_EN BIT(0)
|
||||||
|
|
||||||
|
/* SMI COMMON */
|
||||||
|
#define SMI_BUS_SEL 0x220
|
||||||
|
#define SMI_BUS_LARB_SHIFT(larbid) ((larbid) << 1)
|
||||||
|
/* All are MMU0 defaultly. Only specialize mmu1 here. */
|
||||||
|
#define F_MMU1_LARB(larbid) (0x1 << SMI_BUS_LARB_SHIFT(larbid))
|
||||||
|
|
||||||
|
enum mtk_smi_gen {
|
||||||
|
MTK_SMI_GEN1,
|
||||||
|
MTK_SMI_GEN2
|
||||||
|
};
|
||||||
|
|
||||||
|
struct mtk_smi_common_plat {
|
||||||
|
enum mtk_smi_gen gen;
|
||||||
|
bool has_gals;
|
||||||
|
u32 bus_sel; /* Balance some larbs to enter mmu0 or mmu1 */
|
||||||
|
};
|
||||||
|
|
||||||
struct mtk_smi_larb_gen {
|
struct mtk_smi_larb_gen {
|
||||||
bool need_larbid;
|
|
||||||
int port_in_larb[MTK_LARB_NR_MAX + 1];
|
int port_in_larb[MTK_LARB_NR_MAX + 1];
|
||||||
void (*config_port)(struct device *);
|
void (*config_port)(struct device *);
|
||||||
|
unsigned int larb_direct_to_common_mask;
|
||||||
|
bool has_gals;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mtk_smi {
|
struct mtk_smi {
|
||||||
struct device *dev;
|
struct device *dev;
|
||||||
struct clk *clk_apb, *clk_smi;
|
struct clk *clk_apb, *clk_smi;
|
||||||
|
struct clk *clk_gals0, *clk_gals1;
|
||||||
struct clk *clk_async; /*only needed by mt2701*/
|
struct clk *clk_async; /*only needed by mt2701*/
|
||||||
void __iomem *smi_ao_base;
|
union {
|
||||||
|
void __iomem *smi_ao_base; /* only for gen1 */
|
||||||
|
void __iomem *base; /* only for gen2 */
|
||||||
|
};
|
||||||
|
const struct mtk_smi_common_plat *plat;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct mtk_smi_larb { /* larb: local arbiter */
|
struct mtk_smi_larb { /* larb: local arbiter */
|
||||||
@ -63,82 +86,56 @@ struct mtk_smi_larb { /* larb: local arbiter */
|
|||||||
u32 *mmu;
|
u32 *mmu;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum mtk_smi_gen {
|
static int mtk_smi_clk_enable(const struct mtk_smi *smi)
|
||||||
MTK_SMI_GEN1,
|
|
||||||
MTK_SMI_GEN2
|
|
||||||
};
|
|
||||||
|
|
||||||
static int mtk_smi_enable(const struct mtk_smi *smi)
|
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = pm_runtime_get_sync(smi->dev);
|
|
||||||
if (ret < 0)
|
|
||||||
return ret;
|
|
||||||
|
|
||||||
ret = clk_prepare_enable(smi->clk_apb);
|
ret = clk_prepare_enable(smi->clk_apb);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_put_pm;
|
return ret;
|
||||||
|
|
||||||
ret = clk_prepare_enable(smi->clk_smi);
|
ret = clk_prepare_enable(smi->clk_smi);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err_disable_apb;
|
goto err_disable_apb;
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(smi->clk_gals0);
|
||||||
|
if (ret)
|
||||||
|
goto err_disable_smi;
|
||||||
|
|
||||||
|
ret = clk_prepare_enable(smi->clk_gals1);
|
||||||
|
if (ret)
|
||||||
|
goto err_disable_gals0;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
|
err_disable_gals0:
|
||||||
|
clk_disable_unprepare(smi->clk_gals0);
|
||||||
|
err_disable_smi:
|
||||||
|
clk_disable_unprepare(smi->clk_smi);
|
||||||
err_disable_apb:
|
err_disable_apb:
|
||||||
clk_disable_unprepare(smi->clk_apb);
|
clk_disable_unprepare(smi->clk_apb);
|
||||||
err_put_pm:
|
|
||||||
pm_runtime_put_sync(smi->dev);
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mtk_smi_disable(const struct mtk_smi *smi)
|
static void mtk_smi_clk_disable(const struct mtk_smi *smi)
|
||||||
{
|
{
|
||||||
|
clk_disable_unprepare(smi->clk_gals1);
|
||||||
|
clk_disable_unprepare(smi->clk_gals0);
|
||||||
clk_disable_unprepare(smi->clk_smi);
|
clk_disable_unprepare(smi->clk_smi);
|
||||||
clk_disable_unprepare(smi->clk_apb);
|
clk_disable_unprepare(smi->clk_apb);
|
||||||
pm_runtime_put_sync(smi->dev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int mtk_smi_larb_get(struct device *larbdev)
|
int mtk_smi_larb_get(struct device *larbdev)
|
||||||
{
|
{
|
||||||
struct mtk_smi_larb *larb = dev_get_drvdata(larbdev);
|
int ret = pm_runtime_get_sync(larbdev);
|
||||||
const struct mtk_smi_larb_gen *larb_gen = larb->larb_gen;
|
|
||||||
struct mtk_smi *common = dev_get_drvdata(larb->smi_common_dev);
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
/* Enable the smi-common's power and clocks */
|
return (ret < 0) ? ret : 0;
|
||||||
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 */
|
|
||||||
larb_gen->config_port(larbdev);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(mtk_smi_larb_get);
|
EXPORT_SYMBOL_GPL(mtk_smi_larb_get);
|
||||||
|
|
||||||
void mtk_smi_larb_put(struct device *larbdev)
|
void mtk_smi_larb_put(struct device *larbdev)
|
||||||
{
|
{
|
||||||
struct mtk_smi_larb *larb = dev_get_drvdata(larbdev);
|
pm_runtime_put_sync(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);
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(mtk_smi_larb_put);
|
EXPORT_SYMBOL_GPL(mtk_smi_larb_put);
|
||||||
|
|
||||||
@ -146,39 +143,26 @@ static int
|
|||||||
mtk_smi_larb_bind(struct device *dev, struct device *master, void *data)
|
mtk_smi_larb_bind(struct device *dev, struct device *master, void *data)
|
||||||
{
|
{
|
||||||
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
|
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
|
||||||
struct mtk_smi_iommu *smi_iommu = data;
|
struct mtk_smi_larb_iommu *larb_mmu = data;
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
|
||||||
if (larb->larb_gen->need_larbid) {
|
for (i = 0; i < MTK_LARB_NR_MAX; i++) {
|
||||||
larb->mmu = &smi_iommu->larb_imu[larb->larbid].mmu;
|
if (dev == larb_mmu[i].dev) {
|
||||||
return 0;
|
larb->larbid = i;
|
||||||
}
|
larb->mmu = &larb_mmu[i].mmu;
|
||||||
|
|
||||||
/*
|
|
||||||
* If there is no larbid property, Loop to find the corresponding
|
|
||||||
* iommu information.
|
|
||||||
*/
|
|
||||||
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 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void mtk_smi_larb_config_port_mt2712(struct device *dev)
|
static void mtk_smi_larb_config_port_gen2_general(struct device *dev)
|
||||||
{
|
{
|
||||||
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
|
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
|
||||||
u32 reg;
|
u32 reg;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
/*
|
if (BIT(larb->larbid) & larb->larb_gen->larb_direct_to_common_mask)
|
||||||
* larb 8/9 is the bdpsys larb, the iommu_en is enabled defaultly.
|
|
||||||
* Don't need to set it again.
|
|
||||||
*/
|
|
||||||
if (larb->larbid == 8 || larb->larbid == 9)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for_each_set_bit(i, (unsigned long *)larb->mmu, 32) {
|
for_each_set_bit(i, (unsigned long *)larb->mmu, 32) {
|
||||||
@ -243,7 +227,6 @@ static const struct mtk_smi_larb_gen mtk_smi_larb_mt8173 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct mtk_smi_larb_gen mtk_smi_larb_mt2701 = {
|
static const struct mtk_smi_larb_gen mtk_smi_larb_mt2701 = {
|
||||||
.need_larbid = true,
|
|
||||||
.port_in_larb = {
|
.port_in_larb = {
|
||||||
LARB0_PORT_OFFSET, LARB1_PORT_OFFSET,
|
LARB0_PORT_OFFSET, LARB1_PORT_OFFSET,
|
||||||
LARB2_PORT_OFFSET, LARB3_PORT_OFFSET
|
LARB2_PORT_OFFSET, LARB3_PORT_OFFSET
|
||||||
@ -252,8 +235,15 @@ static const struct mtk_smi_larb_gen mtk_smi_larb_mt2701 = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static const struct mtk_smi_larb_gen mtk_smi_larb_mt2712 = {
|
static const struct mtk_smi_larb_gen mtk_smi_larb_mt2712 = {
|
||||||
.need_larbid = true,
|
.config_port = mtk_smi_larb_config_port_gen2_general,
|
||||||
.config_port = mtk_smi_larb_config_port_mt2712,
|
.larb_direct_to_common_mask = BIT(8) | BIT(9), /* bdpsys */
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct mtk_smi_larb_gen mtk_smi_larb_mt8183 = {
|
||||||
|
.has_gals = true,
|
||||||
|
.config_port = mtk_smi_larb_config_port_gen2_general,
|
||||||
|
.larb_direct_to_common_mask = BIT(2) | BIT(3) | BIT(7),
|
||||||
|
/* IPU0 | IPU1 | CCU */
|
||||||
};
|
};
|
||||||
|
|
||||||
static const struct of_device_id mtk_smi_larb_of_ids[] = {
|
static const struct of_device_id mtk_smi_larb_of_ids[] = {
|
||||||
@ -269,6 +259,10 @@ static const struct of_device_id mtk_smi_larb_of_ids[] = {
|
|||||||
.compatible = "mediatek,mt2712-smi-larb",
|
.compatible = "mediatek,mt2712-smi-larb",
|
||||||
.data = &mtk_smi_larb_mt2712
|
.data = &mtk_smi_larb_mt2712
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
.compatible = "mediatek,mt8183-smi-larb",
|
||||||
|
.data = &mtk_smi_larb_mt8183
|
||||||
|
},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -279,7 +273,6 @@ static int mtk_smi_larb_probe(struct platform_device *pdev)
|
|||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct device_node *smi_node;
|
struct device_node *smi_node;
|
||||||
struct platform_device *smi_pdev;
|
struct platform_device *smi_pdev;
|
||||||
int err;
|
|
||||||
|
|
||||||
larb = devm_kzalloc(dev, sizeof(*larb), GFP_KERNEL);
|
larb = devm_kzalloc(dev, sizeof(*larb), GFP_KERNEL);
|
||||||
if (!larb)
|
if (!larb)
|
||||||
@ -298,16 +291,16 @@ static int mtk_smi_larb_probe(struct platform_device *pdev)
|
|||||||
larb->smi.clk_smi = devm_clk_get(dev, "smi");
|
larb->smi.clk_smi = devm_clk_get(dev, "smi");
|
||||||
if (IS_ERR(larb->smi.clk_smi))
|
if (IS_ERR(larb->smi.clk_smi))
|
||||||
return PTR_ERR(larb->smi.clk_smi);
|
return PTR_ERR(larb->smi.clk_smi);
|
||||||
larb->smi.dev = dev;
|
|
||||||
|
|
||||||
if (larb->larb_gen->need_larbid) {
|
if (larb->larb_gen->has_gals) {
|
||||||
err = of_property_read_u32(dev->of_node, "mediatek,larb-id",
|
/* The larbs may still haven't gals even if the SoC support.*/
|
||||||
&larb->larbid);
|
larb->smi.clk_gals0 = devm_clk_get(dev, "gals");
|
||||||
if (err) {
|
if (PTR_ERR(larb->smi.clk_gals0) == -ENOENT)
|
||||||
dev_err(dev, "missing larbid property\n");
|
larb->smi.clk_gals0 = NULL;
|
||||||
return err;
|
else if (IS_ERR(larb->smi.clk_gals0))
|
||||||
}
|
return PTR_ERR(larb->smi.clk_gals0);
|
||||||
}
|
}
|
||||||
|
larb->smi.dev = dev;
|
||||||
|
|
||||||
smi_node = of_parse_phandle(dev->of_node, "mediatek,smi", 0);
|
smi_node = of_parse_phandle(dev->of_node, "mediatek,smi", 0);
|
||||||
if (!smi_node)
|
if (!smi_node)
|
||||||
@ -336,27 +329,86 @@ static int mtk_smi_larb_remove(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int __maybe_unused mtk_smi_larb_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
|
||||||
|
const struct mtk_smi_larb_gen *larb_gen = larb->larb_gen;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/* Power on smi-common. */
|
||||||
|
ret = pm_runtime_get_sync(larb->smi_common_dev);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "Failed to pm get for smi-common(%d).\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = mtk_smi_clk_enable(&larb->smi);
|
||||||
|
if (ret < 0) {
|
||||||
|
dev_err(dev, "Failed to enable clock(%d).\n", ret);
|
||||||
|
pm_runtime_put_sync(larb->smi_common_dev);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Configure the basic setting for this larb */
|
||||||
|
larb_gen->config_port(dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __maybe_unused mtk_smi_larb_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct mtk_smi_larb *larb = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
mtk_smi_clk_disable(&larb->smi);
|
||||||
|
pm_runtime_put_sync(larb->smi_common_dev);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dev_pm_ops smi_larb_pm_ops = {
|
||||||
|
SET_RUNTIME_PM_OPS(mtk_smi_larb_suspend, mtk_smi_larb_resume, NULL)
|
||||||
|
};
|
||||||
|
|
||||||
static struct platform_driver mtk_smi_larb_driver = {
|
static struct platform_driver mtk_smi_larb_driver = {
|
||||||
.probe = mtk_smi_larb_probe,
|
.probe = mtk_smi_larb_probe,
|
||||||
.remove = mtk_smi_larb_remove,
|
.remove = mtk_smi_larb_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "mtk-smi-larb",
|
.name = "mtk-smi-larb",
|
||||||
.of_match_table = mtk_smi_larb_of_ids,
|
.of_match_table = mtk_smi_larb_of_ids,
|
||||||
|
.pm = &smi_larb_pm_ops,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static const struct mtk_smi_common_plat mtk_smi_common_gen1 = {
|
||||||
|
.gen = MTK_SMI_GEN1,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct mtk_smi_common_plat mtk_smi_common_gen2 = {
|
||||||
|
.gen = MTK_SMI_GEN2,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const struct mtk_smi_common_plat mtk_smi_common_mt8183 = {
|
||||||
|
.gen = MTK_SMI_GEN2,
|
||||||
|
.has_gals = true,
|
||||||
|
.bus_sel = F_MMU1_LARB(1) | F_MMU1_LARB(2) | F_MMU1_LARB(5) |
|
||||||
|
F_MMU1_LARB(7),
|
||||||
|
};
|
||||||
|
|
||||||
static const struct of_device_id mtk_smi_common_of_ids[] = {
|
static const struct of_device_id mtk_smi_common_of_ids[] = {
|
||||||
{
|
{
|
||||||
.compatible = "mediatek,mt8173-smi-common",
|
.compatible = "mediatek,mt8173-smi-common",
|
||||||
.data = (void *)MTK_SMI_GEN2
|
.data = &mtk_smi_common_gen2,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.compatible = "mediatek,mt2701-smi-common",
|
.compatible = "mediatek,mt2701-smi-common",
|
||||||
.data = (void *)MTK_SMI_GEN1
|
.data = &mtk_smi_common_gen1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
.compatible = "mediatek,mt2712-smi-common",
|
.compatible = "mediatek,mt2712-smi-common",
|
||||||
.data = (void *)MTK_SMI_GEN2
|
.data = &mtk_smi_common_gen2,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.compatible = "mediatek,mt8183-smi-common",
|
||||||
|
.data = &mtk_smi_common_mt8183,
|
||||||
},
|
},
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
@ -366,13 +418,13 @@ static int mtk_smi_common_probe(struct platform_device *pdev)
|
|||||||
struct device *dev = &pdev->dev;
|
struct device *dev = &pdev->dev;
|
||||||
struct mtk_smi *common;
|
struct mtk_smi *common;
|
||||||
struct resource *res;
|
struct resource *res;
|
||||||
enum mtk_smi_gen smi_gen;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL);
|
common = devm_kzalloc(dev, sizeof(*common), GFP_KERNEL);
|
||||||
if (!common)
|
if (!common)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
common->dev = dev;
|
common->dev = dev;
|
||||||
|
common->plat = of_device_get_match_data(dev);
|
||||||
|
|
||||||
common->clk_apb = devm_clk_get(dev, "apb");
|
common->clk_apb = devm_clk_get(dev, "apb");
|
||||||
if (IS_ERR(common->clk_apb))
|
if (IS_ERR(common->clk_apb))
|
||||||
@ -382,14 +434,23 @@ static int mtk_smi_common_probe(struct platform_device *pdev)
|
|||||||
if (IS_ERR(common->clk_smi))
|
if (IS_ERR(common->clk_smi))
|
||||||
return PTR_ERR(common->clk_smi);
|
return PTR_ERR(common->clk_smi);
|
||||||
|
|
||||||
|
if (common->plat->has_gals) {
|
||||||
|
common->clk_gals0 = devm_clk_get(dev, "gals0");
|
||||||
|
if (IS_ERR(common->clk_gals0))
|
||||||
|
return PTR_ERR(common->clk_gals0);
|
||||||
|
|
||||||
|
common->clk_gals1 = devm_clk_get(dev, "gals1");
|
||||||
|
if (IS_ERR(common->clk_gals1))
|
||||||
|
return PTR_ERR(common->clk_gals1);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* for mtk smi gen 1, we need to get the ao(always on) base to config
|
* for mtk smi gen 1, we need to get the ao(always on) base to config
|
||||||
* m4u port, and we need to enable the aync clock for transform the smi
|
* m4u port, and we need to enable the aync clock for transform the smi
|
||||||
* clock into emi clock domain, but for mtk smi gen2, there's no smi ao
|
* clock into emi clock domain, but for mtk smi gen2, there's no smi ao
|
||||||
* base.
|
* base.
|
||||||
*/
|
*/
|
||||||
smi_gen = (enum mtk_smi_gen)of_device_get_match_data(dev);
|
if (common->plat->gen == MTK_SMI_GEN1) {
|
||||||
if (smi_gen == MTK_SMI_GEN1) {
|
|
||||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
common->smi_ao_base = devm_ioremap_resource(dev, res);
|
common->smi_ao_base = devm_ioremap_resource(dev, res);
|
||||||
if (IS_ERR(common->smi_ao_base))
|
if (IS_ERR(common->smi_ao_base))
|
||||||
@ -402,6 +463,11 @@ static int mtk_smi_common_probe(struct platform_device *pdev)
|
|||||||
ret = clk_prepare_enable(common->clk_async);
|
ret = clk_prepare_enable(common->clk_async);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
|
} else {
|
||||||
|
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||||
|
common->base = devm_ioremap_resource(dev, res);
|
||||||
|
if (IS_ERR(common->base))
|
||||||
|
return PTR_ERR(common->base);
|
||||||
}
|
}
|
||||||
pm_runtime_enable(dev);
|
pm_runtime_enable(dev);
|
||||||
platform_set_drvdata(pdev, common);
|
platform_set_drvdata(pdev, common);
|
||||||
@ -414,12 +480,42 @@ static int mtk_smi_common_remove(struct platform_device *pdev)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int __maybe_unused mtk_smi_common_resume(struct device *dev)
|
||||||
|
{
|
||||||
|
struct mtk_smi *common = dev_get_drvdata(dev);
|
||||||
|
u32 bus_sel = common->plat->bus_sel;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = mtk_smi_clk_enable(common);
|
||||||
|
if (ret) {
|
||||||
|
dev_err(common->dev, "Failed to enable clock(%d).\n", ret);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (common->plat->gen == MTK_SMI_GEN2 && bus_sel)
|
||||||
|
writel(bus_sel, common->base + SMI_BUS_SEL);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __maybe_unused mtk_smi_common_suspend(struct device *dev)
|
||||||
|
{
|
||||||
|
struct mtk_smi *common = dev_get_drvdata(dev);
|
||||||
|
|
||||||
|
mtk_smi_clk_disable(common);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct dev_pm_ops smi_common_pm_ops = {
|
||||||
|
SET_RUNTIME_PM_OPS(mtk_smi_common_suspend, mtk_smi_common_resume, NULL)
|
||||||
|
};
|
||||||
|
|
||||||
static struct platform_driver mtk_smi_common_driver = {
|
static struct platform_driver mtk_smi_common_driver = {
|
||||||
.probe = mtk_smi_common_probe,
|
.probe = mtk_smi_common_probe,
|
||||||
.remove = mtk_smi_common_remove,
|
.remove = mtk_smi_common_remove,
|
||||||
.driver = {
|
.driver = {
|
||||||
.name = "mtk-smi-common",
|
.name = "mtk-smi-common",
|
||||||
.of_match_table = mtk_smi_common_of_ids,
|
.of_match_table = mtk_smi_common_of_ids,
|
||||||
|
.pm = &smi_common_pm_ops,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -650,12 +650,13 @@ static int vfio_iommu_type1_unpin_pages(void *iommu_data,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static long vfio_sync_unpin(struct vfio_dma *dma, struct vfio_domain *domain,
|
static long vfio_sync_unpin(struct vfio_dma *dma, struct vfio_domain *domain,
|
||||||
struct list_head *regions)
|
struct list_head *regions,
|
||||||
|
struct iommu_iotlb_gather *iotlb_gather)
|
||||||
{
|
{
|
||||||
long unlocked = 0;
|
long unlocked = 0;
|
||||||
struct vfio_regions *entry, *next;
|
struct vfio_regions *entry, *next;
|
||||||
|
|
||||||
iommu_tlb_sync(domain->domain);
|
iommu_tlb_sync(domain->domain, iotlb_gather);
|
||||||
|
|
||||||
list_for_each_entry_safe(entry, next, regions, list) {
|
list_for_each_entry_safe(entry, next, regions, list) {
|
||||||
unlocked += vfio_unpin_pages_remote(dma,
|
unlocked += vfio_unpin_pages_remote(dma,
|
||||||
@ -685,18 +686,19 @@ static size_t unmap_unpin_fast(struct vfio_domain *domain,
|
|||||||
struct vfio_dma *dma, dma_addr_t *iova,
|
struct vfio_dma *dma, dma_addr_t *iova,
|
||||||
size_t len, phys_addr_t phys, long *unlocked,
|
size_t len, phys_addr_t phys, long *unlocked,
|
||||||
struct list_head *unmapped_list,
|
struct list_head *unmapped_list,
|
||||||
int *unmapped_cnt)
|
int *unmapped_cnt,
|
||||||
|
struct iommu_iotlb_gather *iotlb_gather)
|
||||||
{
|
{
|
||||||
size_t unmapped = 0;
|
size_t unmapped = 0;
|
||||||
struct vfio_regions *entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
struct vfio_regions *entry = kzalloc(sizeof(*entry), GFP_KERNEL);
|
||||||
|
|
||||||
if (entry) {
|
if (entry) {
|
||||||
unmapped = iommu_unmap_fast(domain->domain, *iova, len);
|
unmapped = iommu_unmap_fast(domain->domain, *iova, len,
|
||||||
|
iotlb_gather);
|
||||||
|
|
||||||
if (!unmapped) {
|
if (!unmapped) {
|
||||||
kfree(entry);
|
kfree(entry);
|
||||||
} else {
|
} else {
|
||||||
iommu_tlb_range_add(domain->domain, *iova, unmapped);
|
|
||||||
entry->iova = *iova;
|
entry->iova = *iova;
|
||||||
entry->phys = phys;
|
entry->phys = phys;
|
||||||
entry->len = unmapped;
|
entry->len = unmapped;
|
||||||
@ -712,8 +714,8 @@ static size_t unmap_unpin_fast(struct vfio_domain *domain,
|
|||||||
* or in case of errors.
|
* or in case of errors.
|
||||||
*/
|
*/
|
||||||
if (*unmapped_cnt >= VFIO_IOMMU_TLB_SYNC_MAX || !unmapped) {
|
if (*unmapped_cnt >= VFIO_IOMMU_TLB_SYNC_MAX || !unmapped) {
|
||||||
*unlocked += vfio_sync_unpin(dma, domain,
|
*unlocked += vfio_sync_unpin(dma, domain, unmapped_list,
|
||||||
unmapped_list);
|
iotlb_gather);
|
||||||
*unmapped_cnt = 0;
|
*unmapped_cnt = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -744,6 +746,7 @@ static long vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma,
|
|||||||
dma_addr_t iova = dma->iova, end = dma->iova + dma->size;
|
dma_addr_t iova = dma->iova, end = dma->iova + dma->size;
|
||||||
struct vfio_domain *domain, *d;
|
struct vfio_domain *domain, *d;
|
||||||
LIST_HEAD(unmapped_region_list);
|
LIST_HEAD(unmapped_region_list);
|
||||||
|
struct iommu_iotlb_gather iotlb_gather;
|
||||||
int unmapped_region_cnt = 0;
|
int unmapped_region_cnt = 0;
|
||||||
long unlocked = 0;
|
long unlocked = 0;
|
||||||
|
|
||||||
@ -768,6 +771,7 @@ static long vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma,
|
|||||||
cond_resched();
|
cond_resched();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
iommu_iotlb_gather_init(&iotlb_gather);
|
||||||
while (iova < end) {
|
while (iova < end) {
|
||||||
size_t unmapped, len;
|
size_t unmapped, len;
|
||||||
phys_addr_t phys, next;
|
phys_addr_t phys, next;
|
||||||
@ -796,7 +800,8 @@ static long vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma,
|
|||||||
*/
|
*/
|
||||||
unmapped = unmap_unpin_fast(domain, dma, &iova, len, phys,
|
unmapped = unmap_unpin_fast(domain, dma, &iova, len, phys,
|
||||||
&unlocked, &unmapped_region_list,
|
&unlocked, &unmapped_region_list,
|
||||||
&unmapped_region_cnt);
|
&unmapped_region_cnt,
|
||||||
|
&iotlb_gather);
|
||||||
if (!unmapped) {
|
if (!unmapped) {
|
||||||
unmapped = unmap_unpin_slow(domain, dma, &iova, len,
|
unmapped = unmap_unpin_slow(domain, dma, &iova, len,
|
||||||
phys, &unlocked);
|
phys, &unlocked);
|
||||||
@ -807,8 +812,10 @@ static long vfio_unmap_unpin(struct vfio_iommu *iommu, struct vfio_dma *dma,
|
|||||||
|
|
||||||
dma->iommu_mapped = false;
|
dma->iommu_mapped = false;
|
||||||
|
|
||||||
if (unmapped_region_cnt)
|
if (unmapped_region_cnt) {
|
||||||
unlocked += vfio_sync_unpin(dma, domain, &unmapped_region_list);
|
unlocked += vfio_sync_unpin(dma, domain, &unmapped_region_list,
|
||||||
|
&iotlb_gather);
|
||||||
|
}
|
||||||
|
|
||||||
if (do_accounting) {
|
if (do_accounting) {
|
||||||
vfio_lock_acct(dma, -unlocked, true);
|
vfio_lock_acct(dma, -unlocked, true);
|
||||||
|
@ -386,8 +386,8 @@ static dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page,
|
|||||||
*/
|
*/
|
||||||
trace_swiotlb_bounced(dev, dev_addr, size, swiotlb_force);
|
trace_swiotlb_bounced(dev, dev_addr, size, swiotlb_force);
|
||||||
|
|
||||||
map = swiotlb_tbl_map_single(dev, start_dma_addr, phys, size, dir,
|
map = swiotlb_tbl_map_single(dev, start_dma_addr, phys,
|
||||||
attrs);
|
size, size, dir, attrs);
|
||||||
if (map == (phys_addr_t)DMA_MAPPING_ERROR)
|
if (map == (phys_addr_t)DMA_MAPPING_ERROR)
|
||||||
return DMA_MAPPING_ERROR;
|
return DMA_MAPPING_ERROR;
|
||||||
|
|
||||||
@ -397,7 +397,7 @@ static dma_addr_t xen_swiotlb_map_page(struct device *dev, struct page *page,
|
|||||||
* Ensure that the address returned is DMA'ble
|
* Ensure that the address returned is DMA'ble
|
||||||
*/
|
*/
|
||||||
if (unlikely(!dma_capable(dev, dev_addr, size))) {
|
if (unlikely(!dma_capable(dev, dev_addr, size))) {
|
||||||
swiotlb_tbl_unmap_single(dev, map, size, dir,
|
swiotlb_tbl_unmap_single(dev, map, size, size, dir,
|
||||||
attrs | DMA_ATTR_SKIP_CPU_SYNC);
|
attrs | DMA_ATTR_SKIP_CPU_SYNC);
|
||||||
return DMA_MAPPING_ERROR;
|
return DMA_MAPPING_ERROR;
|
||||||
}
|
}
|
||||||
@ -433,7 +433,7 @@ static void xen_unmap_single(struct device *hwdev, dma_addr_t dev_addr,
|
|||||||
|
|
||||||
/* NOTE: We use dev_addr here, not paddr! */
|
/* NOTE: We use dev_addr here, not paddr! */
|
||||||
if (is_xen_swiotlb_buffer(dev_addr))
|
if (is_xen_swiotlb_buffer(dev_addr))
|
||||||
swiotlb_tbl_unmap_single(hwdev, paddr, size, dir, attrs);
|
swiotlb_tbl_unmap_single(hwdev, paddr, size, size, dir, attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void xen_swiotlb_unmap_page(struct device *hwdev, dma_addr_t dev_addr,
|
static void xen_swiotlb_unmap_page(struct device *hwdev, dma_addr_t dev_addr,
|
||||||
|
130
include/dt-bindings/memory/mt8183-larb-port.h
Normal file
130
include/dt-bindings/memory/mt8183-larb-port.h
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 MediaTek Inc.
|
||||||
|
* Author: Yong Wu <yong.wu@mediatek.com>
|
||||||
|
*/
|
||||||
|
#ifndef __DTS_IOMMU_PORT_MT8183_H
|
||||||
|
#define __DTS_IOMMU_PORT_MT8183_H
|
||||||
|
|
||||||
|
#define MTK_M4U_ID(larb, port) (((larb) << 5) | (port))
|
||||||
|
|
||||||
|
#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
|
||||||
|
#define M4U_LARB6_ID 6
|
||||||
|
#define M4U_LARB7_ID 7
|
||||||
|
|
||||||
|
/* larb0 */
|
||||||
|
#define M4U_PORT_DISP_OVL0 MTK_M4U_ID(M4U_LARB0_ID, 0)
|
||||||
|
#define M4U_PORT_DISP_2L_OVL0_LARB0 MTK_M4U_ID(M4U_LARB0_ID, 1)
|
||||||
|
#define M4U_PORT_DISP_2L_OVL1_LARB0 MTK_M4U_ID(M4U_LARB0_ID, 2)
|
||||||
|
#define M4U_PORT_DISP_RDMA0 MTK_M4U_ID(M4U_LARB0_ID, 3)
|
||||||
|
#define M4U_PORT_DISP_RDMA1 MTK_M4U_ID(M4U_LARB0_ID, 4)
|
||||||
|
#define M4U_PORT_DISP_WDMA0 MTK_M4U_ID(M4U_LARB0_ID, 5)
|
||||||
|
#define M4U_PORT_MDP_RDMA0 MTK_M4U_ID(M4U_LARB0_ID, 6)
|
||||||
|
#define M4U_PORT_MDP_WROT0 MTK_M4U_ID(M4U_LARB0_ID, 7)
|
||||||
|
#define M4U_PORT_MDP_WDMA0 MTK_M4U_ID(M4U_LARB0_ID, 8)
|
||||||
|
#define M4U_PORT_DISP_FAKE0 MTK_M4U_ID(M4U_LARB0_ID, 9)
|
||||||
|
|
||||||
|
/* 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_VLD_EXT MTK_M4U_ID(M4U_LARB1_ID, 2)
|
||||||
|
#define M4U_PORT_HW_VDEC_AVC_MV_EXT MTK_M4U_ID(M4U_LARB1_ID, 3)
|
||||||
|
#define M4U_PORT_HW_VDEC_PRED_RD_EXT MTK_M4U_ID(M4U_LARB1_ID, 4)
|
||||||
|
#define M4U_PORT_HW_VDEC_PRED_WR_EXT MTK_M4U_ID(M4U_LARB1_ID, 5)
|
||||||
|
#define M4U_PORT_HW_VDEC_PPWRAP_EXT MTK_M4U_ID(M4U_LARB1_ID, 6)
|
||||||
|
|
||||||
|
/* larb2 VPU0 */
|
||||||
|
#define M4U_PORT_IMG_IPUO MTK_M4U_ID(M4U_LARB2_ID, 0)
|
||||||
|
#define M4U_PORT_IMG_IPU3O MTK_M4U_ID(M4U_LARB2_ID, 1)
|
||||||
|
#define M4U_PORT_IMG_IPUI MTK_M4U_ID(M4U_LARB2_ID, 2)
|
||||||
|
|
||||||
|
/* larb3 VPU1 */
|
||||||
|
#define M4U_PORT_CAM_IPUO MTK_M4U_ID(M4U_LARB3_ID, 0)
|
||||||
|
#define M4U_PORT_CAM_IPU2O MTK_M4U_ID(M4U_LARB3_ID, 1)
|
||||||
|
#define M4U_PORT_CAM_IPU3O MTK_M4U_ID(M4U_LARB3_ID, 2)
|
||||||
|
#define M4U_PORT_CAM_IPUI MTK_M4U_ID(M4U_LARB3_ID, 3)
|
||||||
|
#define M4U_PORT_CAM_IPU2I MTK_M4U_ID(M4U_LARB3_ID, 4)
|
||||||
|
|
||||||
|
/* larb4 */
|
||||||
|
#define M4U_PORT_VENC_RCPU MTK_M4U_ID(M4U_LARB4_ID, 0)
|
||||||
|
#define M4U_PORT_VENC_REC MTK_M4U_ID(M4U_LARB4_ID, 1)
|
||||||
|
#define M4U_PORT_VENC_BSDMA MTK_M4U_ID(M4U_LARB4_ID, 2)
|
||||||
|
#define M4U_PORT_VENC_SV_COMV MTK_M4U_ID(M4U_LARB4_ID, 3)
|
||||||
|
#define M4U_PORT_VENC_RD_COMV MTK_M4U_ID(M4U_LARB4_ID, 4)
|
||||||
|
#define M4U_PORT_JPGENC_RDMA MTK_M4U_ID(M4U_LARB4_ID, 5)
|
||||||
|
#define M4U_PORT_JPGENC_BSDMA MTK_M4U_ID(M4U_LARB4_ID, 6)
|
||||||
|
#define M4U_PORT_VENC_CUR_LUMA MTK_M4U_ID(M4U_LARB4_ID, 7)
|
||||||
|
#define M4U_PORT_VENC_CUR_CHROMA MTK_M4U_ID(M4U_LARB4_ID, 8)
|
||||||
|
#define M4U_PORT_VENC_REF_LUMA MTK_M4U_ID(M4U_LARB4_ID, 9)
|
||||||
|
#define M4U_PORT_VENC_REF_CHROMA MTK_M4U_ID(M4U_LARB4_ID, 10)
|
||||||
|
|
||||||
|
/* larb5 */
|
||||||
|
#define M4U_PORT_CAM_IMGI MTK_M4U_ID(M4U_LARB5_ID, 0)
|
||||||
|
#define M4U_PORT_CAM_IMG2O MTK_M4U_ID(M4U_LARB5_ID, 1)
|
||||||
|
#define M4U_PORT_CAM_IMG3O MTK_M4U_ID(M4U_LARB5_ID, 2)
|
||||||
|
#define M4U_PORT_CAM_VIPI MTK_M4U_ID(M4U_LARB5_ID, 3)
|
||||||
|
#define M4U_PORT_CAM_LCEI MTK_M4U_ID(M4U_LARB5_ID, 4)
|
||||||
|
#define M4U_PORT_CAM_SMXI MTK_M4U_ID(M4U_LARB5_ID, 5)
|
||||||
|
#define M4U_PORT_CAM_SMXO MTK_M4U_ID(M4U_LARB5_ID, 6)
|
||||||
|
#define M4U_PORT_CAM_WPE0_RDMA1 MTK_M4U_ID(M4U_LARB5_ID, 7)
|
||||||
|
#define M4U_PORT_CAM_WPE0_RDMA0 MTK_M4U_ID(M4U_LARB5_ID, 8)
|
||||||
|
#define M4U_PORT_CAM_WPE0_WDMA MTK_M4U_ID(M4U_LARB5_ID, 9)
|
||||||
|
#define M4U_PORT_CAM_FDVT_RP MTK_M4U_ID(M4U_LARB5_ID, 10)
|
||||||
|
#define M4U_PORT_CAM_FDVT_WR MTK_M4U_ID(M4U_LARB5_ID, 11)
|
||||||
|
#define M4U_PORT_CAM_FDVT_RB MTK_M4U_ID(M4U_LARB5_ID, 12)
|
||||||
|
#define M4U_PORT_CAM_WPE1_RDMA0 MTK_M4U_ID(M4U_LARB5_ID, 13)
|
||||||
|
#define M4U_PORT_CAM_WPE1_RDMA1 MTK_M4U_ID(M4U_LARB5_ID, 14)
|
||||||
|
#define M4U_PORT_CAM_WPE1_WDMA MTK_M4U_ID(M4U_LARB5_ID, 15)
|
||||||
|
#define M4U_PORT_CAM_DPE_RDMA MTK_M4U_ID(M4U_LARB5_ID, 16)
|
||||||
|
#define M4U_PORT_CAM_DPE_WDMA MTK_M4U_ID(M4U_LARB5_ID, 17)
|
||||||
|
#define M4U_PORT_CAM_MFB_RDMA0 MTK_M4U_ID(M4U_LARB5_ID, 18)
|
||||||
|
#define M4U_PORT_CAM_MFB_RDMA1 MTK_M4U_ID(M4U_LARB5_ID, 19)
|
||||||
|
#define M4U_PORT_CAM_MFB_WDMA MTK_M4U_ID(M4U_LARB5_ID, 20)
|
||||||
|
#define M4U_PORT_CAM_RSC_RDMA0 MTK_M4U_ID(M4U_LARB5_ID, 21)
|
||||||
|
#define M4U_PORT_CAM_RSC_WDMA MTK_M4U_ID(M4U_LARB5_ID, 22)
|
||||||
|
#define M4U_PORT_CAM_OWE_RDMA MTK_M4U_ID(M4U_LARB5_ID, 23)
|
||||||
|
#define M4U_PORT_CAM_OWE_WDMA MTK_M4U_ID(M4U_LARB5_ID, 24)
|
||||||
|
|
||||||
|
/* larb6 */
|
||||||
|
#define M4U_PORT_CAM_IMGO MTK_M4U_ID(M4U_LARB6_ID, 0)
|
||||||
|
#define M4U_PORT_CAM_RRZO MTK_M4U_ID(M4U_LARB6_ID, 1)
|
||||||
|
#define M4U_PORT_CAM_AAO MTK_M4U_ID(M4U_LARB6_ID, 2)
|
||||||
|
#define M4U_PORT_CAM_AFO MTK_M4U_ID(M4U_LARB6_ID, 3)
|
||||||
|
#define M4U_PORT_CAM_LSCI0 MTK_M4U_ID(M4U_LARB6_ID, 4)
|
||||||
|
#define M4U_PORT_CAM_LSCI1 MTK_M4U_ID(M4U_LARB6_ID, 5)
|
||||||
|
#define M4U_PORT_CAM_PDO MTK_M4U_ID(M4U_LARB6_ID, 6)
|
||||||
|
#define M4U_PORT_CAM_BPCI MTK_M4U_ID(M4U_LARB6_ID, 7)
|
||||||
|
#define M4U_PORT_CAM_LCSO MTK_M4U_ID(M4U_LARB6_ID, 8)
|
||||||
|
#define M4U_PORT_CAM_CAM_RSSO_A MTK_M4U_ID(M4U_LARB6_ID, 9)
|
||||||
|
#define M4U_PORT_CAM_UFEO MTK_M4U_ID(M4U_LARB6_ID, 10)
|
||||||
|
#define M4U_PORT_CAM_SOCO MTK_M4U_ID(M4U_LARB6_ID, 11)
|
||||||
|
#define M4U_PORT_CAM_SOC1 MTK_M4U_ID(M4U_LARB6_ID, 12)
|
||||||
|
#define M4U_PORT_CAM_SOC2 MTK_M4U_ID(M4U_LARB6_ID, 13)
|
||||||
|
#define M4U_PORT_CAM_CCUI MTK_M4U_ID(M4U_LARB6_ID, 14)
|
||||||
|
#define M4U_PORT_CAM_CCUO MTK_M4U_ID(M4U_LARB6_ID, 15)
|
||||||
|
#define M4U_PORT_CAM_RAWI_A MTK_M4U_ID(M4U_LARB6_ID, 16)
|
||||||
|
#define M4U_PORT_CAM_CCUG MTK_M4U_ID(M4U_LARB6_ID, 17)
|
||||||
|
#define M4U_PORT_CAM_PSO MTK_M4U_ID(M4U_LARB6_ID, 18)
|
||||||
|
#define M4U_PORT_CAM_AFO_1 MTK_M4U_ID(M4U_LARB6_ID, 19)
|
||||||
|
#define M4U_PORT_CAM_LSCI_2 MTK_M4U_ID(M4U_LARB6_ID, 20)
|
||||||
|
#define M4U_PORT_CAM_PDI MTK_M4U_ID(M4U_LARB6_ID, 21)
|
||||||
|
#define M4U_PORT_CAM_FLKO MTK_M4U_ID(M4U_LARB6_ID, 22)
|
||||||
|
#define M4U_PORT_CAM_LMVO MTK_M4U_ID(M4U_LARB6_ID, 23)
|
||||||
|
#define M4U_PORT_CAM_UFGO MTK_M4U_ID(M4U_LARB6_ID, 24)
|
||||||
|
#define M4U_PORT_CAM_SPARE MTK_M4U_ID(M4U_LARB6_ID, 25)
|
||||||
|
#define M4U_PORT_CAM_SPARE_2 MTK_M4U_ID(M4U_LARB6_ID, 26)
|
||||||
|
#define M4U_PORT_CAM_SPARE_3 MTK_M4U_ID(M4U_LARB6_ID, 27)
|
||||||
|
#define M4U_PORT_CAM_SPARE_4 MTK_M4U_ID(M4U_LARB6_ID, 28)
|
||||||
|
#define M4U_PORT_CAM_SPARE_5 MTK_M4U_ID(M4U_LARB6_ID, 29)
|
||||||
|
#define M4U_PORT_CAM_SPARE_6 MTK_M4U_ID(M4U_LARB6_ID, 30)
|
||||||
|
|
||||||
|
/* CCU */
|
||||||
|
#define M4U_PORT_CCU0 MTK_M4U_ID(M4U_LARB7_ID, 0)
|
||||||
|
#define M4U_PORT_CCU1 MTK_M4U_ID(M4U_LARB7_ID, 1)
|
||||||
|
|
||||||
|
#endif
|
@ -184,6 +184,9 @@ extern int amd_iommu_register_ga_log_notifier(int (*notifier)(u32));
|
|||||||
extern int
|
extern int
|
||||||
amd_iommu_update_ga(int cpu, bool is_run, void *data);
|
amd_iommu_update_ga(int cpu, bool is_run, void *data);
|
||||||
|
|
||||||
|
extern int amd_iommu_activate_guest_mode(void *data);
|
||||||
|
extern int amd_iommu_deactivate_guest_mode(void *data);
|
||||||
|
|
||||||
#else /* defined(CONFIG_AMD_IOMMU) && defined(CONFIG_IRQ_REMAP) */
|
#else /* defined(CONFIG_AMD_IOMMU) && defined(CONFIG_IRQ_REMAP) */
|
||||||
|
|
||||||
static inline int
|
static inline int
|
||||||
@ -198,6 +201,15 @@ amd_iommu_update_ga(int cpu, bool is_run, void *data)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline int amd_iommu_activate_guest_mode(void *data)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int amd_iommu_deactivate_guest_mode(void *data)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
#endif /* defined(CONFIG_AMD_IOMMU) && defined(CONFIG_IRQ_REMAP) */
|
#endif /* defined(CONFIG_AMD_IOMMU) && defined(CONFIG_IRQ_REMAP) */
|
||||||
|
|
||||||
#endif /* _ASM_X86_AMD_IOMMU_H */
|
#endif /* _ASM_X86_AMD_IOMMU_H */
|
||||||
|
@ -311,6 +311,7 @@ enum req_flag_bits {
|
|||||||
__REQ_RAHEAD, /* read ahead, can fail anytime */
|
__REQ_RAHEAD, /* read ahead, can fail anytime */
|
||||||
__REQ_BACKGROUND, /* background IO */
|
__REQ_BACKGROUND, /* background IO */
|
||||||
__REQ_NOWAIT, /* Don't wait if request will block */
|
__REQ_NOWAIT, /* Don't wait if request will block */
|
||||||
|
__REQ_NOWAIT_INLINE, /* Return would-block error inline */
|
||||||
/*
|
/*
|
||||||
* When a shared kthread needs to issue a bio for a cgroup, doing
|
* When a shared kthread needs to issue a bio for a cgroup, doing
|
||||||
* so synchronously can lead to priority inversions as the kthread
|
* so synchronously can lead to priority inversions as the kthread
|
||||||
@ -345,6 +346,7 @@ enum req_flag_bits {
|
|||||||
#define REQ_RAHEAD (1ULL << __REQ_RAHEAD)
|
#define REQ_RAHEAD (1ULL << __REQ_RAHEAD)
|
||||||
#define REQ_BACKGROUND (1ULL << __REQ_BACKGROUND)
|
#define REQ_BACKGROUND (1ULL << __REQ_BACKGROUND)
|
||||||
#define REQ_NOWAIT (1ULL << __REQ_NOWAIT)
|
#define REQ_NOWAIT (1ULL << __REQ_NOWAIT)
|
||||||
|
#define REQ_NOWAIT_INLINE (1ULL << __REQ_NOWAIT_INLINE)
|
||||||
#define REQ_CGROUP_PUNT (1ULL << __REQ_CGROUP_PUNT)
|
#define REQ_CGROUP_PUNT (1ULL << __REQ_CGROUP_PUNT)
|
||||||
|
|
||||||
#define REQ_NOUNMAP (1ULL << __REQ_NOUNMAP)
|
#define REQ_NOUNMAP (1ULL << __REQ_NOUNMAP)
|
||||||
@ -418,12 +420,13 @@ static inline int op_stat_group(unsigned int op)
|
|||||||
|
|
||||||
typedef unsigned int blk_qc_t;
|
typedef unsigned int blk_qc_t;
|
||||||
#define BLK_QC_T_NONE -1U
|
#define BLK_QC_T_NONE -1U
|
||||||
|
#define BLK_QC_T_EAGAIN -2U
|
||||||
#define BLK_QC_T_SHIFT 16
|
#define BLK_QC_T_SHIFT 16
|
||||||
#define BLK_QC_T_INTERNAL (1U << 31)
|
#define BLK_QC_T_INTERNAL (1U << 31)
|
||||||
|
|
||||||
static inline bool blk_qc_t_valid(blk_qc_t cookie)
|
static inline bool blk_qc_t_valid(blk_qc_t cookie)
|
||||||
{
|
{
|
||||||
return cookie != BLK_QC_T_NONE;
|
return cookie != BLK_QC_T_NONE && cookie != BLK_QC_T_EAGAIN;
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned int blk_qc_t_to_queue_num(blk_qc_t cookie)
|
static inline unsigned int blk_qc_t_to_queue_num(blk_qc_t cookie)
|
||||||
|
@ -272,6 +272,8 @@
|
|||||||
#define dma_frcd_type(d) ((d >> 30) & 1)
|
#define dma_frcd_type(d) ((d >> 30) & 1)
|
||||||
#define dma_frcd_fault_reason(c) (c & 0xff)
|
#define dma_frcd_fault_reason(c) (c & 0xff)
|
||||||
#define dma_frcd_source_id(c) (c & 0xffff)
|
#define dma_frcd_source_id(c) (c & 0xffff)
|
||||||
|
#define dma_frcd_pasid_value(c) (((c) >> 8) & 0xfffff)
|
||||||
|
#define dma_frcd_pasid_present(c) (((c) >> 31) & 1)
|
||||||
/* low 64 bit */
|
/* low 64 bit */
|
||||||
#define dma_frcd_page_addr(d) (d & (((u64)-1) << PAGE_SHIFT))
|
#define dma_frcd_page_addr(d) (d & (((u64)-1) << PAGE_SHIFT))
|
||||||
|
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
/* SPDX-License-Identifier: GPL-2.0 */
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
#ifndef __IO_PGTABLE_H
|
#ifndef __IO_PGTABLE_H
|
||||||
#define __IO_PGTABLE_H
|
#define __IO_PGTABLE_H
|
||||||
|
|
||||||
#include <linux/bitops.h>
|
#include <linux/bitops.h>
|
||||||
|
#include <linux/iommu.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Public API for use by IOMMU drivers
|
* Public API for use by IOMMU drivers
|
||||||
@ -17,22 +19,31 @@ enum io_pgtable_fmt {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct iommu_gather_ops - IOMMU callbacks for TLB and page table management.
|
* struct iommu_flush_ops - IOMMU callbacks for TLB and page table management.
|
||||||
*
|
*
|
||||||
* @tlb_flush_all: Synchronously invalidate the entire TLB context.
|
* @tlb_flush_all: Synchronously invalidate the entire TLB context.
|
||||||
* @tlb_add_flush: Queue up a TLB invalidation for a virtual address range.
|
* @tlb_flush_walk: Synchronously invalidate all intermediate TLB state
|
||||||
* @tlb_sync: Ensure any queued TLB invalidation has taken effect, and
|
* (sometimes referred to as the "walk cache") for a virtual
|
||||||
* any corresponding page table updates are visible to the
|
* address range.
|
||||||
* IOMMU.
|
* @tlb_flush_leaf: Synchronously invalidate all leaf TLB state for a virtual
|
||||||
|
* address range.
|
||||||
|
* @tlb_add_page: Optional callback to queue up leaf TLB invalidation for a
|
||||||
|
* single page. IOMMUs that cannot batch TLB invalidation
|
||||||
|
* operations efficiently will typically issue them here, but
|
||||||
|
* others may decide to update the iommu_iotlb_gather structure
|
||||||
|
* and defer the invalidation until iommu_tlb_sync() instead.
|
||||||
*
|
*
|
||||||
* Note that these can all be called in atomic context and must therefore
|
* Note that these can all be called in atomic context and must therefore
|
||||||
* not block.
|
* not block.
|
||||||
*/
|
*/
|
||||||
struct iommu_gather_ops {
|
struct iommu_flush_ops {
|
||||||
void (*tlb_flush_all)(void *cookie);
|
void (*tlb_flush_all)(void *cookie);
|
||||||
void (*tlb_add_flush)(unsigned long iova, size_t size, size_t granule,
|
void (*tlb_flush_walk)(unsigned long iova, size_t size, size_t granule,
|
||||||
bool leaf, void *cookie);
|
void *cookie);
|
||||||
void (*tlb_sync)(void *cookie);
|
void (*tlb_flush_leaf)(unsigned long iova, size_t size, size_t granule,
|
||||||
|
void *cookie);
|
||||||
|
void (*tlb_add_page)(struct iommu_iotlb_gather *gather,
|
||||||
|
unsigned long iova, size_t granule, void *cookie);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -65,10 +76,9 @@ struct io_pgtable_cfg {
|
|||||||
* (unmapped) entries but the hardware might do so anyway, perform
|
* (unmapped) entries but the hardware might do so anyway, perform
|
||||||
* TLB maintenance when mapping as well as when unmapping.
|
* TLB maintenance when mapping as well as when unmapping.
|
||||||
*
|
*
|
||||||
* IO_PGTABLE_QUIRK_ARM_MTK_4GB: (ARM v7s format) Set bit 9 in all
|
* IO_PGTABLE_QUIRK_ARM_MTK_EXT: (ARM v7s format) MediaTek IOMMUs extend
|
||||||
* PTEs, for Mediatek IOMMUs which treat it as a 33rd address bit
|
* to support up to 34 bits PA where the bit32 and bit33 are
|
||||||
* when the SoC is in "4GB mode" and they can only access the high
|
* encoded in the bit9 and bit4 of the PTE respectively.
|
||||||
* remap of DRAM (0x1_00000000 to 0x1_ffffffff).
|
|
||||||
*
|
*
|
||||||
* IO_PGTABLE_QUIRK_NON_STRICT: Skip issuing synchronous leaf TLBIs
|
* IO_PGTABLE_QUIRK_NON_STRICT: Skip issuing synchronous leaf TLBIs
|
||||||
* on unmap, for DMA domains using the flush queue mechanism for
|
* on unmap, for DMA domains using the flush queue mechanism for
|
||||||
@ -77,14 +87,14 @@ struct io_pgtable_cfg {
|
|||||||
#define IO_PGTABLE_QUIRK_ARM_NS BIT(0)
|
#define IO_PGTABLE_QUIRK_ARM_NS BIT(0)
|
||||||
#define IO_PGTABLE_QUIRK_NO_PERMS BIT(1)
|
#define IO_PGTABLE_QUIRK_NO_PERMS BIT(1)
|
||||||
#define IO_PGTABLE_QUIRK_TLBI_ON_MAP BIT(2)
|
#define IO_PGTABLE_QUIRK_TLBI_ON_MAP BIT(2)
|
||||||
#define IO_PGTABLE_QUIRK_ARM_MTK_4GB BIT(3)
|
#define IO_PGTABLE_QUIRK_ARM_MTK_EXT BIT(3)
|
||||||
#define IO_PGTABLE_QUIRK_NON_STRICT BIT(4)
|
#define IO_PGTABLE_QUIRK_NON_STRICT BIT(4)
|
||||||
unsigned long quirks;
|
unsigned long quirks;
|
||||||
unsigned long pgsize_bitmap;
|
unsigned long pgsize_bitmap;
|
||||||
unsigned int ias;
|
unsigned int ias;
|
||||||
unsigned int oas;
|
unsigned int oas;
|
||||||
bool coherent_walk;
|
bool coherent_walk;
|
||||||
const struct iommu_gather_ops *tlb;
|
const struct iommu_flush_ops *tlb;
|
||||||
struct device *iommu_dev;
|
struct device *iommu_dev;
|
||||||
|
|
||||||
/* Low-level data specific to the table format */
|
/* Low-level data specific to the table format */
|
||||||
@ -128,7 +138,7 @@ struct io_pgtable_ops {
|
|||||||
int (*map)(struct io_pgtable_ops *ops, unsigned long iova,
|
int (*map)(struct io_pgtable_ops *ops, unsigned long iova,
|
||||||
phys_addr_t paddr, size_t size, int prot);
|
phys_addr_t paddr, size_t size, int prot);
|
||||||
size_t (*unmap)(struct io_pgtable_ops *ops, unsigned long iova,
|
size_t (*unmap)(struct io_pgtable_ops *ops, unsigned long iova,
|
||||||
size_t size);
|
size_t size, struct iommu_iotlb_gather *gather);
|
||||||
phys_addr_t (*iova_to_phys)(struct io_pgtable_ops *ops,
|
phys_addr_t (*iova_to_phys)(struct io_pgtable_ops *ops,
|
||||||
unsigned long iova);
|
unsigned long iova);
|
||||||
};
|
};
|
||||||
@ -184,15 +194,27 @@ static inline void io_pgtable_tlb_flush_all(struct io_pgtable *iop)
|
|||||||
iop->cfg.tlb->tlb_flush_all(iop->cookie);
|
iop->cfg.tlb->tlb_flush_all(iop->cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void io_pgtable_tlb_add_flush(struct io_pgtable *iop,
|
static inline void
|
||||||
unsigned long iova, size_t size, size_t granule, bool leaf)
|
io_pgtable_tlb_flush_walk(struct io_pgtable *iop, unsigned long iova,
|
||||||
|
size_t size, size_t granule)
|
||||||
{
|
{
|
||||||
iop->cfg.tlb->tlb_add_flush(iova, size, granule, leaf, iop->cookie);
|
iop->cfg.tlb->tlb_flush_walk(iova, size, granule, iop->cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void io_pgtable_tlb_sync(struct io_pgtable *iop)
|
static inline void
|
||||||
|
io_pgtable_tlb_flush_leaf(struct io_pgtable *iop, unsigned long iova,
|
||||||
|
size_t size, size_t granule)
|
||||||
{
|
{
|
||||||
iop->cfg.tlb->tlb_sync(iop->cookie);
|
iop->cfg.tlb->tlb_flush_leaf(iova, size, granule, iop->cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void
|
||||||
|
io_pgtable_tlb_add_page(struct io_pgtable *iop,
|
||||||
|
struct iommu_iotlb_gather * gather, unsigned long iova,
|
||||||
|
size_t granule)
|
||||||
|
{
|
||||||
|
if (iop->cfg.tlb->tlb_add_page)
|
||||||
|
iop->cfg.tlb->tlb_add_page(gather, iova, granule, iop->cookie);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -191,6 +191,23 @@ struct iommu_sva_ops {
|
|||||||
|
|
||||||
#ifdef CONFIG_IOMMU_API
|
#ifdef CONFIG_IOMMU_API
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct iommu_iotlb_gather - Range information for a pending IOTLB flush
|
||||||
|
*
|
||||||
|
* @start: IOVA representing the start of the range to be flushed
|
||||||
|
* @end: IOVA representing the end of the range to be flushed (exclusive)
|
||||||
|
* @pgsize: The interval at which to perform the flush
|
||||||
|
*
|
||||||
|
* This structure is intended to be updated by multiple calls to the
|
||||||
|
* ->unmap() function in struct iommu_ops before eventually being passed
|
||||||
|
* into ->iotlb_sync().
|
||||||
|
*/
|
||||||
|
struct iommu_iotlb_gather {
|
||||||
|
unsigned long start;
|
||||||
|
unsigned long end;
|
||||||
|
size_t pgsize;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct iommu_ops - iommu ops and capabilities
|
* struct iommu_ops - iommu ops and capabilities
|
||||||
* @capable: check capability
|
* @capable: check capability
|
||||||
@ -201,7 +218,6 @@ struct iommu_sva_ops {
|
|||||||
* @map: map a physically contiguous memory region to an iommu domain
|
* @map: map a physically contiguous memory region to an iommu domain
|
||||||
* @unmap: unmap a physically contiguous memory region from an iommu domain
|
* @unmap: unmap a physically contiguous memory region from an iommu domain
|
||||||
* @flush_iotlb_all: Synchronously flush all hardware TLBs for this domain
|
* @flush_iotlb_all: Synchronously flush all hardware TLBs for this domain
|
||||||
* @iotlb_range_add: Add a given iova range to the flush queue for this domain
|
|
||||||
* @iotlb_sync_map: Sync mappings created recently using @map to the hardware
|
* @iotlb_sync_map: Sync mappings created recently using @map to the hardware
|
||||||
* @iotlb_sync: Flush all queued ranges from the hardware TLBs and empty flush
|
* @iotlb_sync: Flush all queued ranges from the hardware TLBs and empty flush
|
||||||
* queue
|
* queue
|
||||||
@ -242,12 +258,11 @@ struct iommu_ops {
|
|||||||
int (*map)(struct iommu_domain *domain, unsigned long iova,
|
int (*map)(struct iommu_domain *domain, unsigned long iova,
|
||||||
phys_addr_t paddr, size_t size, int prot);
|
phys_addr_t paddr, size_t size, int prot);
|
||||||
size_t (*unmap)(struct iommu_domain *domain, unsigned long iova,
|
size_t (*unmap)(struct iommu_domain *domain, unsigned long iova,
|
||||||
size_t size);
|
size_t size, struct iommu_iotlb_gather *iotlb_gather);
|
||||||
void (*flush_iotlb_all)(struct iommu_domain *domain);
|
void (*flush_iotlb_all)(struct iommu_domain *domain);
|
||||||
void (*iotlb_range_add)(struct iommu_domain *domain,
|
|
||||||
unsigned long iova, size_t size);
|
|
||||||
void (*iotlb_sync_map)(struct iommu_domain *domain);
|
void (*iotlb_sync_map)(struct iommu_domain *domain);
|
||||||
void (*iotlb_sync)(struct iommu_domain *domain);
|
void (*iotlb_sync)(struct iommu_domain *domain,
|
||||||
|
struct iommu_iotlb_gather *iotlb_gather);
|
||||||
phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, dma_addr_t iova);
|
phys_addr_t (*iova_to_phys)(struct iommu_domain *domain, dma_addr_t iova);
|
||||||
int (*add_device)(struct device *dev);
|
int (*add_device)(struct device *dev);
|
||||||
void (*remove_device)(struct device *dev);
|
void (*remove_device)(struct device *dev);
|
||||||
@ -378,6 +393,13 @@ static inline struct iommu_device *dev_to_iommu_device(struct device *dev)
|
|||||||
return (struct iommu_device *)dev_get_drvdata(dev);
|
return (struct iommu_device *)dev_get_drvdata(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void iommu_iotlb_gather_init(struct iommu_iotlb_gather *gather)
|
||||||
|
{
|
||||||
|
*gather = (struct iommu_iotlb_gather) {
|
||||||
|
.start = ULONG_MAX,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
#define IOMMU_GROUP_NOTIFY_ADD_DEVICE 1 /* Device added */
|
#define IOMMU_GROUP_NOTIFY_ADD_DEVICE 1 /* Device added */
|
||||||
#define IOMMU_GROUP_NOTIFY_DEL_DEVICE 2 /* Pre Device removed */
|
#define IOMMU_GROUP_NOTIFY_DEL_DEVICE 2 /* Pre Device removed */
|
||||||
#define IOMMU_GROUP_NOTIFY_BIND_DRIVER 3 /* Pre Driver bind */
|
#define IOMMU_GROUP_NOTIFY_BIND_DRIVER 3 /* Pre Driver bind */
|
||||||
@ -402,7 +424,8 @@ extern int iommu_map(struct iommu_domain *domain, unsigned long iova,
|
|||||||
extern size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova,
|
extern size_t iommu_unmap(struct iommu_domain *domain, unsigned long iova,
|
||||||
size_t size);
|
size_t size);
|
||||||
extern size_t iommu_unmap_fast(struct iommu_domain *domain,
|
extern size_t iommu_unmap_fast(struct iommu_domain *domain,
|
||||||
unsigned long iova, size_t size);
|
unsigned long iova, size_t size,
|
||||||
|
struct iommu_iotlb_gather *iotlb_gather);
|
||||||
extern size_t iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
|
extern size_t iommu_map_sg(struct iommu_domain *domain, unsigned long iova,
|
||||||
struct scatterlist *sg,unsigned int nents, int prot);
|
struct scatterlist *sg,unsigned int nents, int prot);
|
||||||
extern phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova);
|
extern phys_addr_t iommu_iova_to_phys(struct iommu_domain *domain, dma_addr_t iova);
|
||||||
@ -413,6 +436,9 @@ extern void iommu_get_resv_regions(struct device *dev, struct list_head *list);
|
|||||||
extern void iommu_put_resv_regions(struct device *dev, struct list_head *list);
|
extern void iommu_put_resv_regions(struct device *dev, struct list_head *list);
|
||||||
extern int iommu_request_dm_for_dev(struct device *dev);
|
extern int iommu_request_dm_for_dev(struct device *dev);
|
||||||
extern int iommu_request_dma_domain_for_dev(struct device *dev);
|
extern int iommu_request_dma_domain_for_dev(struct device *dev);
|
||||||
|
extern void iommu_set_default_passthrough(bool cmd_line);
|
||||||
|
extern void iommu_set_default_translated(bool cmd_line);
|
||||||
|
extern bool iommu_default_passthrough(void);
|
||||||
extern struct iommu_resv_region *
|
extern struct iommu_resv_region *
|
||||||
iommu_alloc_resv_region(phys_addr_t start, size_t length, int prot,
|
iommu_alloc_resv_region(phys_addr_t start, size_t length, int prot,
|
||||||
enum iommu_resv_type type);
|
enum iommu_resv_type type);
|
||||||
@ -476,17 +502,38 @@ static inline void iommu_flush_tlb_all(struct iommu_domain *domain)
|
|||||||
domain->ops->flush_iotlb_all(domain);
|
domain->ops->flush_iotlb_all(domain);
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void iommu_tlb_range_add(struct iommu_domain *domain,
|
static inline void iommu_tlb_sync(struct iommu_domain *domain,
|
||||||
unsigned long iova, size_t size)
|
struct iommu_iotlb_gather *iotlb_gather)
|
||||||
{
|
|
||||||
if (domain->ops->iotlb_range_add)
|
|
||||||
domain->ops->iotlb_range_add(domain, iova, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void iommu_tlb_sync(struct iommu_domain *domain)
|
|
||||||
{
|
{
|
||||||
if (domain->ops->iotlb_sync)
|
if (domain->ops->iotlb_sync)
|
||||||
domain->ops->iotlb_sync(domain);
|
domain->ops->iotlb_sync(domain, iotlb_gather);
|
||||||
|
|
||||||
|
iommu_iotlb_gather_init(iotlb_gather);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void iommu_iotlb_gather_add_page(struct iommu_domain *domain,
|
||||||
|
struct iommu_iotlb_gather *gather,
|
||||||
|
unsigned long iova, size_t size)
|
||||||
|
{
|
||||||
|
unsigned long start = iova, end = start + size;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the new page is disjoint from the current range or is mapped at
|
||||||
|
* a different granularity, then sync the TLB so that the gather
|
||||||
|
* structure can be rewritten.
|
||||||
|
*/
|
||||||
|
if (gather->pgsize != size ||
|
||||||
|
end < gather->start || start > gather->end) {
|
||||||
|
if (gather->pgsize)
|
||||||
|
iommu_tlb_sync(domain, gather);
|
||||||
|
gather->pgsize = size;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gather->end < end)
|
||||||
|
gather->end = end;
|
||||||
|
|
||||||
|
if (gather->start > start)
|
||||||
|
gather->start = start;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* PCI device grouping function */
|
/* PCI device grouping function */
|
||||||
@ -567,6 +614,7 @@ struct iommu_group {};
|
|||||||
struct iommu_fwspec {};
|
struct iommu_fwspec {};
|
||||||
struct iommu_device {};
|
struct iommu_device {};
|
||||||
struct iommu_fault_param {};
|
struct iommu_fault_param {};
|
||||||
|
struct iommu_iotlb_gather {};
|
||||||
|
|
||||||
static inline bool iommu_present(struct bus_type *bus)
|
static inline bool iommu_present(struct bus_type *bus)
|
||||||
{
|
{
|
||||||
@ -621,7 +669,8 @@ static inline size_t iommu_unmap(struct iommu_domain *domain,
|
|||||||
}
|
}
|
||||||
|
|
||||||
static inline size_t iommu_unmap_fast(struct iommu_domain *domain,
|
static inline size_t iommu_unmap_fast(struct iommu_domain *domain,
|
||||||
unsigned long iova, int gfp_order)
|
unsigned long iova, int gfp_order,
|
||||||
|
struct iommu_iotlb_gather *iotlb_gather)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -637,12 +686,8 @@ static inline void iommu_flush_tlb_all(struct iommu_domain *domain)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void iommu_tlb_range_add(struct iommu_domain *domain,
|
static inline void iommu_tlb_sync(struct iommu_domain *domain,
|
||||||
unsigned long iova, size_t size)
|
struct iommu_iotlb_gather *iotlb_gather)
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline void iommu_tlb_sync(struct iommu_domain *domain)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -694,6 +739,19 @@ static inline int iommu_request_dma_domain_for_dev(struct device *dev)
|
|||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void iommu_set_default_passthrough(bool cmd_line)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void iommu_set_default_translated(bool cmd_line)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool iommu_default_passthrough(void)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static inline int iommu_attach_group(struct iommu_domain *domain,
|
static inline int iommu_attach_group(struct iommu_domain *domain,
|
||||||
struct iommu_group *group)
|
struct iommu_group *group)
|
||||||
{
|
{
|
||||||
@ -827,6 +885,16 @@ static inline struct iommu_device *dev_to_iommu_device(struct device *dev)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline void iommu_iotlb_gather_init(struct iommu_iotlb_gather *gather)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void iommu_iotlb_gather_add_page(struct iommu_domain *domain,
|
||||||
|
struct iommu_iotlb_gather *gather,
|
||||||
|
unsigned long iova, size_t size)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
static inline void iommu_device_unregister(struct iommu_device *iommu)
|
static inline void iommu_device_unregister(struct iommu_device *iommu)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -10,12 +10,27 @@
|
|||||||
#ifndef _OMAP_IOMMU_H_
|
#ifndef _OMAP_IOMMU_H_
|
||||||
#define _OMAP_IOMMU_H_
|
#define _OMAP_IOMMU_H_
|
||||||
|
|
||||||
|
struct iommu_domain;
|
||||||
|
|
||||||
#ifdef CONFIG_OMAP_IOMMU
|
#ifdef CONFIG_OMAP_IOMMU
|
||||||
extern void omap_iommu_save_ctx(struct device *dev);
|
extern void omap_iommu_save_ctx(struct device *dev);
|
||||||
extern void omap_iommu_restore_ctx(struct device *dev);
|
extern void omap_iommu_restore_ctx(struct device *dev);
|
||||||
|
|
||||||
|
int omap_iommu_domain_deactivate(struct iommu_domain *domain);
|
||||||
|
int omap_iommu_domain_activate(struct iommu_domain *domain);
|
||||||
#else
|
#else
|
||||||
static inline void omap_iommu_save_ctx(struct device *dev) {}
|
static inline void omap_iommu_save_ctx(struct device *dev) {}
|
||||||
static inline void omap_iommu_restore_ctx(struct device *dev) {}
|
static inline void omap_iommu_restore_ctx(struct device *dev) {}
|
||||||
|
|
||||||
|
static inline int omap_iommu_domain_deactivate(struct iommu_domain *domain)
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int omap_iommu_domain_activate(struct iommu_domain *domain)
|
||||||
|
{
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -13,4 +13,8 @@ struct iommu_platform_data {
|
|||||||
const char *reset_name;
|
const char *reset_name;
|
||||||
int (*assert_reset)(struct platform_device *pdev, const char *name);
|
int (*assert_reset)(struct platform_device *pdev, const char *name);
|
||||||
int (*deassert_reset)(struct platform_device *pdev, const char *name);
|
int (*deassert_reset)(struct platform_device *pdev, const char *name);
|
||||||
|
int (*device_enable)(struct platform_device *pdev);
|
||||||
|
int (*device_idle)(struct platform_device *pdev);
|
||||||
|
int (*set_pwrdm_constraint)(struct platform_device *pdev, bool request,
|
||||||
|
u8 *pwrst);
|
||||||
};
|
};
|
||||||
|
@ -46,13 +46,17 @@ enum dma_sync_target {
|
|||||||
|
|
||||||
extern phys_addr_t swiotlb_tbl_map_single(struct device *hwdev,
|
extern phys_addr_t swiotlb_tbl_map_single(struct device *hwdev,
|
||||||
dma_addr_t tbl_dma_addr,
|
dma_addr_t tbl_dma_addr,
|
||||||
phys_addr_t phys, size_t size,
|
phys_addr_t phys,
|
||||||
|
size_t mapping_size,
|
||||||
|
size_t alloc_size,
|
||||||
enum dma_data_direction dir,
|
enum dma_data_direction dir,
|
||||||
unsigned long attrs);
|
unsigned long attrs);
|
||||||
|
|
||||||
extern void swiotlb_tbl_unmap_single(struct device *hwdev,
|
extern void swiotlb_tbl_unmap_single(struct device *hwdev,
|
||||||
phys_addr_t tlb_addr,
|
phys_addr_t tlb_addr,
|
||||||
size_t size, enum dma_data_direction dir,
|
size_t mapping_size,
|
||||||
|
size_t alloc_size,
|
||||||
|
enum dma_data_direction dir,
|
||||||
unsigned long attrs);
|
unsigned long attrs);
|
||||||
|
|
||||||
extern void swiotlb_tbl_sync_single(struct device *hwdev,
|
extern void swiotlb_tbl_sync_single(struct device *hwdev,
|
||||||
|
@ -20,11 +20,6 @@ struct mtk_smi_larb_iommu {
|
|||||||
unsigned int mmu;
|
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.
|
* mtk_smi_larb_get: Enable the power domain and clocks for this local arbiter.
|
||||||
* It also initialize some basic setting(like iommu).
|
* It also initialize some basic setting(like iommu).
|
||||||
|
106
include/trace/events/intel_iommu.h
Normal file
106
include/trace/events/intel_iommu.h
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
/* SPDX-License-Identifier: GPL-2.0 */
|
||||||
|
/*
|
||||||
|
* Intel IOMMU trace support
|
||||||
|
*
|
||||||
|
* Copyright (C) 2019 Intel Corporation
|
||||||
|
*
|
||||||
|
* Author: Lu Baolu <baolu.lu@linux.intel.com>
|
||||||
|
*/
|
||||||
|
#ifdef CONFIG_INTEL_IOMMU
|
||||||
|
#undef TRACE_SYSTEM
|
||||||
|
#define TRACE_SYSTEM intel_iommu
|
||||||
|
|
||||||
|
#if !defined(_TRACE_INTEL_IOMMU_H) || defined(TRACE_HEADER_MULTI_READ)
|
||||||
|
#define _TRACE_INTEL_IOMMU_H
|
||||||
|
|
||||||
|
#include <linux/tracepoint.h>
|
||||||
|
#include <linux/intel-iommu.h>
|
||||||
|
|
||||||
|
DECLARE_EVENT_CLASS(dma_map,
|
||||||
|
TP_PROTO(struct device *dev, dma_addr_t dev_addr, phys_addr_t phys_addr,
|
||||||
|
size_t size),
|
||||||
|
|
||||||
|
TP_ARGS(dev, dev_addr, phys_addr, size),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__string(dev_name, dev_name(dev))
|
||||||
|
__field(dma_addr_t, dev_addr)
|
||||||
|
__field(phys_addr_t, phys_addr)
|
||||||
|
__field(size_t, size)
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
__assign_str(dev_name, dev_name(dev));
|
||||||
|
__entry->dev_addr = dev_addr;
|
||||||
|
__entry->phys_addr = phys_addr;
|
||||||
|
__entry->size = size;
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk("dev=%s dev_addr=0x%llx phys_addr=0x%llx size=%zu",
|
||||||
|
__get_str(dev_name),
|
||||||
|
(unsigned long long)__entry->dev_addr,
|
||||||
|
(unsigned long long)__entry->phys_addr,
|
||||||
|
__entry->size)
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_EVENT(dma_map, map_single,
|
||||||
|
TP_PROTO(struct device *dev, dma_addr_t dev_addr, phys_addr_t phys_addr,
|
||||||
|
size_t size),
|
||||||
|
TP_ARGS(dev, dev_addr, phys_addr, size)
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_EVENT(dma_map, map_sg,
|
||||||
|
TP_PROTO(struct device *dev, dma_addr_t dev_addr, phys_addr_t phys_addr,
|
||||||
|
size_t size),
|
||||||
|
TP_ARGS(dev, dev_addr, phys_addr, size)
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_EVENT(dma_map, bounce_map_single,
|
||||||
|
TP_PROTO(struct device *dev, dma_addr_t dev_addr, phys_addr_t phys_addr,
|
||||||
|
size_t size),
|
||||||
|
TP_ARGS(dev, dev_addr, phys_addr, size)
|
||||||
|
);
|
||||||
|
|
||||||
|
DECLARE_EVENT_CLASS(dma_unmap,
|
||||||
|
TP_PROTO(struct device *dev, dma_addr_t dev_addr, size_t size),
|
||||||
|
|
||||||
|
TP_ARGS(dev, dev_addr, size),
|
||||||
|
|
||||||
|
TP_STRUCT__entry(
|
||||||
|
__string(dev_name, dev_name(dev))
|
||||||
|
__field(dma_addr_t, dev_addr)
|
||||||
|
__field(size_t, size)
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_fast_assign(
|
||||||
|
__assign_str(dev_name, dev_name(dev));
|
||||||
|
__entry->dev_addr = dev_addr;
|
||||||
|
__entry->size = size;
|
||||||
|
),
|
||||||
|
|
||||||
|
TP_printk("dev=%s dev_addr=0x%llx size=%zu",
|
||||||
|
__get_str(dev_name),
|
||||||
|
(unsigned long long)__entry->dev_addr,
|
||||||
|
__entry->size)
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_EVENT(dma_unmap, unmap_single,
|
||||||
|
TP_PROTO(struct device *dev, dma_addr_t dev_addr, size_t size),
|
||||||
|
TP_ARGS(dev, dev_addr, size)
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_EVENT(dma_unmap, unmap_sg,
|
||||||
|
TP_PROTO(struct device *dev, dma_addr_t dev_addr, size_t size),
|
||||||
|
TP_ARGS(dev, dev_addr, size)
|
||||||
|
);
|
||||||
|
|
||||||
|
DEFINE_EVENT(dma_unmap, bounce_unmap_single,
|
||||||
|
TP_PROTO(struct device *dev, dma_addr_t dev_addr, size_t size),
|
||||||
|
TP_ARGS(dev, dev_addr, size)
|
||||||
|
);
|
||||||
|
|
||||||
|
#endif /* _TRACE_INTEL_IOMMU_H */
|
||||||
|
|
||||||
|
/* This part must be outside protection */
|
||||||
|
#include <trace/define_trace.h>
|
||||||
|
#endif /* CONFIG_INTEL_IOMMU */
|
@ -305,7 +305,7 @@ void dma_direct_unmap_page(struct device *dev, dma_addr_t addr,
|
|||||||
dma_direct_sync_single_for_cpu(dev, addr, size, dir);
|
dma_direct_sync_single_for_cpu(dev, addr, size, dir);
|
||||||
|
|
||||||
if (unlikely(is_swiotlb_buffer(phys)))
|
if (unlikely(is_swiotlb_buffer(phys)))
|
||||||
swiotlb_tbl_unmap_single(dev, phys, size, dir, attrs);
|
swiotlb_tbl_unmap_single(dev, phys, size, size, dir, attrs);
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(dma_direct_unmap_page);
|
EXPORT_SYMBOL(dma_direct_unmap_page);
|
||||||
|
|
||||||
|
@ -444,7 +444,9 @@ static void swiotlb_bounce(phys_addr_t orig_addr, phys_addr_t tlb_addr,
|
|||||||
|
|
||||||
phys_addr_t swiotlb_tbl_map_single(struct device *hwdev,
|
phys_addr_t swiotlb_tbl_map_single(struct device *hwdev,
|
||||||
dma_addr_t tbl_dma_addr,
|
dma_addr_t tbl_dma_addr,
|
||||||
phys_addr_t orig_addr, size_t size,
|
phys_addr_t orig_addr,
|
||||||
|
size_t mapping_size,
|
||||||
|
size_t alloc_size,
|
||||||
enum dma_data_direction dir,
|
enum dma_data_direction dir,
|
||||||
unsigned long attrs)
|
unsigned long attrs)
|
||||||
{
|
{
|
||||||
@ -464,6 +466,12 @@ phys_addr_t swiotlb_tbl_map_single(struct device *hwdev,
|
|||||||
pr_warn_once("%s is active and system is using DMA bounce buffers\n",
|
pr_warn_once("%s is active and system is using DMA bounce buffers\n",
|
||||||
sme_active() ? "SME" : "SEV");
|
sme_active() ? "SME" : "SEV");
|
||||||
|
|
||||||
|
if (mapping_size > alloc_size) {
|
||||||
|
dev_warn_once(hwdev, "Invalid sizes (mapping: %zd bytes, alloc: %zd bytes)",
|
||||||
|
mapping_size, alloc_size);
|
||||||
|
return (phys_addr_t)DMA_MAPPING_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
mask = dma_get_seg_boundary(hwdev);
|
mask = dma_get_seg_boundary(hwdev);
|
||||||
|
|
||||||
tbl_dma_addr &= mask;
|
tbl_dma_addr &= mask;
|
||||||
@ -471,8 +479,8 @@ phys_addr_t swiotlb_tbl_map_single(struct device *hwdev,
|
|||||||
offset_slots = ALIGN(tbl_dma_addr, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT;
|
offset_slots = ALIGN(tbl_dma_addr, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Carefully handle integer overflow which can occur when mask == ~0UL.
|
* Carefully handle integer overflow which can occur when mask == ~0UL.
|
||||||
*/
|
*/
|
||||||
max_slots = mask + 1
|
max_slots = mask + 1
|
||||||
? ALIGN(mask + 1, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT
|
? ALIGN(mask + 1, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT
|
||||||
: 1UL << (BITS_PER_LONG - IO_TLB_SHIFT);
|
: 1UL << (BITS_PER_LONG - IO_TLB_SHIFT);
|
||||||
@ -481,8 +489,8 @@ phys_addr_t swiotlb_tbl_map_single(struct device *hwdev,
|
|||||||
* For mappings greater than or equal to a page, we limit the stride
|
* For mappings greater than or equal to a page, we limit the stride
|
||||||
* (and hence alignment) to a page size.
|
* (and hence alignment) to a page size.
|
||||||
*/
|
*/
|
||||||
nslots = ALIGN(size, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT;
|
nslots = ALIGN(alloc_size, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT;
|
||||||
if (size >= PAGE_SIZE)
|
if (alloc_size >= PAGE_SIZE)
|
||||||
stride = (1 << (PAGE_SHIFT - IO_TLB_SHIFT));
|
stride = (1 << (PAGE_SHIFT - IO_TLB_SHIFT));
|
||||||
else
|
else
|
||||||
stride = 1;
|
stride = 1;
|
||||||
@ -547,7 +555,7 @@ phys_addr_t swiotlb_tbl_map_single(struct device *hwdev,
|
|||||||
spin_unlock_irqrestore(&io_tlb_lock, flags);
|
spin_unlock_irqrestore(&io_tlb_lock, flags);
|
||||||
if (!(attrs & DMA_ATTR_NO_WARN) && printk_ratelimit())
|
if (!(attrs & DMA_ATTR_NO_WARN) && printk_ratelimit())
|
||||||
dev_warn(hwdev, "swiotlb buffer is full (sz: %zd bytes), total %lu (slots), used %lu (slots)\n",
|
dev_warn(hwdev, "swiotlb buffer is full (sz: %zd bytes), total %lu (slots), used %lu (slots)\n",
|
||||||
size, io_tlb_nslabs, tmp_io_tlb_used);
|
alloc_size, io_tlb_nslabs, tmp_io_tlb_used);
|
||||||
return (phys_addr_t)DMA_MAPPING_ERROR;
|
return (phys_addr_t)DMA_MAPPING_ERROR;
|
||||||
found:
|
found:
|
||||||
io_tlb_used += nslots;
|
io_tlb_used += nslots;
|
||||||
@ -562,7 +570,7 @@ phys_addr_t swiotlb_tbl_map_single(struct device *hwdev,
|
|||||||
io_tlb_orig_addr[index+i] = orig_addr + (i << IO_TLB_SHIFT);
|
io_tlb_orig_addr[index+i] = orig_addr + (i << IO_TLB_SHIFT);
|
||||||
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
|
if (!(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
|
||||||
(dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL))
|
(dir == DMA_TO_DEVICE || dir == DMA_BIDIRECTIONAL))
|
||||||
swiotlb_bounce(orig_addr, tlb_addr, size, DMA_TO_DEVICE);
|
swiotlb_bounce(orig_addr, tlb_addr, mapping_size, DMA_TO_DEVICE);
|
||||||
|
|
||||||
return tlb_addr;
|
return tlb_addr;
|
||||||
}
|
}
|
||||||
@ -571,11 +579,11 @@ phys_addr_t swiotlb_tbl_map_single(struct device *hwdev,
|
|||||||
* tlb_addr is the physical address of the bounce buffer to unmap.
|
* tlb_addr is the physical address of the bounce buffer to unmap.
|
||||||
*/
|
*/
|
||||||
void swiotlb_tbl_unmap_single(struct device *hwdev, phys_addr_t tlb_addr,
|
void swiotlb_tbl_unmap_single(struct device *hwdev, phys_addr_t tlb_addr,
|
||||||
size_t size, enum dma_data_direction dir,
|
size_t mapping_size, size_t alloc_size,
|
||||||
unsigned long attrs)
|
enum dma_data_direction dir, unsigned long attrs)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
int i, count, nslots = ALIGN(size, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT;
|
int i, count, nslots = ALIGN(alloc_size, 1 << IO_TLB_SHIFT) >> IO_TLB_SHIFT;
|
||||||
int index = (tlb_addr - io_tlb_start) >> IO_TLB_SHIFT;
|
int index = (tlb_addr - io_tlb_start) >> IO_TLB_SHIFT;
|
||||||
phys_addr_t orig_addr = io_tlb_orig_addr[index];
|
phys_addr_t orig_addr = io_tlb_orig_addr[index];
|
||||||
|
|
||||||
@ -585,7 +593,7 @@ void swiotlb_tbl_unmap_single(struct device *hwdev, phys_addr_t tlb_addr,
|
|||||||
if (orig_addr != INVALID_PHYS_ADDR &&
|
if (orig_addr != INVALID_PHYS_ADDR &&
|
||||||
!(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
|
!(attrs & DMA_ATTR_SKIP_CPU_SYNC) &&
|
||||||
((dir == DMA_FROM_DEVICE) || (dir == DMA_BIDIRECTIONAL)))
|
((dir == DMA_FROM_DEVICE) || (dir == DMA_BIDIRECTIONAL)))
|
||||||
swiotlb_bounce(orig_addr, tlb_addr, size, DMA_FROM_DEVICE);
|
swiotlb_bounce(orig_addr, tlb_addr, mapping_size, DMA_FROM_DEVICE);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return the buffer to the free list by setting the corresponding
|
* Return the buffer to the free list by setting the corresponding
|
||||||
@ -665,14 +673,14 @@ bool swiotlb_map(struct device *dev, phys_addr_t *phys, dma_addr_t *dma_addr,
|
|||||||
|
|
||||||
/* Oh well, have to allocate and map a bounce buffer. */
|
/* Oh well, have to allocate and map a bounce buffer. */
|
||||||
*phys = swiotlb_tbl_map_single(dev, __phys_to_dma(dev, io_tlb_start),
|
*phys = swiotlb_tbl_map_single(dev, __phys_to_dma(dev, io_tlb_start),
|
||||||
*phys, size, dir, attrs);
|
*phys, size, size, dir, attrs);
|
||||||
if (*phys == (phys_addr_t)DMA_MAPPING_ERROR)
|
if (*phys == (phys_addr_t)DMA_MAPPING_ERROR)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/* Ensure that the address returned is DMA'ble */
|
/* Ensure that the address returned is DMA'ble */
|
||||||
*dma_addr = __phys_to_dma(dev, *phys);
|
*dma_addr = __phys_to_dma(dev, *phys);
|
||||||
if (unlikely(!dma_capable(dev, *dma_addr, size))) {
|
if (unlikely(!dma_capable(dev, *dma_addr, size))) {
|
||||||
swiotlb_tbl_unmap_single(dev, *phys, size, dir,
|
swiotlb_tbl_unmap_single(dev, *phys, size, size, dir,
|
||||||
attrs | DMA_ATTR_SKIP_CPU_SYNC);
|
attrs | DMA_ATTR_SKIP_CPU_SYNC);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user