Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
9a51f3fc
Commit
9a51f3fc
authored
May 09, 2016
by
Joerg Roedel
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'arm/smmu' into core
parents
809eac54
8801561c
Changes
6
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
265 additions
and
83 deletions
+265
-83
Documentation/arm64/silicon-errata.txt
Documentation/arm64/silicon-errata.txt
+2
-0
Documentation/devicetree/bindings/iommu/arm,smmu.txt
Documentation/devicetree/bindings/iommu/arm,smmu.txt
+1
-0
drivers/iommu/arm-smmu.c
drivers/iommu/arm-smmu.c
+210
-81
include/asm-generic/io.h
include/asm-generic/io.h
+2
-2
include/linux/io-64-nonatomic-hi-lo.h
include/linux/io-64-nonatomic-hi-lo.h
+25
-0
include/linux/io-64-nonatomic-lo-hi.h
include/linux/io-64-nonatomic-lo-hi.h
+25
-0
No files found.
Documentation/arm64/silicon-errata.txt
View file @
9a51f3fc
...
...
@@ -53,7 +53,9 @@ stable kernels.
| ARM | Cortex-A57 | #832075 | ARM64_ERRATUM_832075 |
| ARM | Cortex-A57 | #852523 | N/A |
| ARM | Cortex-A57 | #834220 | ARM64_ERRATUM_834220 |
| ARM | MMU-500 | #841119,#826419 | N/A |
| | | | |
| Cavium | ThunderX ITS | #22375, #24313 | CAVIUM_ERRATUM_22375 |
| Cavium | ThunderX GICv3 | #23154 | CAVIUM_ERRATUM_23154 |
| Cavium | ThunderX Core | #27456 | CAVIUM_ERRATUM_27456 |
| Cavium | ThunderX SMMUv2 | #27704 | N/A |
Documentation/devicetree/bindings/iommu/arm,smmu.txt
View file @
9a51f3fc
...
...
@@ -16,6 +16,7 @@ conditions.
"arm,mmu-400"
"arm,mmu-401"
"arm,mmu-500"
"cavium,smmu-v2"
depending on the particular implementation and/or the
version of the architecture implemented.
...
...
drivers/iommu/arm-smmu.c
View file @
9a51f3fc
...
...
@@ -34,6 +34,7 @@
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/io-64-nonatomic-hi-lo.h>
#include <linux/iommu.h>
#include <linux/iopoll.h>
#include <linux/module.h>
...
...
@@ -71,16 +72,15 @@
((smmu->options & ARM_SMMU_OPT_SECURE_CFG_ACCESS) \
? 0x400 : 0))
/*
* Some 64-bit registers only make sense to write atomically, but in such
* cases all the data relevant to AArch32 formats lies within the lower word,
* therefore this actually makes more sense than it might first appear.
*/
#ifdef CONFIG_64BIT
#define smmu_write
q
writeq_relaxed
#define smmu_write
_atomic_lq
writeq_relaxed
#else
#define smmu_writeq(reg64, addr) \
do { \
u64 __val = (reg64); \
void __iomem *__addr = (addr); \
writel_relaxed(__val >> 32, __addr + 4); \
writel_relaxed(__val, __addr); \
} while (0)
#define smmu_write_atomic_lq writel_relaxed
#endif
/* Configuration registers */
...
...
@@ -94,9 +94,13 @@
#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
...
...
@@ -116,6 +120,8 @@
#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
...
...
@@ -141,6 +147,10 @@
#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
...
...
@@ -193,12 +203,15 @@
#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
/* Translation context bank */
#define ARM_SMMU_CB_BASE(smmu) ((smmu)->base + ((smmu)->size >> 1))
#define ARM_SMMU_CB(smmu, n) ((n) * (1 << (smmu)->pgshift))
#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
...
...
@@ -206,11 +219,9 @@
#define ARM_SMMU_CB_TTBCR 0x30
#define ARM_SMMU_CB_S1_MAIR0 0x38
#define ARM_SMMU_CB_S1_MAIR1 0x3c
#define ARM_SMMU_CB_PAR_LO 0x50
#define ARM_SMMU_CB_PAR_HI 0x54
#define ARM_SMMU_CB_PAR 0x50
#define ARM_SMMU_CB_FSR 0x58
#define ARM_SMMU_CB_FAR_LO 0x60
#define ARM_SMMU_CB_FAR_HI 0x64
#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
...
...
@@ -230,6 +241,10 @@
#define SCTLR_M (1 << 0)
#define SCTLR_EAE_SBOP (SCTLR_AFE | SCTLR_TRE)
#define ARM_MMU500_ACTLR_CPRE (1 << 1)
#define ARM_MMU500_ACR_CACHE_LOCK (1 << 26)
#define CB_PAR_F (1 << 0)
#define ATSR_ACTIVE (1 << 0)
...
...
@@ -270,10 +285,17 @@ MODULE_PARM_DESC(disable_bypass,
"Disable bypass streams such that incoming transactions from devices that are not attached to an iommu domain will report an abort back to the device and will not be allowed to pass through the SMMU."
);
enum
arm_smmu_arch_version
{
ARM_SMMU_V1
=
1
,
ARM_SMMU_V1
,
ARM_SMMU_V1_64K
,
ARM_SMMU_V2
,
};
enum
arm_smmu_implementation
{
GENERIC_SMMU
,
ARM_MMU500
,
CAVIUM_SMMUV2
,
};
struct
arm_smmu_smr
{
u8
idx
;
u16
mask
;
...
...
@@ -305,11 +327,18 @@ struct arm_smmu_device {
#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)
u32
features
;
#define ARM_SMMU_OPT_SECURE_CFG_ACCESS (1 << 0)
u32
options
;
enum
arm_smmu_arch_version
version
;
enum
arm_smmu_implementation
model
;
u32
num_context_banks
;
u32
num_s2_context_banks
;
...
...
@@ -329,17 +358,27 @@ struct arm_smmu_device {
struct
list_head
list
;
struct
rb_root
masters
;
u32
cavium_id_base
;
/* Specific to Cavium */
};
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
;
u32
cbar
;
enum
arm_smmu_context_fmt
fmt
;
};
#define INVALID_IRPTNDX 0xff
#define ARM_SMMU_CB_ASID(
cfg) (
(cfg)->cbndx)
#define ARM_SMMU_CB_VMID(
cfg) (
(cfg)->cbndx + 1)
#define ARM_SMMU_CB_ASID(
smmu, cfg) ((u16)(smmu)->cavium_id_base +
(cfg)->cbndx)
#define ARM_SMMU_CB_VMID(
smmu, cfg) ((u16)(smmu)->cavium_id_base +
(cfg)->cbndx + 1)
enum
arm_smmu_domain_stage
{
ARM_SMMU_DOMAIN_S1
=
0
,
...
...
@@ -367,6 +406,8 @@ struct arm_smmu_option_prop {
const
char
*
prop
;
};
static
atomic_t
cavium_smmu_context_count
=
ATOMIC_INIT
(
0
);
static
struct
arm_smmu_option_prop
arm_smmu_options
[]
=
{
{
ARM_SMMU_OPT_SECURE_CFG_ACCESS
,
"calxeda,smmu-secure-config-access"
},
{
0
,
NULL
},
...
...
@@ -578,11 +619,11 @@ static void arm_smmu_tlb_inv_context(void *cookie)
if
(
stage1
)
{
base
=
ARM_SMMU_CB_BASE
(
smmu
)
+
ARM_SMMU_CB
(
smmu
,
cfg
->
cbndx
);
writel_relaxed
(
ARM_SMMU_CB_ASID
(
cfg
),
writel_relaxed
(
ARM_SMMU_CB_ASID
(
smmu
,
cfg
),
base
+
ARM_SMMU_CB_S1_TLBIASID
);
}
else
{
base
=
ARM_SMMU_GR0
(
smmu
);
writel_relaxed
(
ARM_SMMU_CB_VMID
(
cfg
),
writel_relaxed
(
ARM_SMMU_CB_VMID
(
smmu
,
cfg
),
base
+
ARM_SMMU_GR0_TLBIVMID
);
}
...
...
@@ -602,37 +643,33 @@ static void arm_smmu_tlb_inv_range_nosync(unsigned long iova, size_t size,
reg
=
ARM_SMMU_CB_BASE
(
smmu
)
+
ARM_SMMU_CB
(
smmu
,
cfg
->
cbndx
);
reg
+=
leaf
?
ARM_SMMU_CB_S1_TLBIVAL
:
ARM_SMMU_CB_S1_TLBIVA
;
if
(
!
IS_ENABLED
(
CONFIG_64BIT
)
||
smmu
->
version
==
ARM_SMMU_V1
)
{
if
(
cfg
->
fmt
!=
ARM_SMMU_CTX_FMT_AARCH64
)
{
iova
&=
~
12UL
;
iova
|=
ARM_SMMU_CB_ASID
(
cfg
);
iova
|=
ARM_SMMU_CB_ASID
(
smmu
,
cfg
);
do
{
writel_relaxed
(
iova
,
reg
);
iova
+=
granule
;
}
while
(
size
-=
granule
);
#ifdef CONFIG_64BIT
}
else
{
iova
>>=
12
;
iova
|=
(
u64
)
ARM_SMMU_CB_ASID
(
cfg
)
<<
48
;
iova
|=
(
u64
)
ARM_SMMU_CB_ASID
(
smmu
,
cfg
)
<<
48
;
do
{
writeq_relaxed
(
iova
,
reg
);
iova
+=
granule
>>
12
;
}
while
(
size
-=
granule
);
#endif
}
#ifdef CONFIG_64BIT
}
else
if
(
smmu
->
version
==
ARM_SMMU_V2
)
{
reg
=
ARM_SMMU_CB_BASE
(
smmu
)
+
ARM_SMMU_CB
(
smmu
,
cfg
->
cbndx
);
reg
+=
leaf
?
ARM_SMMU_CB_S2_TLBIIPAS2L
:
ARM_SMMU_CB_S2_TLBIIPAS2
;
iova
>>=
12
;
do
{
writeq_relaxed
(
iova
,
reg
);
smmu_write_atomic_lq
(
iova
,
reg
);
iova
+=
granule
>>
12
;
}
while
(
size
-=
granule
);
#endif
}
else
{
reg
=
ARM_SMMU_GR0
(
smmu
)
+
ARM_SMMU_GR0_TLBIVMID
;
writel_relaxed
(
ARM_SMMU_CB_VMID
(
cfg
),
reg
);
writel_relaxed
(
ARM_SMMU_CB_VMID
(
smmu
,
cfg
),
reg
);
}
}
...
...
@@ -645,7 +682,7 @@ static struct iommu_gather_ops arm_smmu_gather_ops = {
static
irqreturn_t
arm_smmu_context_fault
(
int
irq
,
void
*
dev
)
{
int
flags
,
ret
;
u32
fsr
,
f
ar
,
f
synr
,
resume
;
u32
fsr
,
fsynr
,
resume
;
unsigned
long
iova
;
struct
iommu_domain
*
domain
=
dev
;
struct
arm_smmu_domain
*
smmu_domain
=
to_smmu_domain
(
domain
);
...
...
@@ -667,13 +704,7 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
fsynr
=
readl_relaxed
(
cb_base
+
ARM_SMMU_CB_FSYNR0
);
flags
=
fsynr
&
FSYNR0_WNR
?
IOMMU_FAULT_WRITE
:
IOMMU_FAULT_READ
;
far
=
readl_relaxed
(
cb_base
+
ARM_SMMU_CB_FAR_LO
);
iova
=
far
;
#ifdef CONFIG_64BIT
far
=
readl_relaxed
(
cb_base
+
ARM_SMMU_CB_FAR_HI
);
iova
|=
((
unsigned
long
)
far
<<
32
);
#endif
iova
=
readq_relaxed
(
cb_base
+
ARM_SMMU_CB_FAR
);
if
(
!
report_iommu_fault
(
domain
,
smmu
->
dev
,
iova
,
flags
))
{
ret
=
IRQ_HANDLED
;
resume
=
RESUME_RETRY
;
...
...
@@ -734,22 +765,20 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain,
cb_base
=
ARM_SMMU_CB_BASE
(
smmu
)
+
ARM_SMMU_CB
(
smmu
,
cfg
->
cbndx
);
if
(
smmu
->
version
>
ARM_SMMU_V1
)
{
/*
* CBA2R.
* *Must* be initialised before CBAR thanks to VMID16
* architectural oversight affected some implementations.
*/
#ifdef CONFIG_64BIT
reg
=
CBA2R_RW64_64BIT
;
#else
reg
=
CBA2R_RW64_32BIT
;
#endif
if
(
cfg
->
fmt
==
ARM_SMMU_CTX_FMT_AARCH64
)
reg
=
CBA2R_RW64_64BIT
;
else
reg
=
CBA2R_RW64_32BIT
;
/* 16-bit VMIDs live in CBA2R */
if
(
smmu
->
features
&
ARM_SMMU_FEAT_VMID16
)
reg
|=
ARM_SMMU_CB_VMID
(
smmu
,
cfg
)
<<
CBA2R_VMID_SHIFT
;
writel_relaxed
(
reg
,
gr1_base
+
ARM_SMMU_GR1_CBA2R
(
cfg
->
cbndx
));
}
/* CBAR */
reg
=
cfg
->
cbar
;
if
(
smmu
->
version
==
ARM_SMMU_V1
)
if
(
smmu
->
version
<
ARM_SMMU_V2
)
reg
|=
cfg
->
irptndx
<<
CBAR_IRPTNDX_SHIFT
;
/*
...
...
@@ -759,8 +788,9 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain,
if
(
stage1
)
{
reg
|=
(
CBAR_S1_BPSHCFG_NSH
<<
CBAR_S1_BPSHCFG_SHIFT
)
|
(
CBAR_S1_MEMATTR_WB
<<
CBAR_S1_MEMATTR_SHIFT
);
}
else
{
reg
|=
ARM_SMMU_CB_VMID
(
cfg
)
<<
CBAR_VMID_SHIFT
;
}
else
if
(
!
(
smmu
->
features
&
ARM_SMMU_FEAT_VMID16
))
{
/* 8-bit VMIDs live in CBAR */
reg
|=
ARM_SMMU_CB_VMID
(
smmu
,
cfg
)
<<
CBAR_VMID_SHIFT
;
}
writel_relaxed
(
reg
,
gr1_base
+
ARM_SMMU_GR1_CBAR
(
cfg
->
cbndx
));
...
...
@@ -768,15 +798,15 @@ static void arm_smmu_init_context_bank(struct arm_smmu_domain *smmu_domain,
if
(
stage1
)
{
reg64
=
pgtbl_cfg
->
arm_lpae_s1_cfg
.
ttbr
[
0
];
reg64
|=
((
u64
)
ARM_SMMU_CB_ASID
(
cfg
))
<<
TTBRn_ASID_SHIFT
;
smmu_writeq
(
reg64
,
cb_base
+
ARM_SMMU_CB_TTBR0
);
reg64
|=
((
u64
)
ARM_SMMU_CB_ASID
(
smmu
,
cfg
))
<<
TTBRn_ASID_SHIFT
;
writeq_relaxed
(
reg64
,
cb_base
+
ARM_SMMU_CB_TTBR0
);
reg64
=
pgtbl_cfg
->
arm_lpae_s1_cfg
.
ttbr
[
1
];
reg64
|=
((
u64
)
ARM_SMMU_CB_ASID
(
cfg
))
<<
TTBRn_ASID_SHIFT
;
smmu_writeq
(
reg64
,
cb_base
+
ARM_SMMU_CB_TTBR1
);
reg64
|=
((
u64
)
ARM_SMMU_CB_ASID
(
smmu
,
cfg
))
<<
TTBRn_ASID_SHIFT
;
writeq_relaxed
(
reg64
,
cb_base
+
ARM_SMMU_CB_TTBR1
);
}
else
{
reg64
=
pgtbl_cfg
->
arm_lpae_s2_cfg
.
vttbr
;
smmu_writeq
(
reg64
,
cb_base
+
ARM_SMMU_CB_TTBR0
);
writeq_relaxed
(
reg64
,
cb_base
+
ARM_SMMU_CB_TTBR0
);
}
/* TTBCR */
...
...
@@ -855,16 +885,40 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
if
(
!
(
smmu
->
features
&
ARM_SMMU_FEAT_TRANS_S2
))
smmu_domain
->
stage
=
ARM_SMMU_DOMAIN_S1
;
/*
* Choosing a suitable context format is even more fiddly. Until we
* grow some way for the caller to express a preference, and/or move
* the decision into the io-pgtable code where it arguably belongs,
* just aim for the closest thing to the rest of the system, and hope
* that the hardware isn't esoteric enough that we can't assume AArch64
* support to be a superset of AArch32 support...
*/
if
(
smmu
->
features
&
ARM_SMMU_FEAT_FMT_AARCH32_L
)
cfg
->
fmt
=
ARM_SMMU_CTX_FMT_AARCH32_L
;
if
((
IS_ENABLED
(
CONFIG_64BIT
)
||
cfg
->
fmt
==
ARM_SMMU_CTX_FMT_NONE
)
&&
(
smmu
->
features
&
(
ARM_SMMU_FEAT_FMT_AARCH64_64K
|
ARM_SMMU_FEAT_FMT_AARCH64_16K
|
ARM_SMMU_FEAT_FMT_AARCH64_4K
)))
cfg
->
fmt
=
ARM_SMMU_CTX_FMT_AARCH64
;
if
(
cfg
->
fmt
==
ARM_SMMU_CTX_FMT_NONE
)
{
ret
=
-
EINVAL
;
goto
out_unlock
;
}
switch
(
smmu_domain
->
stage
)
{
case
ARM_SMMU_DOMAIN_S1
:
cfg
->
cbar
=
CBAR_TYPE_S1_TRANS_S2_BYPASS
;
start
=
smmu
->
num_s2_context_banks
;
ias
=
smmu
->
va_size
;
oas
=
smmu
->
ipa_size
;
if
(
IS_ENABLED
(
CONFIG_64BIT
))
if
(
cfg
->
fmt
==
ARM_SMMU_CTX_FMT_AARCH64
)
{
fmt
=
ARM_64_LPAE_S1
;
else
}
else
{
fmt
=
ARM_32_LPAE_S1
;
ias
=
min
(
ias
,
32UL
);
oas
=
min
(
oas
,
40UL
);
}
break
;
case
ARM_SMMU_DOMAIN_NESTED
:
/*
...
...
@@ -876,10 +930,13 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
start
=
0
;
ias
=
smmu
->
ipa_size
;
oas
=
smmu
->
pa_size
;
if
(
IS_ENABLED
(
CONFIG_64BIT
))
if
(
cfg
->
fmt
==
ARM_SMMU_CTX_FMT_AARCH64
)
{
fmt
=
ARM_64_LPAE_S2
;
else
}
else
{
fmt
=
ARM_32_LPAE_S2
;
ias
=
min
(
ias
,
40UL
);
oas
=
min
(
oas
,
40UL
);
}
break
;
default:
ret
=
-
EINVAL
;
...
...
@@ -892,7 +949,7 @@ static int arm_smmu_init_domain_context(struct iommu_domain *domain,
goto
out_unlock
;
cfg
->
cbndx
=
ret
;
if
(
smmu
->
version
==
ARM_SMMU_V1
)
{
if
(
smmu
->
version
<
ARM_SMMU_V2
)
{
cfg
->
irptndx
=
atomic_inc_return
(
&
smmu
->
irptndx
);
cfg
->
irptndx
%=
smmu
->
num_context_irqs
;
}
else
{
...
...
@@ -1252,8 +1309,8 @@ static phys_addr_t arm_smmu_iova_to_phys_hard(struct iommu_domain *domain,
/* ATS1 registers can only be written atomically */
va
=
iova
&
~
0xfffUL
;
if
(
smmu
->
version
==
ARM_SMMU_V2
)
smmu_writeq
(
va
,
cb_base
+
ARM_SMMU_CB_ATS1PR
);
else
smmu_write
_atomic_l
q
(
va
,
cb_base
+
ARM_SMMU_CB_ATS1PR
);
else
/* Register is only 32-bit in v1 */
writel_relaxed
(
va
,
cb_base
+
ARM_SMMU_CB_ATS1PR
);
if
(
readl_poll_timeout_atomic
(
cb_base
+
ARM_SMMU_CB_ATSR
,
tmp
,
...
...
@@ -1264,9 +1321,7 @@ static phys_addr_t arm_smmu_iova_to_phys_hard(struct iommu_domain *domain,
return
ops
->
iova_to_phys
(
ops
,
iova
);
}
phys
=
readl_relaxed
(
cb_base
+
ARM_SMMU_CB_PAR_LO
);
phys
|=
((
u64
)
readl_relaxed
(
cb_base
+
ARM_SMMU_CB_PAR_HI
))
<<
32
;
phys
=
readq_relaxed
(
cb_base
+
ARM_SMMU_CB_PAR
);
if
(
phys
&
CB_PAR_F
)
{
dev_err
(
dev
,
"translation fault!
\n
"
);
dev_err
(
dev
,
"PAR = 0x%llx
\n
"
,
phys
);
...
...
@@ -1492,7 +1547,7 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
void
__iomem
*
gr0_base
=
ARM_SMMU_GR0
(
smmu
);
void
__iomem
*
cb_base
;
int
i
=
0
;
u32
reg
;
u32
reg
,
major
;
/* clear global FSR */
reg
=
readl_relaxed
(
ARM_SMMU_GR0_NS
(
smmu
)
+
ARM_SMMU_GR0_sGFSR
);
...
...
@@ -1505,11 +1560,33 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
writel_relaxed
(
reg
,
gr0_base
+
ARM_SMMU_GR0_S2CR
(
i
));
}
/*
* Before clearing ARM_MMU500_ACTLR_CPRE, need to
* clear CACHE_LOCK bit of ACR first. And, CACHE_LOCK
* bit is only present in MMU-500r2 onwards.
*/
reg
=
readl_relaxed
(
gr0_base
+
ARM_SMMU_GR0_ID7
);
major
=
(
reg
>>
ID7_MAJOR_SHIFT
)
&
ID7_MAJOR_MASK
;
if
((
smmu
->
model
==
ARM_MMU500
)
&&
(
major
>=
2
))
{
reg
=
readl_relaxed
(
gr0_base
+
ARM_SMMU_GR0_sACR
);
reg
&=
~
ARM_MMU500_ACR_CACHE_LOCK
;
writel_relaxed
(
reg
,
gr0_base
+
ARM_SMMU_GR0_sACR
);
}
/* Make sure all context banks are disabled and clear CB_FSR */
for
(
i
=
0
;
i
<
smmu
->
num_context_banks
;
++
i
)
{
cb_base
=
ARM_SMMU_CB_BASE
(
smmu
)
+
ARM_SMMU_CB
(
smmu
,
i
);
writel_relaxed
(
0
,
cb_base
+
ARM_SMMU_CB_SCTLR
);
writel_relaxed
(
FSR_FAULT
,
cb_base
+
ARM_SMMU_CB_FSR
);
/*
* Disable MMU-500's not-particularly-beneficial next-page
* prefetcher for the sake of errata #841119 and #826419.
*/
if
(
smmu
->
model
==
ARM_MMU500
)
{
reg
=
readl_relaxed
(
cb_base
+
ARM_SMMU_CB_ACTLR
);
reg
&=
~
ARM_MMU500_ACTLR_CPRE
;
writel_relaxed
(
reg
,
cb_base
+
ARM_SMMU_CB_ACTLR
);
}
}
/* Invalidate the TLB, just in case */
...
...
@@ -1537,6 +1614,9 @@ static void arm_smmu_device_reset(struct arm_smmu_device *smmu)
/* Don't upgrade barriers */
reg
&=
~
(
sCR0_BSU_MASK
<<
sCR0_BSU_SHIFT
);
if
(
smmu
->
features
&
ARM_SMMU_FEAT_VMID16
)
reg
|=
sCR0_VMID16EN
;
/* Push the button */
__arm_smmu_tlb_sync
(
smmu
);
writel
(
reg
,
ARM_SMMU_GR0_NS
(
smmu
)
+
ARM_SMMU_GR0_sCR0
);
...
...
@@ -1569,7 +1649,8 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
bool
cttw_dt
,
cttw_reg
;
dev_notice
(
smmu
->
dev
,
"probing hardware configuration...
\n
"
);
dev_notice
(
smmu
->
dev
,
"SMMUv%d with:
\n
"
,
smmu
->
version
);
dev_notice
(
smmu
->
dev
,
"SMMUv%d with:
\n
"
,
smmu
->
version
==
ARM_SMMU_V2
?
2
:
1
);
/* ID0 */
id
=
readl_relaxed
(
gr0_base
+
ARM_SMMU_GR0_ID0
);
...
...
@@ -1601,7 +1682,8 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
return
-
ENODEV
;
}
if
((
id
&
ID0_S1TS
)
&&
((
smmu
->
version
==
1
)
||
!
(
id
&
ID0_ATOSNS
)))
{
if
((
id
&
ID0_S1TS
)
&&
((
smmu
->
version
<
ARM_SMMU_V2
)
||
!
(
id
&
ID0_ATOSNS
)))
{
smmu
->
features
|=
ARM_SMMU_FEAT_TRANS_OPS
;
dev_notice
(
smmu
->
dev
,
"
\t
address translation ops
\n
"
);
}
...
...
@@ -1657,6 +1739,12 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
ID0_NUMSIDB_MASK
;
}
if
(
smmu
->
version
<
ARM_SMMU_V2
||
!
(
id
&
ID0_PTFS_NO_AARCH32
))
{
smmu
->
features
|=
ARM_SMMU_FEAT_FMT_AARCH32_L
;
if
(
!
(
id
&
ID0_PTFS_NO_AARCH32S
))
smmu
->
features
|=
ARM_SMMU_FEAT_FMT_AARCH32_S
;
}
/* ID1 */
id
=
readl_relaxed
(
gr0_base
+
ARM_SMMU_GR0_ID1
);
smmu
->
pgshift
=
(
id
&
ID1_PAGESIZE
)
?
16
:
12
;
...
...
@@ -1677,6 +1765,17 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
}
dev_notice
(
smmu
->
dev
,
"
\t
%u context banks (%u stage-2 only)
\n
"
,
smmu
->
num_context_banks
,
smmu
->
num_s2_context_banks
);
/*
* Cavium CN88xx erratum #27704.
* Ensure ASID and VMID allocation is unique across all SMMUs in
* the system.
*/
if
(
smmu
->
model
==
CAVIUM_SMMUV2
)
{
smmu
->
cavium_id_base
=
atomic_add_return
(
smmu
->
num_context_banks
,
&
cavium_smmu_context_count
);
smmu
->
cavium_id_base
-=
smmu
->
num_context_banks
;
}
/* ID2 */
id
=
readl_relaxed
(
gr0_base
+
ARM_SMMU_GR0_ID2
);
...
...
@@ -1687,6 +1786,9 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
size
=
arm_smmu_id_size_to_bits
((
id
>>
ID2_OAS_SHIFT
)
&
ID2_OAS_MASK
);
smmu
->
pa_size
=
size
;
if
(
id
&
ID2_VMID16
)
smmu
->
features
|=
ARM_SMMU_FEAT_VMID16
;
/*
* What the page table walker can address actually depends on which
* descriptor format is in use, but since a) we don't know that yet,
...
...
@@ -1696,24 +1798,33 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
dev_warn
(
smmu
->
dev
,
"failed to set DMA mask for table walker
\n
"
);
if
(
smmu
->
version
==
ARM_SMMU_V1
)
{
if
(
smmu
->
version
<
ARM_SMMU_V2
)
{
smmu
->
va_size
=
smmu
->
ipa_size
;
size
=
SZ_4K
|
SZ_2M
|
SZ_1G
;
if
(
smmu
->
version
==
ARM_SMMU_V1_64K
)
smmu
->
features
|=
ARM_SMMU_FEAT_FMT_AARCH64_64K
;
}
else
{
size
=
(
id
>>
ID2_UBS_SHIFT
)
&
ID2_UBS_MASK
;
smmu
->
va_size
=
arm_smmu_id_size_to_bits
(
size
);
#ifndef CONFIG_64BIT
smmu
->
va_size
=
min
(
32UL
,
smmu
->
va_size
);
#endif
size
=
0
;
if
(
id
&
ID2_PTFS_4K
)
s
ize
|=
SZ_4K
|
SZ_2M
|
SZ_1G
;
s
mmu
->
features
|=
ARM_SMMU_FEAT_FMT_AARCH64_4K
;
if
(
id
&
ID2_PTFS_16K
)
s
ize
|=
SZ_16K
|
SZ_32M
;
s
mmu
->
features
|=
ARM_SMMU_FEAT_FMT_AARCH64_16K
;
if
(
id
&
ID2_PTFS_64K
)
s
ize
|=
SZ_64K
|
SZ_512M
;
s
mmu
->
features
|=
ARM_SMMU_FEAT_FMT_AARCH64_64K
;
}
/* Now we've corralled the various formats, what'll it do? */
size
=
0
;
if
(
smmu
->
features
&
ARM_SMMU_FEAT_FMT_AARCH32_S
)
size
|=
SZ_4K
|
SZ_64K
|
SZ_1M
|
SZ_16M
;
if
(
smmu
->
features
&
(
ARM_SMMU_FEAT_FMT_AARCH32_L
|
ARM_SMMU_FEAT_FMT_AARCH64_4K
))
size
|=
SZ_4K
|
SZ_2M
|
SZ_1G
;
if
(
smmu
->
features
&
ARM_SMMU_FEAT_FMT_AARCH64_16K
)
size
|=
SZ_16K
|
SZ_32M
;
if
(
smmu
->
features
&
ARM_SMMU_FEAT_FMT_AARCH64_64K
)
size
|=
SZ_64K
|
SZ_512M
;
arm_smmu_ops
.
pgsize_bitmap
&=
size
;
dev_notice
(
smmu
->
dev
,
"
\t
Supported page sizes: 0x%08lx
\n
"
,
size
);
...
...
@@ -1728,12 +1839,27 @@ static int arm_smmu_device_cfg_probe(struct arm_smmu_device *smmu)
return
0
;
}
struct
arm_smmu_match_data
{
enum
arm_smmu_arch_version
version
;
enum
arm_smmu_implementation
model
;
};
#define ARM_SMMU_MATCH_DATA(name, ver, imp) \
static struct arm_smmu_match_data name = { .version = ver, .model = imp }
ARM_SMMU_MATCH_DATA
(
smmu_generic_v1
,
ARM_SMMU_V1
,
GENERIC_SMMU
);
ARM_SMMU_MATCH_DATA
(
smmu_generic_v2
,
ARM_SMMU_V2
,
GENERIC_SMMU
);
ARM_SMMU_MATCH_DATA
(
arm_mmu401
,
ARM_SMMU_V1_64K
,
GENERIC_SMMU
);
ARM_SMMU_MATCH_DATA
(
arm_mmu500
,
ARM_SMMU_V2
,
ARM_MMU500
);
ARM_SMMU_MATCH_DATA
(
cavium_smmuv2
,
ARM_SMMU_V2
,
CAVIUM_SMMUV2
);
static
const
struct
of_device_id
arm_smmu_of_match
[]
=
{
{
.
compatible
=
"arm,smmu-v1"
,
.
data
=
(
void
*
)
ARM_SMMU_V1
},
{
.
compatible
=
"arm,smmu-v2"
,
.
data
=
(
void
*
)
ARM_SMMU_V2
},
{
.
compatible
=
"arm,mmu-400"
,
.
data
=
(
void
*
)
ARM_SMMU_V1
},
{
.
compatible
=
"arm,mmu-401"
,
.
data
=
(
void
*
)
ARM_SMMU_V1
},
{
.
compatible
=
"arm,mmu-500"
,
.
data
=
(
void
*
)
ARM_SMMU_V2
},
{
.
compatible
=
"arm,smmu-v1"
,
.
data
=
&
smmu_generic_v1
},
{
.
compatible
=
"arm,smmu-v2"
,
.
data
=
&
smmu_generic_v2
},
{
.
compatible
=
"arm,mmu-400"
,
.
data
=
&
smmu_generic_v1
},
{
.
compatible
=
"arm,mmu-401"
,
.
data
=
&
arm_mmu401
},
{
.
compatible
=
"arm,mmu-500"
,
.
data
=
&
arm_mmu500
},
{
.
compatible
=
"cavium,smmu-v2"
,
.
data
=
&
cavium_smmuv2
},
{
},
};
MODULE_DEVICE_TABLE
(
of
,
arm_smmu_of_match
);
...
...
@@ -1741,6 +1867,7 @@ MODULE_DEVICE_TABLE(of, arm_smmu_of_match);
static
int
arm_smmu_device_dt_probe
(
struct
platform_device
*
pdev
)
{
const
struct
of_device_id
*
of_id
;
const
struct
arm_smmu_match_data
*
data
;
struct
resource
*
res
;
struct
arm_smmu_device
*
smmu
;
struct
device
*
dev
=
&
pdev
->
dev
;
...
...
@@ -1756,7 +1883,9 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
smmu
->
dev
=
dev
;
of_id
=
of_match_node
(
arm_smmu_of_match
,
dev
->
of_node
);
smmu
->
version
=
(
enum
arm_smmu_arch_version
)
of_id
->
data
;
data
=
of_id
->
data
;
smmu
->
version
=
data
->
version
;
smmu
->
model
=
data
->
model
;
res
=
platform_get_resource
(
pdev
,
IORESOURCE_MEM
,
0
);
smmu
->
base
=
devm_ioremap_resource
(
dev
,
res
);
...
...
@@ -1822,7 +1951,7 @@ static int arm_smmu_device_dt_probe(struct platform_device *pdev)
parse_driver_options
(
smmu
);
if
(
smmu
->
version
>
ARM_SMMU_V1
&&
if
(
smmu
->
version
==
ARM_SMMU_V2
&&
smmu
->
num_context_banks
!=
smmu
->
num_context_irqs
)
{
dev_err
(
dev
,
"found only %d context interrupt(s) but %d required
\n
"
,
...
...
include/asm-generic/io.h
View file @
9a51f3fc
...
...
@@ -191,7 +191,7 @@ static inline void writeq(u64 value, volatile void __iomem *addr)
#define readl_relaxed readl
#endif
#if
ndef readq_relaxed
#if
defined(readq) && !defined(readq_relaxed)
#define readq_relaxed readq
#endif
...
...
@@ -207,7 +207,7 @@ static inline void writeq(u64 value, volatile void __iomem *addr)
#define writel_relaxed writel
#endif
#if
ndef writeq_relaxed
#if
defined(writeq) && !defined(writeq_relaxed)
#define writeq_relaxed writeq
#endif
...
...
include/linux/io-64-nonatomic-hi-lo.h
View file @
9a51f3fc
...
...
@@ -21,6 +21,23 @@ static inline void hi_lo_writeq(__u64 val, volatile void __iomem *addr)
writel
(
val
,
addr
);
}
static
inline
__u64
hi_lo_readq_relaxed
(
const
volatile
void
__iomem
*
addr
)
{
const
volatile
u32
__iomem
*
p
=
addr
;
u32
low
,
high
;
high
=
readl_relaxed
(
p
+
1
);
low
=
readl_relaxed
(
p
);
return
low
+
((
u64
)
high
<<
32
);
}
static
inline
void
hi_lo_writeq_relaxed
(
__u64
val
,
volatile
void
__iomem
*
addr
)
{
writel_relaxed
(
val
>>
32
,
addr
+
4
);
writel_relaxed
(
val
,
addr
);
}
#ifndef readq
#define readq hi_lo_readq
#endif
...
...
@@ -29,4 +46,12 @@ static inline void hi_lo_writeq(__u64 val, volatile void __iomem *addr)
#define writeq hi_lo_writeq
#endif
#ifndef readq_relaxed
#define readq_relaxed hi_lo_readq_relaxed
#endif
#ifndef writeq_relaxed
#define writeq_relaxed hi_lo_writeq_relaxed
#endif
#endif
/* _LINUX_IO_64_NONATOMIC_HI_LO_H_ */
include/linux/io-64-nonatomic-lo-hi.h
View file @
9a51f3fc
...
...
@@ -21,6 +21,23 @@ static inline void lo_hi_writeq(__u64 val, volatile void __iomem *addr)
writel
(
val
>>
32
,
addr
+
4
);
}
static
inline
__u64
lo_hi_readq_relaxed
(
const
volatile
void
__iomem
*
addr
)
{
const
volatile
u32
__iomem
*
p
=
addr
;
u32
low
,
high
;
low
=
readl_relaxed
(
p
);
high
=
readl_relaxed
(
p
+
1
);
return
low
+
((
u64
)
high
<<
32
);
}
static
inline
void
lo_hi_writeq_relaxed
(
__u64
val
,
volatile
void
__iomem
*
addr
)
{
writel_relaxed
(
val
,
addr
);
writel_relaxed
(
val
>>
32
,
addr
+
4
);
}
#ifndef readq
#define readq lo_hi_readq
#endif
...
...
@@ -29,4 +46,12 @@ static inline void lo_hi_writeq(__u64 val, volatile void __iomem *addr)
#define writeq lo_hi_writeq
#endif
#ifndef readq_relaxed
#define readq_relaxed lo_hi_readq_relaxed
#endif
#ifndef writeq_relaxed
#define writeq_relaxed lo_hi_writeq_relaxed
#endif
#endif
/* _LINUX_IO_64_NONATOMIC_LO_HI_H_ */
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment