* [PATCH v4 6/9] arm64: Add helper to decode register from instruction
From: Suzuki K Poulose @ 2017-01-09 17:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483982912-27215-1-git-send-email-suzuki.poulose@arm.com>
Add a helper to extract the register field from a given
instruction.
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
arch/arm64/include/asm/insn.h | 2 ++
arch/arm64/kernel/insn.c | 29 +++++++++++++++++++++++++++++
2 files changed, 31 insertions(+)
diff --git a/arch/arm64/include/asm/insn.h b/arch/arm64/include/asm/insn.h
index bc85366..aecc07e 100644
--- a/arch/arm64/include/asm/insn.h
+++ b/arch/arm64/include/asm/insn.h
@@ -332,6 +332,8 @@ bool aarch64_insn_is_branch(u32 insn);
u64 aarch64_insn_decode_immediate(enum aarch64_insn_imm_type type, u32 insn);
u32 aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type,
u32 insn, u64 imm);
+u32 aarch64_insn_decode_register(enum aarch64_insn_register_type type,
+ u32 insn);
u32 aarch64_insn_gen_branch_imm(unsigned long pc, unsigned long addr,
enum aarch64_insn_branch_type type);
u32 aarch64_insn_gen_comp_branch_imm(unsigned long pc, unsigned long addr,
diff --git a/arch/arm64/kernel/insn.c b/arch/arm64/kernel/insn.c
index 94b62c1..1f44cf8 100644
--- a/arch/arm64/kernel/insn.c
+++ b/arch/arm64/kernel/insn.c
@@ -417,6 +417,35 @@ u32 __kprobes aarch64_insn_encode_immediate(enum aarch64_insn_imm_type type,
return insn;
}
+u32 aarch64_insn_decode_register(enum aarch64_insn_register_type type,
+ u32 insn)
+{
+ int shift;
+
+ switch (type) {
+ case AARCH64_INSN_REGTYPE_RT:
+ case AARCH64_INSN_REGTYPE_RD:
+ shift = 0;
+ break;
+ case AARCH64_INSN_REGTYPE_RN:
+ shift = 5;
+ break;
+ case AARCH64_INSN_REGTYPE_RT2:
+ case AARCH64_INSN_REGTYPE_RA:
+ shift = 10;
+ break;
+ case AARCH64_INSN_REGTYPE_RM:
+ shift = 16;
+ break;
+ default:
+ pr_err("%s: unknown register type encoding %d\n", __func__,
+ type);
+ return 0;
+ }
+
+ return (insn >> shift) & GENMASK(4, 0);
+}
+
static u32 aarch64_insn_encode_register(enum aarch64_insn_register_type type,
u32 insn,
enum aarch64_insn_register reg)
--
2.7.4
^ permalink raw reply related
* [PATCH v4 5/9] arm64: cpufeature: Define helpers for sys_reg id
From: Suzuki K Poulose @ 2017-01-09 17:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483982912-27215-1-git-send-email-suzuki.poulose@arm.com>
Define helper macros to extract op0, op1, CRn, CRm & op2
for a given sys_reg id. While at it remove the explicit
masking only used for Op0.
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
arch/arm64/include/asm/sysreg.h | 21 ++++++++++++++++++++-
1 file changed, 20 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index 98ae03f..cf43064 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -32,8 +32,27 @@
* [11-8] : CRm
* [7-5] : Op2
*/
+#define Op0_shift 19
+#define Op0_mask 0x3
+#define Op1_shift 16
+#define Op1_mask 0x7
+#define CRn_shift 12
+#define CRn_mask 0xf
+#define CRm_shift 8
+#define CRm_mask 0xf
+#define Op2_shift 5
+#define Op2_mask 0x7
+
#define sys_reg(op0, op1, crn, crm, op2) \
- ((((op0)&3)<<19)|((op1)<<16)|((crn)<<12)|((crm)<<8)|((op2)<<5))
+ (((op0) << Op0_shift) | ((op1) << Op1_shift) | \
+ ((crn) << CRn_shift) | ((crm) << CRm_shift) | \
+ ((op2) << Op2_shift))
+
+#define sys_reg_Op0(id) (((id) >> Op0_shift) & Op0_mask)
+#define sys_reg_Op1(id) (((id) >> Op1_shift) & Op1_mask)
+#define sys_reg_CRn(id) (((id) >> CRn_shift) & CRn_mask)
+#define sys_reg_CRm(id) (((id) >> CRm_shift) & CRm_mask)
+#define sys_reg_Op2(id) (((id) >> Op2_shift) & Op2_mask)
#ifndef CONFIG_BROKEN_GAS_INST
--
2.7.4
^ permalink raw reply related
* [PATCH v4 4/9] arm64: cpufeature: Document the rules of safe value for features
From: Suzuki K Poulose @ 2017-01-09 17:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483982912-27215-1-git-send-email-suzuki.poulose@arm.com>
Document the rules for choosing the safe value for different types
of features.
Cc: Dave Martin <dave.martin@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
Changes since v3:
- Reworded the comment
---
arch/arm64/include/asm/cpufeature.h | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index b4989df..10d5624 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -29,7 +29,20 @@
#include <linux/jump_label.h>
#include <linux/kernel.h>
-/* CPU feature register tracking */
+/*
+ * CPU feature register tracking
+ *
+ * The safe value of a CPUID feature field is dependent on the implications
+ * of the values assigned to it by the architecture. Based on the relationship
+ * between the values, the features are classified into 3 types - LOWER_SAFE,
+ * HIGHER_SAFE and EXACT.
+ *
+ * The lowest value of all the CPUs is chosen for LOWER_SAFE and highest
+ * for HIGHER_SAFE. It is expected that all CPUs have the same value for
+ * a field when EXACT is specified, failing which, the safe value specified
+ * in the table is chosen.
+ */
+
enum ftr_type {
FTR_EXACT, /* Use a predefined safe value */
FTR_LOWER_SAFE, /* Smaller value is safe */
--
2.7.4
^ permalink raw reply related
* [PATCH v4 3/9] arm64: cpufeature: Cleanup feature bit tables
From: Suzuki K Poulose @ 2017-01-09 17:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483982912-27215-1-git-send-email-suzuki.poulose@arm.com>
This patch does the following clean ups :
1) All undescribed fields of a register are now treated as 'strict'
with a safe value of 0. Hence we could leave an empty table for
describing registers which are RAZ.
2) ID_AA64DFR1_EL1 is RAZ and should use the table for RAZ register.
3) ftr_generic32 is used to represent a register with a 32bit feature
value. Rename this to ftr_singl32 to make it more obvious. Since
we don't have a 64bit singe feature register, kill ftr_generic.
Based on a patch by Mark Rutland.
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Reviewed-by: Mark Rutland <mark.rutland@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
arch/arm64/kernel/cpufeature.c | 19 +++++++------------
1 file changed, 7 insertions(+), 12 deletions(-)
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 72bda9d..fb519e1 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -247,18 +247,13 @@ static const struct arm64_ftr_bits ftr_generic_32bits[] = {
ARM64_FTR_END,
};
-static const struct arm64_ftr_bits ftr_generic[] = {
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 0, 64, 0),
- ARM64_FTR_END,
-};
-
-static const struct arm64_ftr_bits ftr_generic32[] = {
+/* Table for a single 32bit feature value */
+static const struct arm64_ftr_bits ftr_single32[] = {
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 0, 32, 0),
ARM64_FTR_END,
};
-static const struct arm64_ftr_bits ftr_aa64raz[] = {
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 0, 64, 0),
+static const struct arm64_ftr_bits ftr_raz[] = {
ARM64_FTR_END,
};
@@ -299,15 +294,15 @@ static const struct __ftr_reg_entry {
/* Op1 = 0, CRn = 0, CRm = 4 */
ARM64_FTR_REG(SYS_ID_AA64PFR0_EL1, ftr_id_aa64pfr0),
- ARM64_FTR_REG(SYS_ID_AA64PFR1_EL1, ftr_aa64raz),
+ ARM64_FTR_REG(SYS_ID_AA64PFR1_EL1, ftr_raz),
/* Op1 = 0, CRn = 0, CRm = 5 */
ARM64_FTR_REG(SYS_ID_AA64DFR0_EL1, ftr_id_aa64dfr0),
- ARM64_FTR_REG(SYS_ID_AA64DFR1_EL1, ftr_generic),
+ ARM64_FTR_REG(SYS_ID_AA64DFR1_EL1, ftr_raz),
/* Op1 = 0, CRn = 0, CRm = 6 */
ARM64_FTR_REG(SYS_ID_AA64ISAR0_EL1, ftr_id_aa64isar0),
- ARM64_FTR_REG(SYS_ID_AA64ISAR1_EL1, ftr_aa64raz),
+ ARM64_FTR_REG(SYS_ID_AA64ISAR1_EL1, ftr_raz),
/* Op1 = 0, CRn = 0, CRm = 7 */
ARM64_FTR_REG(SYS_ID_AA64MMFR0_EL1, ftr_id_aa64mmfr0),
@@ -319,7 +314,7 @@ static const struct __ftr_reg_entry {
ARM64_FTR_REG(SYS_DCZID_EL0, ftr_dczid),
/* Op1 = 3, CRn = 14, CRm = 0 */
- ARM64_FTR_REG(SYS_CNTFRQ_EL0, ftr_generic32),
+ ARM64_FTR_REG(SYS_CNTFRQ_EL0, ftr_single32),
};
static int search_cmp_ftr_reg(const void *id, const void *regp)
--
2.7.4
^ permalink raw reply related
* [PATCH v4 2/9] arm64: cpufeature: remove explicit RAZ fields
From: Suzuki K Poulose @ 2017-01-09 17:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483982912-27215-1-git-send-email-suzuki.poulose@arm.com>
From: Mark Rutland <mark.rutland@arm.com>
We currently have some RAZ fields described explicitly in our
arm64_ftr_bits arrays. These are inconsistently commented, grouped,
and/or applied, and maintaining these is error-prone.
Luckily, we don't need these at all. We'll never need to inspect RAZ
fields to determine feature support, and init_cpu_ftr_reg() will ensure
that any bits without a corresponding arm64_ftr_bits entry are treated
as RES0 with strict matching requirements. In check_update_ftr_reg()
we'll then compare these bits from the relevant cpuinfo_arm64
structures, and need not store them in a arm64_ftr_reg.
This patch removes the unnecessary arm64_ftr_bits entries for RES0 bits.
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
arch/arm64/kernel/cpufeature.c | 15 ---------------
1 file changed, 15 deletions(-)
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index ea02201..72bda9d 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -81,21 +81,16 @@ cpufeature_pan_not_uao(const struct arm64_cpu_capabilities *entry, int __unused)
static const struct arm64_ftr_bits ftr_id_aa64isar0[] = {
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 32, 32, 0),
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64ISAR0_RDM_SHIFT, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 24, 4, 0),
ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR0_ATOMICS_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR0_CRC32_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR0_SHA2_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR0_SHA1_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR0_AES_SHIFT, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 0, 4, 0), /* RAZ */
ARM64_FTR_END,
};
static const struct arm64_ftr_bits ftr_id_aa64pfr0[] = {
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 32, 32, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 28, 4, 0),
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64PFR0_GIC_SHIFT, 4, 0),
S_ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_ASIMD_SHIFT, 4, ID_AA64PFR0_ASIMD_NI),
S_ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_FP_SHIFT, 4, ID_AA64PFR0_FP_NI),
@@ -108,7 +103,6 @@ static const struct arm64_ftr_bits ftr_id_aa64pfr0[] = {
};
static const struct arm64_ftr_bits ftr_id_aa64mmfr0[] = {
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 32, 32, 0),
S_ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR0_TGRAN4_SHIFT, 4, ID_AA64MMFR0_TGRAN4_NI),
S_ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR0_TGRAN64_SHIFT, 4, ID_AA64MMFR0_TGRAN64_NI),
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR0_TGRAN16_SHIFT, 4, ID_AA64MMFR0_TGRAN16_NI),
@@ -126,7 +120,6 @@ static const struct arm64_ftr_bits ftr_id_aa64mmfr0[] = {
};
static const struct arm64_ftr_bits ftr_id_aa64mmfr1[] = {
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 32, 32, 0),
ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR1_PAN_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR1_LOR_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR1_HPD_SHIFT, 4, 0),
@@ -147,7 +140,6 @@ static const struct arm64_ftr_bits ftr_id_aa64mmfr2[] = {
static const struct arm64_ftr_bits ftr_ctr[] = {
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 31, 1, 1), /* RAO */
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 28, 3, 0),
ARM64_FTR_BITS(FTR_STRICT, FTR_HIGHER_SAFE, 24, 4, 0), /* CWG */
ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 20, 4, 0), /* ERG */
ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 16, 4, 1), /* DminLine */
@@ -157,7 +149,6 @@ static const struct arm64_ftr_bits ftr_ctr[] = {
* If we have differing I-cache policies, report it as the weakest - AIVIVT.
*/
ARM64_FTR_BITS(FTR_NONSTRICT, FTR_EXACT, 14, 2, ICACHE_POLICY_AIVIVT), /* L1Ip */
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 4, 10, 0), /* RAZ */
ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 0, 4, 0), /* IminLine */
ARM64_FTR_END,
};
@@ -191,14 +182,12 @@ static const struct arm64_ftr_bits ftr_id_aa64dfr0[] = {
};
static const struct arm64_ftr_bits ftr_mvfr2[] = {
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 8, 24, 0), /* RAZ */
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 4, 4, 0), /* FPMisc */
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 0, 4, 0), /* SIMDMisc */
ARM64_FTR_END,
};
static const struct arm64_ftr_bits ftr_dczid[] = {
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 5, 27, 0), /* RAZ */
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 4, 1, 1), /* DZP */
ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 0, 4, 0), /* BS */
ARM64_FTR_END,
@@ -207,7 +196,6 @@ static const struct arm64_ftr_bits ftr_dczid[] = {
static const struct arm64_ftr_bits ftr_id_isar5[] = {
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_ISAR5_RDM_SHIFT, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 20, 4, 0), /* RAZ */
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_ISAR5_CRC32_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_ISAR5_SHA2_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_ISAR5_SHA1_SHIFT, 4, 0),
@@ -217,14 +205,11 @@ static const struct arm64_ftr_bits ftr_id_isar5[] = {
};
static const struct arm64_ftr_bits ftr_id_mmfr4[] = {
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 8, 24, 0), /* RAZ */
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 4, 4, 0), /* ac2 */
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 0, 4, 0), /* RAZ */
ARM64_FTR_END,
};
static const struct arm64_ftr_bits ftr_id_pfr0[] = {
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 16, 16, 0), /* RAZ */
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 12, 4, 0), /* State3 */
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 8, 4, 0), /* State2 */
ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 4, 4, 0), /* State1 */
--
2.7.4
^ permalink raw reply related
* [PATCH v4 1/9] arm64: cpufeature: treat unknown fields as RES0
From: Suzuki K Poulose @ 2017-01-09 17:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483982912-27215-1-git-send-email-suzuki.poulose@arm.com>
From: Mark Rutland <mark.rutland@arm.com>
Any fields not defined in an arm64_ftr_bits entry are propagated to the
system-wide register value in init_cpu_ftr_reg(), and while we require
that these strictly match for the sanity checks, we don't update them in
update_cpu_ftr_reg().
Generally, the lack of an arm64_ftr_bits entry indicates that the bits
are currently RES0 (as is the case for the upper 32 bits of all
supposedly 32-bit registers).
A better default would be to use zero for the system-wide value of
unallocated bits, making all register checking consistent, and allowing
for subsequent simplifications to the arm64_ftr_bits arrays.
This patch updates init_cpu_ftr_reg() to treat unallocated bits as RES0
for the purpose of the system-wide safe value. These bits will still be
sanity checked with strict match requirements, as is currently the case.
Signed-off-by: Mark Rutland <mark.rutland@arm.com>
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Reviewed-by: Suzuki K Poulose <suzuki.poulose@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
arch/arm64/kernel/cpufeature.c | 12 +++++++++++-
1 file changed, 11 insertions(+), 1 deletion(-)
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index fdf8f04..ea02201 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -410,23 +410,33 @@ static void __init sort_ftr_regs(void)
/*
* Initialise the CPU feature register from Boot CPU values.
* Also initiliases the strict_mask for the register.
+ * Any bits that are not covered by an arm64_ftr_bits entry are considered
+ * RES0 for the system-wide value, and must strictly match.
*/
static void __init init_cpu_ftr_reg(u32 sys_reg, u64 new)
{
u64 val = 0;
u64 strict_mask = ~0x0ULL;
+ u64 valid_mask = 0;
+
const struct arm64_ftr_bits *ftrp;
struct arm64_ftr_reg *reg = get_arm64_ftr_reg(sys_reg);
BUG_ON(!reg);
for (ftrp = reg->ftr_bits; ftrp->width; ftrp++) {
+ u64 ftr_mask = arm64_ftr_mask(ftrp);
s64 ftr_new = arm64_ftr_value(ftrp, new);
val = arm64_ftr_set_value(ftrp, val, ftr_new);
+
+ valid_mask |= ftr_mask;
if (!ftrp->strict)
- strict_mask &= ~arm64_ftr_mask(ftrp);
+ strict_mask &= ~ftr_mask;
}
+
+ val &= valid_mask;
+
reg->sys_val = val;
reg->strict_mask = strict_mask;
}
--
2.7.4
^ permalink raw reply related
* [PATCH v4 0/9] arm64: Expose CPUID registers via emulation
From: Suzuki K Poulose @ 2017-01-09 17:28 UTC (permalink / raw)
To: linux-arm-kernel
This series adds a new ABI to expose the CPU feature registers
to the user space via emulation of MRS instruction. The system exposes
only a limited set of feature values (See the documentation patch)
from the cpufeature infrastructure. The feature bits that are not
exposed are set to the 'safe value' which implies 'not supported'.
Apart from the selected feature registers, we expose MIDR_EL1 (Main
ID Register). The user should be aware that, reading MIDR_EL1 can be
tricky on a heterogeneous system (just like getcpu()). We export the
value of the current CPU where 'MRS' is executed.
Applies on v4.10-rc3.
Changes since V3:
- Mark ID_AA64ISAR0_EL1:RDM visible.
- Improve commentary of the type of feature for safe value computation
- Fix Documentation for the RES0 fields and minor cleanups
- Added Reviewed-by tags from the previous version.
Changes since V2:
- Mark DCZID_EL0 fields visible, as the register is already accessible
to EL0.
- Included the sample program to demonstrate the use of the new ABI in
documentation.
- Fixed minor coding style issues.
Changes since V1:
- Drop mask for Op0 in sys_reg()
- Fix documentation
- Change safe value of MPIDR to drop MT support
- Do not emulate AArch32 ID registers
Mark Rutland (2):
arm64: cpufeature: treat unknown fields as RES0
arm64: cpufeature: remove explicit RAZ fields
Suzuki K Poulose (7):
arm64: cpufeature: Cleanup feature bit tables
arm64: cpufeature: Document the rules of safe value for features
arm64: cpufeature: Define helpers for sys_reg id
arm64: Add helper to decode register from instruction
arm64: cpufeature: Track user visible fields
arm64: cpufeature: Expose CPUID registers by emulation
arm64: Documentation - Expose CPU feature registers
Documentation/arm64/cpu-feature-registers.txt | 240 ++++++++++++++++++
arch/arm64/include/asm/cpufeature.h | 26 +-
arch/arm64/include/asm/insn.h | 2 +
arch/arm64/include/asm/sysreg.h | 25 +-
arch/arm64/include/uapi/asm/hwcap.h | 1 +
arch/arm64/kernel/cpufeature.c | 340 +++++++++++++++++---------
arch/arm64/kernel/cpuinfo.c | 1 +
arch/arm64/kernel/insn.c | 29 +++
arch/arm64/kernel/traps.c | 2 +-
9 files changed, 545 insertions(+), 121 deletions(-)
create mode 100644 Documentation/arm64/cpu-feature-registers.txt
--
2.7.4
^ permalink raw reply
* [PATCH 0/3] watchdog: imx2: handle WMCR only being available on i.MX35 and later
From: Guenter Roeck @ 2017-01-09 17:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170109095039.11979-1-u.kleine-koenig@pengutronix.de>
On Mon, Jan 09, 2017 at 10:50:36AM +0100, Uwe Kleine-K?nig wrote:
> Hello,
>
> this is my approach to fix the issue reported by Vladimir Zapolskiy.
>
So ...
Unfortunately, I don't see any convergence on this issue. My take is that,
given the lack of convergence, it can't be that severe since no one feels
that it is important enough to get it fixed one way or the other to budge
from your positions.
I had the other patch in my watchdog-next branch. I'll drop it, and wait for
the two of you to agree to something. You can either agree to that something,
or try to convince Wim to accept one or the other patch set. For my part, I'll
only accept a patch or patch series which has a Signed-off: and/or Reviewed-by:
from both of you.
Maybe you can get together into a room and agree not to leave it until white
smoke comes out of the chimney. Just an idea.
Thanks,
Guenter
^ permalink raw reply
* [PATCH] ARM: prima2: constify reset_control_ops structures
From: Bhumika Goyal @ 2017-01-09 17:17 UTC (permalink / raw)
To: linux-arm-kernel
Declare reset_control_ops structures as const as they are only stored
in the ops field of a reset_controller_dev structure. This field is of
type const struct reset_control_ops *, so reset_control_ops structures
having this property can be declared as const.
Done using Coccinelle:
@r1 disable optional_qualifier@
identifier i;
position p;
@@
static struct reset_control_ops i at p={...};
@ok1@
identifier r1.i;
position p;
struct reset_controller_dev x;
@@
x.ops=&i at p;
@bad@
position p!={r1.p,ok1.p};
identifier r1.i;
@@
i at p
@depends on !bad disable optional_qualifier@
identifier r1.i;
@@
+const
struct reset_control_ops i;
File size before: arch/arm/mach-prima2/rstc.o
text data bss dec hex filename
992 236 4 1232 4d0 arch/arm/mach-prima2/rstc.o
File size after: arch/arm/mach-prima2/rstc.o
text data bss dec hex filename
1008 220 4 1232 4d0 arch/arm/mach-prima2/rstc.o
Signed-off-by: Bhumika Goyal <bhumirks@gmail.com>
---
arch/arm/mach-prima2/rstc.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/mach-prima2/rstc.c b/arch/arm/mach-prima2/rstc.c
index 7c251eb..1cdb616 100644
--- a/arch/arm/mach-prima2/rstc.c
+++ b/arch/arm/mach-prima2/rstc.c
@@ -54,7 +54,7 @@ static int sirfsoc_reset_module(struct reset_controller_dev *rcdev,
return 0;
}
-static struct reset_control_ops sirfsoc_rstc_ops = {
+static const struct reset_control_ops sirfsoc_rstc_ops = {
.reset = sirfsoc_reset_module,
};
--
1.9.1
^ permalink raw reply related
* [PATCH v2 6/6] arm64: allwinner: a64: Increase the MMC max frequency
From: Chen-Yu Tsai @ 2017-01-09 17:01 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <3689701f93c8426bfa476e9a5f31c3642a8a898e.1483980339.git-series.maxime.ripard@free-electrons.com>
On Tue, Jan 10, 2017 at 12:46 AM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> All the controllers can have a maximum frequency of 200MHz.
>
> Since older SoCs cannot go that high, we cannot change the default maximum
> frequency, but fortunately for us we have a property for that in the DT.
>
> This also has the side effect of allowing to use the MMC HS200 mode for the
> boards that support it (with either 1.2v or 1.8v IOs).
>
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
> arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 3 +++
> 1 file changed, 3 insertions(+), 0 deletions(-)
>
> diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
> index 8e149498e096..f46ae965cf5b 100644
> --- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
> +++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
> @@ -332,6 +332,7 @@
> resets = <&ccu RST_BUS_MMC0>;
> reset-names = "ahb";
> interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
> + max-frequency = <200000000>;
You also have to set one of MMC_CAP2_HS200* in the driver,
or mmc-hs200-1_8v or mmc-hs200-1_2v in the device tree to
actually use HS200, right?
ChenYu
> status = "disabled";
> #address-cells = <1>;
> #size-cells = <0>;
> @@ -345,6 +346,7 @@
> resets = <&ccu RST_BUS_MMC1>;
> reset-names = "ahb";
> interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
> + max-frequency = <200000000>;
> status = "disabled";
> #address-cells = <1>;
> #size-cells = <0>;
> @@ -358,6 +360,7 @@
> resets = <&ccu RST_BUS_MMC2>;
> reset-names = "ahb";
> interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
> + max-frequency = <200000000>;
> status = "disabled";
> #address-cells = <1>;
> #size-cells = <0>;
> --
> git-series 0.8.11
^ permalink raw reply
* [RFC PATCH] vring: Force use of DMA API for ARM-based systems
From: Michael S. Tsirkin @ 2017-01-09 17:00 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <fbf1a09a-bdb2-504a-f4a7-56fa54b6292b@arm.com>
On Mon, Jan 09, 2017 at 11:24:04AM +0000, Robin Murphy wrote:
> On 06/01/17 21:51, Andy Lutomirski wrote:
> > On Fri, Jan 6, 2017 at 10:32 AM, Robin Murphy <robin.murphy@arm.com> wrote:
> >> On 06/01/17 17:48, Jean-Philippe Brucker wrote:
> >>> Hi Will,
> >>>
> >>> On 20/12/16 15:14, Will Deacon wrote:
> >>>> Booting Linux on an ARM fastmodel containing an SMMU emulation results
> >>>> in an unexpected I/O page fault from the legacy virtio-blk PCI device:
> >>>>
> >>>> [ 1.211721] arm-smmu-v3 2b400000.smmu: event 0x10 received:
> >>>> [ 1.211800] arm-smmu-v3 2b400000.smmu: 0x00000000fffff010
> >>>> [ 1.211880] arm-smmu-v3 2b400000.smmu: 0x0000020800000000
> >>>> [ 1.211959] arm-smmu-v3 2b400000.smmu: 0x00000008fa081002
> >>>> [ 1.212075] arm-smmu-v3 2b400000.smmu: 0x0000000000000000
> >>>> [ 1.212155] arm-smmu-v3 2b400000.smmu: event 0x10 received:
> >>>> [ 1.212234] arm-smmu-v3 2b400000.smmu: 0x00000000fffff010
> >>>> [ 1.212314] arm-smmu-v3 2b400000.smmu: 0x0000020800000000
> >>>> [ 1.212394] arm-smmu-v3 2b400000.smmu: 0x00000008fa081000
> >>>> [ 1.212471] arm-smmu-v3 2b400000.smmu: 0x0000000000000000
> >>>>
> >>>> <system hangs failing to read partition table>
> >>>>
> >>>> This is because the virtio-blk is behind an SMMU, so we have consequently
> >>>> swizzled its DMA ops and configured the SMMU to translate accesses. This
> >>>> then requires the vring code to use the DMA API to establish translations,
> >>>> otherwise all transactions will result in fatal faults and termination.
> >>>>
> >>>> Given that ARM-based systems only see an SMMU if one is really present
> >>>> (the topology is all described by firmware tables such as device-tree or
> >>>> IORT), then we can safely use the DMA API for all virtio devices.
> >>>
> >>> There is a problem with the platform block device on that same model.
> >>> Since it's not behind the SMMU, the DMA ops fall back to swiotlb, which
> >>> limits the number of mappings.
> >>>
> >>> It used to work with 4.9, but since 9491ae4 ("mm: don't cap request size
> >>> based on read-ahead setting") unlocked read-ahead, we quickly run into
> >>> the limit of swiotlb and panic:
> >>>
> >>> [ 5.382359] virtio-mmio 1c130000.virtio_block: swiotlb buffer is full
> >>> (sz: 491520 bytes)
> >>> [ 5.382452] virtio-mmio 1c130000.virtio_block: DMA: Out of SW-IOMMU
> >>> space for 491520 bytes
> >>> [ 5.382531] Kernel panic - not syncing: DMA: Random memory could be
> >>> DMA written
> >>> ...
> >>> [ 5.383148] [<ffff0000083ad754>] swiotlb_map_page+0x194/0x1a0
> >>> [ 5.383226] [<ffff000008096bb8>] __swiotlb_map_page+0x20/0x88
> >>> [ 5.383320] [<ffff0000084bf738>] vring_map_one_sg.isra.1+0x70/0x88
> >>> [ 5.383417] [<ffff0000084c04fc>] virtqueue_add_sgs+0x2ec/0x4e8
> >>> [ 5.383505] [<ffff00000856d99c>] __virtblk_add_req+0x9c/0x1a8
> >>> ...
> >>> [ 5.384449] [<ffff0000081829c4>] ondemand_readahead+0xfc/0x2b8
> >>>
> >>> Commit 9491ae4 caps the read-ahead request to a limit set by the backing
> >>> device. For virtio-blk, it is infinite (as set by the call to
> >>> blk_queue_max_hw_sectors in virtblk_probe).
> >>>
> >>> I'm not sure how to fix this. Setting an arbitrary sector limit in the
> >>> virtio-blk driver seems unfair to other users. Maybe we should check if
> >>> the device is behind a hardware IOMMU before using the DMA API?
> >>
> >> Hmm, this looks more like the virtio_block device simply has the wrong
> >> DMA mask to begin with. For virtio-pci we set the streaming DMA mask to
> >> 64 bits - should a platform device not be similarly capable?
> >
> > If it's not, then turning off DMA API will cause random corruption.
> > ISTM one way or another the bug is in either the DMA ops or in the
> > driver initialization.
>
> OK, having looked a little deeper, I reckon virtio_mmio_probe() is
> indeed missing a dma_set_mask() call compared to its PCI friends. The
> only question then is where does virtio-mmio stand with respect to
> legacy/modern/44-bit/64-bit etc.?
>
> Robin.
AFAIK current drivers support the modern interface since Jan 2015.
44/64 is almost the same as PCI really, except page size isn't fixed to 4K.
So legacy ones need to set coherent mask to 32 + PAGE_SHIFT.
> >
> > --Andy
> >
^ permalink raw reply
* [RFC PATCH v3 3/5] ARM: NOMMU: Introduce dma operations for noMMU
From: Robin Murphy @ 2017-01-09 16:59 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <63b3b1d4-c93d-3244-e421-b8e7993075df@arm.com>
On 09/01/17 16:51, Vladimir Murzin wrote:
> Hi Robin,
>
> On 09/01/17 16:43, Robin Murphy wrote:
>> Hi Vladimir,
>>
>> On 09/01/17 13:47, Vladimir Murzin wrote:
>>> R/M classes of cpus can have memory covered by MPU which in turn might
>>> configure RAM as Normal i.e. bufferable and cacheable. It breaks
>>> dma_alloc_coherent() and friends, since data can stuck in caches now
>>> or be buffered.
>>>
>>> This patch factors out DMA support for NOMMU configuration into
>>> separate entity which provides dedicated dma_ops. We have to handle
>>> there several cases:
>>> - configurations with MMU/MPU setup
>>> - configurations without MMU/MPU setup
>>> - special case for M-class, since caches and MPU there are optional
>>>
>>> In general we rely on default DMA area for coherent allocations or/and
>>> per-device memory reserves suitable for coherent DMA, so if such
>>> regions are set coherent allocations go from there.
>>>
>>> In case MPU/MPU was not setup we fallback to normal page allocator for
>>> DMA memory allocation.
>>>
>>> In case we run M-class cpus, for configuration without cache support
>>> (like Cortex-M3/M4) dma operations are forced to be coherent and wired
>>> with dma-noop (such decision is made based on cacheid global
>>> variable); however, if caches are detected there and no DMA coherent
>>> region is given (either default or per-device), dma is disallowed even
>>> MPU is not set - it is because M-class implement system memory map
>>> which defines part of address space as Normal memory.
>>>
>>> Reported-by: Alexandre Torgue <alexandre.torgue@st.com>
>>> Reported-by: Andras Szemzo <sza@esh.hu>
>>> Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
>>> ---
>>> arch/arm/include/asm/dma-mapping.h | 3 +-
>>> arch/arm/mm/Makefile | 5 +-
>>> arch/arm/mm/dma-mapping-nommu.c | 252 +++++++++++++++++++++++++++++++++++++
>>> 3 files changed, 256 insertions(+), 4 deletions(-)
>>> create mode 100644 arch/arm/mm/dma-mapping-nommu.c
>>>
>>> diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h
>>> index bf02dbd..559faad 100644
>>> --- a/arch/arm/include/asm/dma-mapping.h
>>> +++ b/arch/arm/include/asm/dma-mapping.h
>>> @@ -20,7 +20,8 @@ static inline struct dma_map_ops *__generic_dma_ops(struct device *dev)
>>> {
>>> if (dev && dev->archdata.dma_ops)
>>> return dev->archdata.dma_ops;
>>> - return &arm_dma_ops;
>>> +
>>> + return IS_ENABLED(CONFIG_MMU) ? &arm_dma_ops : &dma_noop_ops;
>>> }
>>>
>>> static inline struct dma_map_ops *get_dma_ops(struct device *dev)
>>> diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile
>>> index 2ac7988..5796357 100644
>>> --- a/arch/arm/mm/Makefile
>>> +++ b/arch/arm/mm/Makefile
>>> @@ -2,9 +2,8 @@
>>> # Makefile for the linux arm-specific parts of the memory manager.
>>> #
>>>
>>> -obj-y := dma-mapping.o extable.o fault.o init.o \
>>> - iomap.o
>>> -
>>> +obj-y := extable.o fault.o init.o iomap.o
>>> +obj-y += dma-mapping$(MMUEXT).o
>>> obj-$(CONFIG_MMU) += fault-armv.o flush.o idmap.o ioremap.o \
>>> mmap.o pgd.o mmu.o pageattr.o
>>>
>>> diff --git a/arch/arm/mm/dma-mapping-nommu.c b/arch/arm/mm/dma-mapping-nommu.c
>>> new file mode 100644
>>> index 0000000..a5c50fb
>>> --- /dev/null
>>> +++ b/arch/arm/mm/dma-mapping-nommu.c
>>> @@ -0,0 +1,252 @@
>>> +/*
>>> + * Based on linux/arch/arm/mm/dma-mapping.c
>>> + *
>>> + * Copyright (C) 2000-2004 Russell King
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU General Public License version 2 as
>>> + * published by the Free Software Foundation.
>>> + *
>>> + */
>>> +
>>> +#include <linux/export.h>
>>> +#include <linux/mm.h>
>>> +#include <linux/dma-mapping.h>
>>> +#include <linux/scatterlist.h>
>>> +
>>> +#include <asm/cachetype.h>
>>> +#include <asm/cacheflush.h>
>>> +#include <asm/outercache.h>
>>> +#include <asm/cp15.h>
>>> +
>>> +#include "dma.h"
>>> +
>>> +/*
>>> + * dma_noop_ops is used if
>>> + * - MMU/MPU is off
>>> + * - cpu is v7m w/o cache support
>>> + * - device is coherent
>>> + * otherwise arm_nommu_dma_ops is used.
>>> + *
>>> + * arm_nommu_dma_ops rely on consistent DMA memory (please, refer to
>>> + * [1] on how to declare such memory).
>>> + *
>>> + * [1] Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
>>> + */
>>> +
>>> +static void *arm_nommu_dma_alloc(struct device *dev, size_t size,
>>> + dma_addr_t *dma_handle, gfp_t gfp,
>>> + unsigned long attrs)
>>> +
>>> +{
>>> + struct dma_map_ops *ops = &dma_noop_ops;
>>> +
>>> + /*
>>> + * We are here because:
>>> + * - no consistent DMA region has been defined, so we can't
>>> + * continue.
>>> + * - there is no space left in consistent DMA region, so we
>>> + * only can fallback to generic allocator if we are
>>> + * advertised that consistency is not required.
>>> + */
>>> +
>>> + if (attrs & DMA_ATTR_NON_CONSISTENT)
>>> + return ops->alloc(dev, size, dma_handle, gfp, attrs);
>>> +
>>> + WARN_ON_ONCE(1);
>>> + return NULL;
>>> +}
>>> +
>>> +static void arm_nommu_dma_free(struct device *dev, size_t size,
>>> + void *cpu_addr, dma_addr_t dma_addr,
>>> + unsigned long attrs)
>>> +{
>>> + struct dma_map_ops *ops = &dma_noop_ops;
>>> +
>>> + if (attrs & DMA_ATTR_NON_CONSISTENT)
>>> + ops->free(dev, size, cpu_addr, dma_addr, attrs);
>>> +
>>> + WARN_ON_ONCE(1);
>>> + return;
>>> +}
>>> +
>>> +static int arm_nommu_dma_mmap(struct device *dev, struct vm_area_struct *vma,
>>> + void *cpu_addr, dma_addr_t dma_addr, size_t size,
>>> + unsigned long attrs)
>>> +{
>>> + struct dma_map_ops *ops = &dma_noop_ops;
>>> + int ret;
>>> +
>>> + if (dma_mmap_from_coherent(dev, vma, cpu_addr, size, &ret))
>>> + return ret;
>>> +
>>> + if (attrs & DMA_ATTR_NON_CONSISTENT)
>>> + return ops->mmap(dev, vma, cpu_addr, dma_addr, size, attrs);
>>> +
>>> + WARN_ON_ONCE(1);
>>> + return -ENXIO;
>>> +}
>>> +
>>> +static void __dma_page_cpu_to_dev(dma_addr_t handle, size_t size,
>>> + enum dma_data_direction dir)
>>> +{
>>> + dmac_unmap_area(__va(handle), size, dir);
>>> +
>>> + if (dir == DMA_FROM_DEVICE)
>>> + outer_inv_range(handle, handle + size);
>>> + else
>>> + outer_clean_range(handle, handle + size);
>>> +}
>>> +
>>> +static void __dma_page_dev_to_cpu(dma_addr_t handle, size_t size,
>>> + enum dma_data_direction dir)
>>> +{
>>> + if (dir != DMA_TO_DEVICE) {
>>> + outer_inv_range(handle, handle + size);
>>> + dmac_unmap_area(__va(handle), size, dir);
>>> + }
>>> +}
>>
>> Nit: I appreciate that the situation here makes it OK by construction,
>> but CPU cache maintenance on a DMA address just looks *so* wrong :)
>> Could we pass either the "virtual" or physical version of the address as
>> the argument to these helpers so that the code looks less crazy at a glance?
>
> Something like bellow?
>
> static void __dma_page_dev_to_cpu(dma_addr_t paddr, size_t size,
^
I meant more in terms of this being a const void* or phys_addr_t ;)
> enum dma_data_direction dir)
> {
> if (dir != DMA_TO_DEVICE) {
> outer_inv_range(paddr, paddr + size);
> dmac_unmap_area(__va(paddr), size, dir);
> }
>
> Btw, thanks for having a look!
Otherwise, I think the rest of the series looks OK, thanks for
respinning it.
Robin.
> Cheers
> Vladimir
>
>>
>> Robin.
>>
>>> +static dma_addr_t arm_nommu_dma_map_page(struct device *dev, struct page *page,
>>> + unsigned long offset, size_t size,
>>> + enum dma_data_direction dir,
>>> + unsigned long attrs)
>>> +{
>>> + dma_addr_t handle = page_to_phys(page) + offset;
>>> +
>>> + __dma_page_cpu_to_dev(handle, size, dir);
>>> +
>>> + return handle;
>>> +}
>>> +
>>> +static void arm_nommu_dma_unmap_page(struct device *dev, dma_addr_t handle,
>>> + size_t size, enum dma_data_direction dir,
>>> + unsigned long attrs)
>>> +{
>>> + __dma_page_dev_to_cpu(handle, size, dir);
>>> +}
>>> +
>>> +
>>> +static int arm_nommu_dma_map_sg(struct device *dev, struct scatterlist *sgl,
>>> + int nents, enum dma_data_direction dir,
>>> + unsigned long attrs)
>>> +{
>>> + int i;
>>> + struct scatterlist *sg;
>>> +
>>> + for_each_sg(sgl, sg, nents, i) {
>>> + sg_dma_address(sg) = sg_phys(sg);
>>> + sg_dma_len(sg) = sg->length;
>>> + __dma_page_cpu_to_dev(sg_dma_address(sg), sg_dma_len(sg), dir);
>>> + }
>>> +
>>> + return nents;
>>> +}
>>> +
>>> +static void arm_nommu_dma_unmap_sg(struct device *dev, struct scatterlist *sgl,
>>> + int nents, enum dma_data_direction dir,
>>> + unsigned long attrs)
>>> +{
>>> + struct scatterlist *sg;
>>> + int i;
>>> +
>>> + for_each_sg(sgl, sg, nents, i)
>>> + __dma_page_dev_to_cpu(sg_dma_address(sg), sg_dma_len(sg), dir);
>>> +}
>>> +
>>> +static void arm_nommu_dma_sync_single_for_device(struct device *dev,
>>> + dma_addr_t handle, size_t size, enum dma_data_direction dir)
>>> +{
>>> + __dma_page_cpu_to_dev(handle, size, dir);
>>> +}
>>> +
>>> +static void arm_nommu_dma_sync_single_for_cpu(struct device *dev,
>>> + dma_addr_t handle, size_t size, enum dma_data_direction dir)
>>> +{
>>> + __dma_page_cpu_to_dev(handle, size, dir);
>>> +}
>>> +
>>> +static void arm_nommu_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sgl,
>>> + int nents, enum dma_data_direction dir)
>>> +{
>>> + struct scatterlist *sg;
>>> + int i;
>>> +
>>> + for_each_sg(sgl, sg, nents, i)
>>> + __dma_page_cpu_to_dev(sg_dma_address(sg), sg_dma_len(sg), dir);
>>> +}
>>> +
>>> +static void arm_nommu_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sgl,
>>> + int nents, enum dma_data_direction dir)
>>> +{
>>> + struct scatterlist *sg;
>>> + int i;
>>> +
>>> + for_each_sg(sgl, sg, nents, i)
>>> + __dma_page_dev_to_cpu(sg_dma_address(sg), sg_dma_len(sg), dir);
>>> +}
>>> +
>>> +struct dma_map_ops arm_nommu_dma_ops = {
>>> + .alloc = arm_nommu_dma_alloc,
>>> + .free = arm_nommu_dma_free,
>>> + .mmap = arm_nommu_dma_mmap,
>>> + .map_page = arm_nommu_dma_map_page,
>>> + .unmap_page = arm_nommu_dma_unmap_page,
>>> + .map_sg = arm_nommu_dma_map_sg,
>>> + .unmap_sg = arm_nommu_dma_unmap_sg,
>>> + .sync_single_for_device = arm_nommu_dma_sync_single_for_device,
>>> + .sync_single_for_cpu = arm_nommu_dma_sync_single_for_cpu,
>>> + .sync_sg_for_device = arm_nommu_dma_sync_sg_for_device,
>>> + .sync_sg_for_cpu = arm_nommu_dma_sync_sg_for_cpu,
>>> +};
>>> +EXPORT_SYMBOL(arm_nommu_dma_ops);
>>> +
>>> +static struct dma_map_ops *arm_nommu_get_dma_map_ops(bool coherent)
>>> +{
>>> + return coherent ? &dma_noop_ops : &arm_nommu_dma_ops;
>>> +}
>>> +
>>> +void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
>>> + const struct iommu_ops *iommu, bool coherent)
>>> +{
>>> + struct dma_map_ops *dma_ops;
>>> +
>>> + if (IS_ENABLED(CONFIG_CPU_V7M)) {
>>> + /*
>>> + * Cache support for v7m is optional, so can be treated as
>>> + * coherent if no cache has been detected. Note that it is not
>>> + * enough to check if MPU is in use or not since in absense of
>>> + * MPU system memory map is used.
>>> + */
>>> + dev->archdata.dma_coherent = (cacheid) ? coherent : true;
>>> + } else {
>>> + /*
>>> + * Assume coherent DMA in case MMU/MPU has not been set up.
>>> + */
>>> + dev->archdata.dma_coherent = (get_cr() & CR_M) ? coherent : true;
>>> + }
>>> +
>>> + dma_ops = arm_nommu_get_dma_map_ops(dev->archdata.dma_coherent);
>>> +
>>> + set_dma_ops(dev, dma_ops);
>>> +}
>>> +
>>> +void arch_teardown_dma_ops(struct device *dev)
>>> +{
>>> +}
>>> +
>>> +int dma_supported(struct device *dev, u64 mask)
>>> +{
>>> + return 1;
>>> +}
>>> +
>>> +EXPORT_SYMBOL(dma_supported);
>>> +
>>> +#define PREALLOC_DMA_DEBUG_ENTRIES 4096
>>> +
>>> +static int __init dma_debug_do_init(void)
>>> +{
>>> + dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES);
>>> + return 0;
>>> +}
>>> +core_initcall(dma_debug_do_init);
>>>
>>
>>
>
>
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>
^ permalink raw reply
* [RFC PATCH v3 3/5] ARM: NOMMU: Introduce dma operations for noMMU
From: Vladimir Murzin @ 2017-01-09 16:51 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <c4eb8a2b-65f0-9de6-76c3-7f5f2cefddab@arm.com>
Hi Robin,
On 09/01/17 16:43, Robin Murphy wrote:
> Hi Vladimir,
>
> On 09/01/17 13:47, Vladimir Murzin wrote:
>> R/M classes of cpus can have memory covered by MPU which in turn might
>> configure RAM as Normal i.e. bufferable and cacheable. It breaks
>> dma_alloc_coherent() and friends, since data can stuck in caches now
>> or be buffered.
>>
>> This patch factors out DMA support for NOMMU configuration into
>> separate entity which provides dedicated dma_ops. We have to handle
>> there several cases:
>> - configurations with MMU/MPU setup
>> - configurations without MMU/MPU setup
>> - special case for M-class, since caches and MPU there are optional
>>
>> In general we rely on default DMA area for coherent allocations or/and
>> per-device memory reserves suitable for coherent DMA, so if such
>> regions are set coherent allocations go from there.
>>
>> In case MPU/MPU was not setup we fallback to normal page allocator for
>> DMA memory allocation.
>>
>> In case we run M-class cpus, for configuration without cache support
>> (like Cortex-M3/M4) dma operations are forced to be coherent and wired
>> with dma-noop (such decision is made based on cacheid global
>> variable); however, if caches are detected there and no DMA coherent
>> region is given (either default or per-device), dma is disallowed even
>> MPU is not set - it is because M-class implement system memory map
>> which defines part of address space as Normal memory.
>>
>> Reported-by: Alexandre Torgue <alexandre.torgue@st.com>
>> Reported-by: Andras Szemzo <sza@esh.hu>
>> Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
>> ---
>> arch/arm/include/asm/dma-mapping.h | 3 +-
>> arch/arm/mm/Makefile | 5 +-
>> arch/arm/mm/dma-mapping-nommu.c | 252 +++++++++++++++++++++++++++++++++++++
>> 3 files changed, 256 insertions(+), 4 deletions(-)
>> create mode 100644 arch/arm/mm/dma-mapping-nommu.c
>>
>> diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h
>> index bf02dbd..559faad 100644
>> --- a/arch/arm/include/asm/dma-mapping.h
>> +++ b/arch/arm/include/asm/dma-mapping.h
>> @@ -20,7 +20,8 @@ static inline struct dma_map_ops *__generic_dma_ops(struct device *dev)
>> {
>> if (dev && dev->archdata.dma_ops)
>> return dev->archdata.dma_ops;
>> - return &arm_dma_ops;
>> +
>> + return IS_ENABLED(CONFIG_MMU) ? &arm_dma_ops : &dma_noop_ops;
>> }
>>
>> static inline struct dma_map_ops *get_dma_ops(struct device *dev)
>> diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile
>> index 2ac7988..5796357 100644
>> --- a/arch/arm/mm/Makefile
>> +++ b/arch/arm/mm/Makefile
>> @@ -2,9 +2,8 @@
>> # Makefile for the linux arm-specific parts of the memory manager.
>> #
>>
>> -obj-y := dma-mapping.o extable.o fault.o init.o \
>> - iomap.o
>> -
>> +obj-y := extable.o fault.o init.o iomap.o
>> +obj-y += dma-mapping$(MMUEXT).o
>> obj-$(CONFIG_MMU) += fault-armv.o flush.o idmap.o ioremap.o \
>> mmap.o pgd.o mmu.o pageattr.o
>>
>> diff --git a/arch/arm/mm/dma-mapping-nommu.c b/arch/arm/mm/dma-mapping-nommu.c
>> new file mode 100644
>> index 0000000..a5c50fb
>> --- /dev/null
>> +++ b/arch/arm/mm/dma-mapping-nommu.c
>> @@ -0,0 +1,252 @@
>> +/*
>> + * Based on linux/arch/arm/mm/dma-mapping.c
>> + *
>> + * Copyright (C) 2000-2004 Russell King
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + *
>> + */
>> +
>> +#include <linux/export.h>
>> +#include <linux/mm.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/scatterlist.h>
>> +
>> +#include <asm/cachetype.h>
>> +#include <asm/cacheflush.h>
>> +#include <asm/outercache.h>
>> +#include <asm/cp15.h>
>> +
>> +#include "dma.h"
>> +
>> +/*
>> + * dma_noop_ops is used if
>> + * - MMU/MPU is off
>> + * - cpu is v7m w/o cache support
>> + * - device is coherent
>> + * otherwise arm_nommu_dma_ops is used.
>> + *
>> + * arm_nommu_dma_ops rely on consistent DMA memory (please, refer to
>> + * [1] on how to declare such memory).
>> + *
>> + * [1] Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
>> + */
>> +
>> +static void *arm_nommu_dma_alloc(struct device *dev, size_t size,
>> + dma_addr_t *dma_handle, gfp_t gfp,
>> + unsigned long attrs)
>> +
>> +{
>> + struct dma_map_ops *ops = &dma_noop_ops;
>> +
>> + /*
>> + * We are here because:
>> + * - no consistent DMA region has been defined, so we can't
>> + * continue.
>> + * - there is no space left in consistent DMA region, so we
>> + * only can fallback to generic allocator if we are
>> + * advertised that consistency is not required.
>> + */
>> +
>> + if (attrs & DMA_ATTR_NON_CONSISTENT)
>> + return ops->alloc(dev, size, dma_handle, gfp, attrs);
>> +
>> + WARN_ON_ONCE(1);
>> + return NULL;
>> +}
>> +
>> +static void arm_nommu_dma_free(struct device *dev, size_t size,
>> + void *cpu_addr, dma_addr_t dma_addr,
>> + unsigned long attrs)
>> +{
>> + struct dma_map_ops *ops = &dma_noop_ops;
>> +
>> + if (attrs & DMA_ATTR_NON_CONSISTENT)
>> + ops->free(dev, size, cpu_addr, dma_addr, attrs);
>> +
>> + WARN_ON_ONCE(1);
>> + return;
>> +}
>> +
>> +static int arm_nommu_dma_mmap(struct device *dev, struct vm_area_struct *vma,
>> + void *cpu_addr, dma_addr_t dma_addr, size_t size,
>> + unsigned long attrs)
>> +{
>> + struct dma_map_ops *ops = &dma_noop_ops;
>> + int ret;
>> +
>> + if (dma_mmap_from_coherent(dev, vma, cpu_addr, size, &ret))
>> + return ret;
>> +
>> + if (attrs & DMA_ATTR_NON_CONSISTENT)
>> + return ops->mmap(dev, vma, cpu_addr, dma_addr, size, attrs);
>> +
>> + WARN_ON_ONCE(1);
>> + return -ENXIO;
>> +}
>> +
>> +static void __dma_page_cpu_to_dev(dma_addr_t handle, size_t size,
>> + enum dma_data_direction dir)
>> +{
>> + dmac_unmap_area(__va(handle), size, dir);
>> +
>> + if (dir == DMA_FROM_DEVICE)
>> + outer_inv_range(handle, handle + size);
>> + else
>> + outer_clean_range(handle, handle + size);
>> +}
>> +
>> +static void __dma_page_dev_to_cpu(dma_addr_t handle, size_t size,
>> + enum dma_data_direction dir)
>> +{
>> + if (dir != DMA_TO_DEVICE) {
>> + outer_inv_range(handle, handle + size);
>> + dmac_unmap_area(__va(handle), size, dir);
>> + }
>> +}
>
> Nit: I appreciate that the situation here makes it OK by construction,
> but CPU cache maintenance on a DMA address just looks *so* wrong :)
> Could we pass either the "virtual" or physical version of the address as
> the argument to these helpers so that the code looks less crazy at a glance?
Something like bellow?
static void __dma_page_dev_to_cpu(dma_addr_t paddr, size_t size,
enum dma_data_direction dir)
{
if (dir != DMA_TO_DEVICE) {
outer_inv_range(paddr, paddr + size);
dmac_unmap_area(__va(paddr), size, dir);
}
Btw, thanks for having a look!
Cheers
Vladimir
>
> Robin.
>
>> +static dma_addr_t arm_nommu_dma_map_page(struct device *dev, struct page *page,
>> + unsigned long offset, size_t size,
>> + enum dma_data_direction dir,
>> + unsigned long attrs)
>> +{
>> + dma_addr_t handle = page_to_phys(page) + offset;
>> +
>> + __dma_page_cpu_to_dev(handle, size, dir);
>> +
>> + return handle;
>> +}
>> +
>> +static void arm_nommu_dma_unmap_page(struct device *dev, dma_addr_t handle,
>> + size_t size, enum dma_data_direction dir,
>> + unsigned long attrs)
>> +{
>> + __dma_page_dev_to_cpu(handle, size, dir);
>> +}
>> +
>> +
>> +static int arm_nommu_dma_map_sg(struct device *dev, struct scatterlist *sgl,
>> + int nents, enum dma_data_direction dir,
>> + unsigned long attrs)
>> +{
>> + int i;
>> + struct scatterlist *sg;
>> +
>> + for_each_sg(sgl, sg, nents, i) {
>> + sg_dma_address(sg) = sg_phys(sg);
>> + sg_dma_len(sg) = sg->length;
>> + __dma_page_cpu_to_dev(sg_dma_address(sg), sg_dma_len(sg), dir);
>> + }
>> +
>> + return nents;
>> +}
>> +
>> +static void arm_nommu_dma_unmap_sg(struct device *dev, struct scatterlist *sgl,
>> + int nents, enum dma_data_direction dir,
>> + unsigned long attrs)
>> +{
>> + struct scatterlist *sg;
>> + int i;
>> +
>> + for_each_sg(sgl, sg, nents, i)
>> + __dma_page_dev_to_cpu(sg_dma_address(sg), sg_dma_len(sg), dir);
>> +}
>> +
>> +static void arm_nommu_dma_sync_single_for_device(struct device *dev,
>> + dma_addr_t handle, size_t size, enum dma_data_direction dir)
>> +{
>> + __dma_page_cpu_to_dev(handle, size, dir);
>> +}
>> +
>> +static void arm_nommu_dma_sync_single_for_cpu(struct device *dev,
>> + dma_addr_t handle, size_t size, enum dma_data_direction dir)
>> +{
>> + __dma_page_cpu_to_dev(handle, size, dir);
>> +}
>> +
>> +static void arm_nommu_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sgl,
>> + int nents, enum dma_data_direction dir)
>> +{
>> + struct scatterlist *sg;
>> + int i;
>> +
>> + for_each_sg(sgl, sg, nents, i)
>> + __dma_page_cpu_to_dev(sg_dma_address(sg), sg_dma_len(sg), dir);
>> +}
>> +
>> +static void arm_nommu_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sgl,
>> + int nents, enum dma_data_direction dir)
>> +{
>> + struct scatterlist *sg;
>> + int i;
>> +
>> + for_each_sg(sgl, sg, nents, i)
>> + __dma_page_dev_to_cpu(sg_dma_address(sg), sg_dma_len(sg), dir);
>> +}
>> +
>> +struct dma_map_ops arm_nommu_dma_ops = {
>> + .alloc = arm_nommu_dma_alloc,
>> + .free = arm_nommu_dma_free,
>> + .mmap = arm_nommu_dma_mmap,
>> + .map_page = arm_nommu_dma_map_page,
>> + .unmap_page = arm_nommu_dma_unmap_page,
>> + .map_sg = arm_nommu_dma_map_sg,
>> + .unmap_sg = arm_nommu_dma_unmap_sg,
>> + .sync_single_for_device = arm_nommu_dma_sync_single_for_device,
>> + .sync_single_for_cpu = arm_nommu_dma_sync_single_for_cpu,
>> + .sync_sg_for_device = arm_nommu_dma_sync_sg_for_device,
>> + .sync_sg_for_cpu = arm_nommu_dma_sync_sg_for_cpu,
>> +};
>> +EXPORT_SYMBOL(arm_nommu_dma_ops);
>> +
>> +static struct dma_map_ops *arm_nommu_get_dma_map_ops(bool coherent)
>> +{
>> + return coherent ? &dma_noop_ops : &arm_nommu_dma_ops;
>> +}
>> +
>> +void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
>> + const struct iommu_ops *iommu, bool coherent)
>> +{
>> + struct dma_map_ops *dma_ops;
>> +
>> + if (IS_ENABLED(CONFIG_CPU_V7M)) {
>> + /*
>> + * Cache support for v7m is optional, so can be treated as
>> + * coherent if no cache has been detected. Note that it is not
>> + * enough to check if MPU is in use or not since in absense of
>> + * MPU system memory map is used.
>> + */
>> + dev->archdata.dma_coherent = (cacheid) ? coherent : true;
>> + } else {
>> + /*
>> + * Assume coherent DMA in case MMU/MPU has not been set up.
>> + */
>> + dev->archdata.dma_coherent = (get_cr() & CR_M) ? coherent : true;
>> + }
>> +
>> + dma_ops = arm_nommu_get_dma_map_ops(dev->archdata.dma_coherent);
>> +
>> + set_dma_ops(dev, dma_ops);
>> +}
>> +
>> +void arch_teardown_dma_ops(struct device *dev)
>> +{
>> +}
>> +
>> +int dma_supported(struct device *dev, u64 mask)
>> +{
>> + return 1;
>> +}
>> +
>> +EXPORT_SYMBOL(dma_supported);
>> +
>> +#define PREALLOC_DMA_DEBUG_ENTRIES 4096
>> +
>> +static int __init dma_debug_do_init(void)
>> +{
>> + dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES);
>> + return 0;
>> +}
>> +core_initcall(dma_debug_do_init);
>>
>
>
^ permalink raw reply
* [PATCH v2 6/6] arm64: allwinner: a64: Increase the MMC max frequency
From: Maxime Ripard @ 2017-01-09 16:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.493dc67855a5f1837e875d37307319a03d14d1d0.1483980339.git-series.maxime.ripard@free-electrons.com>
All the controllers can have a maximum frequency of 200MHz.
Since older SoCs cannot go that high, we cannot change the default maximum
frequency, but fortunately for us we have a property for that in the DT.
This also has the side effect of allowing to use the MMC HS200 mode for the
boards that support it (with either 1.2v or 1.8v IOs).
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 3 +++
1 file changed, 3 insertions(+), 0 deletions(-)
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
index 8e149498e096..f46ae965cf5b 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -332,6 +332,7 @@
resets = <&ccu RST_BUS_MMC0>;
reset-names = "ahb";
interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
+ max-frequency = <200000000>;
status = "disabled";
#address-cells = <1>;
#size-cells = <0>;
@@ -345,6 +346,7 @@
resets = <&ccu RST_BUS_MMC1>;
reset-names = "ahb";
interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
+ max-frequency = <200000000>;
status = "disabled";
#address-cells = <1>;
#size-cells = <0>;
@@ -358,6 +360,7 @@
resets = <&ccu RST_BUS_MMC2>;
reset-names = "ahb";
interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
+ max-frequency = <200000000>;
status = "disabled";
#address-cells = <1>;
#size-cells = <0>;
--
git-series 0.8.11
^ permalink raw reply related
* [PATCH v2 5/6] arm64: allwinner: a64: Add MMC pinctrl nodes
From: Maxime Ripard @ 2017-01-09 16:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.493dc67855a5f1837e875d37307319a03d14d1d0.1483980339.git-series.maxime.ripard@free-electrons.com>
The A64 only has a single set of pins for each MMC controller. Since we
already have boards that require all of them, let's add them to the DTSI.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 25 ++++++++++++++++++++-
1 file changed, 25 insertions(+), 0 deletions(-)
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
index 143e9706438f..8e149498e096 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -205,6 +205,31 @@
function = "i2c1";
};
+ mmc0_pins: mmc0-pins {
+ pins = "PF0", "PF1", "PF2", "PF3",
+ "PF4", "PF5";
+ function = "mmc0";
+ drive-strength = <30>;
+ bias-pull-up;
+ };
+
+ mmc1_pins: mmc1-pins {
+ pins = "PG0", "PG1", "PG2", "PG3",
+ "PG4", "PG5";
+ function = "mmc1";
+ drive-strength = <30>;
+ bias-pull-up;
+ };
+
+ mmc2_pins: mmc2-pins {
+ pins = "PC1", "PC5", "PC6", "PC8", "PC9",
+ "PC10","PC11", "PC12", "PC13",
+ "PC14", "PC15", "PC16";
+ function = "mmc2";
+ drive-strength = <30>;
+ bias-pull-up;
+ };
+
uart0_pins_a: uart0 at 0 {
pins = "PB8", "PB9";
function = "uart0";
--
git-series 0.8.11
^ permalink raw reply related
* [PATCH v2 4/6] arm64: allwinner: a64: Add MMC nodes
From: Maxime Ripard @ 2017-01-09 16:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.493dc67855a5f1837e875d37307319a03d14d1d0.1483980339.git-series.maxime.ripard@free-electrons.com>
From: Andre Przywara <andre.przywara@arm.com>
The A64 has 3 MMC controllers, one of them being especially targeted to
eMMC. Among other things, it has a data strobe signal and a 8 bits data
width.
The two other are more usual controllers that will have a 4 bits width at
most and no data strobe signal, which limits it to more usual SD or MMC
peripherals.
Signed-off-by: Andre Przywara <andre.przywara@arm.com>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 39 ++++++++++++++++++++-
1 file changed, 39 insertions(+), 0 deletions(-)
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
index 99b6bb1e141c..143e9706438f 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -299,6 +299,45 @@
#size-cells = <0>;
};
+ mmc0: mmc at 1c0f000 {
+ compatible = "allwinner,sun50i-a64-mmc";
+ reg = <0x01c0f000 0x1000>;
+ clocks = <&ccu CLK_BUS_MMC0>, <&ccu CLK_MMC0>;
+ clock-names = "ahb", "mmc";
+ resets = <&ccu RST_BUS_MMC0>;
+ reset-names = "ahb";
+ interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ mmc1: mmc at 1c10000 {
+ compatible = "allwinner,sun50i-a64-mmc";
+ reg = <0x01c10000 0x1000>;
+ clocks = <&ccu CLK_BUS_MMC1>, <&ccu CLK_MMC1>;
+ clock-names = "ahb", "mmc";
+ resets = <&ccu RST_BUS_MMC1>;
+ reset-names = "ahb";
+ interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
+ mmc2: mmc at 1c11000 {
+ compatible = "allwinner,sun50i-a64-emmc";
+ reg = <0x01c11000 0x1000>;
+ clocks = <&ccu CLK_BUS_MMC2>, <&ccu CLK_MMC2>;
+ clock-names = "ahb", "mmc";
+ resets = <&ccu RST_BUS_MMC2>;
+ reset-names = "ahb";
+ interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ #address-cells = <1>;
+ #size-cells = <0>;
+ };
+
gic: interrupt-controller at 1c81000 {
compatible = "arm,gic-400";
reg = <0x01c81000 0x1000>,
--
git-series 0.8.11
^ permalink raw reply related
* [PATCHv3 3/8] rtc: add STM32 RTC driver
From: Amelie DELAUNAY @ 2017-01-09 16:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170105173301.GA27341@linaro.org>
Hi,
Thanks for reviewing!
Alexandre, could you please review this v3 and say when you plan to take
this driver in the cycle ?
Thanks,
Amelie
On 01/05/2017 06:33 PM, Mathieu Poirier wrote:
> On Thu, Jan 05, 2017 at 02:43:24PM +0100, Amelie Delaunay wrote:
>> This patch adds support for the STM32 RTC.
>>
>> Signed-off-by: Amelie Delaunay <amelie.delaunay@st.com>
>> ---
>> drivers/rtc/Kconfig | 11 +
>> drivers/rtc/Makefile | 1 +
>> drivers/rtc/rtc-stm32.c | 776 ++++++++++++++++++++++++++++++++++++++++++++++++
>> 3 files changed, 788 insertions(+)
>> create mode 100644 drivers/rtc/rtc-stm32.c
>>
>> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
>> index e859d14..11eb28a 100644
>> --- a/drivers/rtc/Kconfig
>> +++ b/drivers/rtc/Kconfig
>> @@ -1706,6 +1706,17 @@ config RTC_DRV_PIC32
>> This driver can also be built as a module. If so, the module
>> will be called rtc-pic32
>>
>> +config RTC_DRV_STM32
>> + tristate "STM32 RTC"
>> + select REGMAP_MMIO
>> + depends on ARCH_STM32 || COMPILE_TEST
>> + help
>> + If you say yes here you get support for the STM32 On-Chip
>> + Real Time Clock.
>> +
>> + This driver can also be built as a module, if so, the module
>> + will be called "rtc-stm32".
>> +
>> comment "HID Sensor RTC drivers"
>>
>> config RTC_DRV_HID_SENSOR_TIME
>> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
>> index 1ac694a..87bd9cc 100644
>> --- a/drivers/rtc/Makefile
>> +++ b/drivers/rtc/Makefile
>> @@ -144,6 +144,7 @@ obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o
>> obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o
>> obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
>> obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
>> +obj-$(CONFIG_RTC_DRV_STM32) += rtc-stm32.o
>> obj-$(CONFIG_RTC_DRV_STMP) += rtc-stmp3xxx.o
>> obj-$(CONFIG_RTC_DRV_ST_LPC) += rtc-st-lpc.o
>> obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o
>> diff --git a/drivers/rtc/rtc-stm32.c b/drivers/rtc/rtc-stm32.c
>> new file mode 100644
>> index 0000000..fdd3a31
>> --- /dev/null
>> +++ b/drivers/rtc/rtc-stm32.c
>> @@ -0,0 +1,776 @@
>> +/*
>> + * Copyright (C) Amelie Delaunay 2016
>> + * Author: Amelie Delaunay <amelie.delaunay@st.com>
>> + * License terms: GNU General Public License (GPL), version 2
>> + */
>> +
>> +#include <linux/bcd.h>
>> +#include <linux/clk.h>
>> +#include <linux/iopoll.h>
>> +#include <linux/ioport.h>
>> +#include <linux/mfd/syscon.h>
>> +#include <linux/module.h>
>> +#include <linux/of_device.h>
>> +#include <linux/regmap.h>
>> +#include <linux/rtc.h>
>> +#include <linux/spinlock.h>
>> +
>> +#define DRIVER_NAME "stm32_rtc"
>> +
>> +/* STM32 RTC registers */
>> +#define STM32_RTC_TR 0x00
>> +#define STM32_RTC_DR 0x04
>> +#define STM32_RTC_CR 0x08
>> +#define STM32_RTC_ISR 0x0C
>> +#define STM32_RTC_PRER 0x10
>> +#define STM32_RTC_ALRMAR 0x1C
>> +#define STM32_RTC_WPR 0x24
>> +
>> +/* STM32_RTC_TR bit fields */
>> +#define STM32_RTC_TR_SEC_SHIFT 0
>> +#define STM32_RTC_TR_SEC GENMASK(6, 0)
>> +#define STM32_RTC_TR_MIN_SHIFT 8
>> +#define STM32_RTC_TR_MIN GENMASK(14, 8)
>> +#define STM32_RTC_TR_HOUR_SHIFT 16
>> +#define STM32_RTC_TR_HOUR GENMASK(21, 16)
>> +
>> +/* STM32_RTC_DR bit fields */
>> +#define STM32_RTC_DR_DATE_SHIFT 0
>> +#define STM32_RTC_DR_DATE GENMASK(5, 0)
>> +#define STM32_RTC_DR_MONTH_SHIFT 8
>> +#define STM32_RTC_DR_MONTH GENMASK(12, 8)
>> +#define STM32_RTC_DR_WDAY_SHIFT 13
>> +#define STM32_RTC_DR_WDAY GENMASK(15, 13)
>> +#define STM32_RTC_DR_YEAR_SHIFT 16
>> +#define STM32_RTC_DR_YEAR GENMASK(23, 16)
>> +
>> +/* STM32_RTC_CR bit fields */
>> +#define STM32_RTC_CR_FMT BIT(6)
>> +#define STM32_RTC_CR_ALRAE BIT(8)
>> +#define STM32_RTC_CR_ALRAIE BIT(12)
>> +
>> +/* STM32_RTC_ISR bit fields */
>> +#define STM32_RTC_ISR_ALRAWF BIT(0)
>> +#define STM32_RTC_ISR_INITS BIT(4)
>> +#define STM32_RTC_ISR_RSF BIT(5)
>> +#define STM32_RTC_ISR_INITF BIT(6)
>> +#define STM32_RTC_ISR_INIT BIT(7)
>> +#define STM32_RTC_ISR_ALRAF BIT(8)
>> +
>> +/* STM32_RTC_PRER bit fields */
>> +#define STM32_RTC_PRER_PRED_S_SHIFT 0
>> +#define STM32_RTC_PRER_PRED_S GENMASK(14, 0)
>> +#define STM32_RTC_PRER_PRED_A_SHIFT 16
>> +#define STM32_RTC_PRER_PRED_A GENMASK(22, 16)
>> +
>> +/* STM32_RTC_ALRMAR and STM32_RTC_ALRMBR bit fields */
>> +#define STM32_RTC_ALRMXR_SEC_SHIFT 0
>> +#define STM32_RTC_ALRMXR_SEC GENMASK(6, 0)
>> +#define STM32_RTC_ALRMXR_SEC_MASK BIT(7)
>> +#define STM32_RTC_ALRMXR_MIN_SHIFT 8
>> +#define STM32_RTC_ALRMXR_MIN GENMASK(14, 8)
>> +#define STM32_RTC_ALRMXR_MIN_MASK BIT(15)
>> +#define STM32_RTC_ALRMXR_HOUR_SHIFT 16
>> +#define STM32_RTC_ALRMXR_HOUR GENMASK(21, 16)
>> +#define STM32_RTC_ALRMXR_PM BIT(22)
>> +#define STM32_RTC_ALRMXR_HOUR_MASK BIT(23)
>> +#define STM32_RTC_ALRMXR_DATE_SHIFT 24
>> +#define STM32_RTC_ALRMXR_DATE GENMASK(29, 24)
>> +#define STM32_RTC_ALRMXR_WDSEL BIT(30)
>> +#define STM32_RTC_ALRMXR_WDAY_SHIFT 24
>> +#define STM32_RTC_ALRMXR_WDAY GENMASK(27, 24)
>> +#define STM32_RTC_ALRMXR_DATE_MASK BIT(31)
>> +
>> +/* STM32_RTC_WPR key constants */
>> +#define RTC_WPR_1ST_KEY 0xCA
>> +#define RTC_WPR_2ND_KEY 0x53
>> +#define RTC_WPR_WRONG_KEY 0xFF
>> +
>> +/*
>> + * RTC registers are protected agains parasitic write access.
>> + * PWR_CR_DBP bit must be set to enable write access to RTC registers.
>> + */
>> +/* STM32_PWR_CR */
>> +#define PWR_CR 0x00
>> +/* STM32_PWR_CR bit field */
>> +#define PWR_CR_DBP BIT(8)
>> +
>> +static struct regmap *dbp;
>> +
>> +struct stm32_rtc {
>> + struct rtc_device *rtc_dev;
>> + void __iomem *base;
>> + struct clk *ck_rtc;
>> + spinlock_t lock; /* Protects registers accesses */
>> + int irq_alarm;
>> +};
>> +
>> +static void stm32_rtc_wpr_unlock(struct stm32_rtc *rtc)
>> +{
>> + writel_relaxed(RTC_WPR_1ST_KEY, rtc->base + STM32_RTC_WPR);
>> + writel_relaxed(RTC_WPR_2ND_KEY, rtc->base + STM32_RTC_WPR);
>> +}
>> +
>> +static void stm32_rtc_wpr_lock(struct stm32_rtc *rtc)
>> +{
>> + writel_relaxed(RTC_WPR_WRONG_KEY, rtc->base + STM32_RTC_WPR);
>> +}
>> +
>> +static int stm32_rtc_enter_init_mode(struct stm32_rtc *rtc)
>> +{
>> + unsigned int isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
>> +
>> + if (!(isr & STM32_RTC_ISR_INITF)) {
>> + isr |= STM32_RTC_ISR_INIT;
>> + writel_relaxed(isr, rtc->base + STM32_RTC_ISR);
>> +
>> + /*
>> + * It takes around 2 ck_rtc clock cycles to enter in
>> + * initialization phase mode (and have INITF flag set). As
>> + * slowest ck_rtc frequency may be 32kHz and highest should be
>> + * 1MHz, we poll every 10 us with a timeout of 100ms.
>> + */
>> + return readl_relaxed_poll_timeout_atomic(
>> + rtc->base + STM32_RTC_ISR,
>> + isr, (isr & STM32_RTC_ISR_INITF),
>> + 10, 100000);
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static void stm32_rtc_exit_init_mode(struct stm32_rtc *rtc)
>> +{
>> + unsigned int isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
>> +
>> + isr &= ~STM32_RTC_ISR_INIT;
>> + writel_relaxed(isr, rtc->base + STM32_RTC_ISR);
>> +}
>> +
>> +static int stm32_rtc_wait_sync(struct stm32_rtc *rtc)
>> +{
>> + unsigned int isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
>> +
>> + isr &= ~STM32_RTC_ISR_RSF;
>> + writel_relaxed(isr, rtc->base + STM32_RTC_ISR);
>> +
>> + /*
>> + * Wait for RSF to be set to ensure the calendar registers are
>> + * synchronised, it takes around 2 ck_rtc clock cycles
>> + */
>> + return readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR,
>> + isr,
>> + (isr & STM32_RTC_ISR_RSF),
>> + 10, 100000);
>> +}
>> +
>> +static irqreturn_t stm32_rtc_alarm_irq(int irq, void *dev_id)
>> +{
>> + struct stm32_rtc *rtc = (struct stm32_rtc *)dev_id;
>> + unsigned int isr, cr;
>> +
>> + mutex_lock(&rtc->rtc_dev->ops_lock);
>> +
>> + isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
>> + cr = readl_relaxed(rtc->base + STM32_RTC_CR);
>> +
>> + if ((isr & STM32_RTC_ISR_ALRAF) &&
>> + (cr & STM32_RTC_CR_ALRAIE)) {
>> + /* Alarm A flag - Alarm interrupt */
>> + dev_dbg(&rtc->rtc_dev->dev, "Alarm occurred\n");
>> +
>> + /* Pass event to the kernel */
>> + rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF);
>> +
>> + /* Clear event flag, otherwise new events won't be received */
>> + writel_relaxed(isr & ~STM32_RTC_ISR_ALRAF,
>> + rtc->base + STM32_RTC_ISR);
>> + }
>> +
>> + mutex_unlock(&rtc->rtc_dev->ops_lock);
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +/* Convert rtc_time structure from bin to bcd format */
>> +static void tm2bcd(struct rtc_time *tm)
>> +{
>> + tm->tm_sec = bin2bcd(tm->tm_sec);
>> + tm->tm_min = bin2bcd(tm->tm_min);
>> + tm->tm_hour = bin2bcd(tm->tm_hour);
>> +
>> + tm->tm_mday = bin2bcd(tm->tm_mday);
>> + tm->tm_mon = bin2bcd(tm->tm_mon + 1);
>> + tm->tm_year = bin2bcd(tm->tm_year - 100);
>> + /*
>> + * Number of days since Sunday
>> + * - on kernel side, 0=Sunday...6=Saturday
>> + * - on rtc side, 0=invalid,1=Monday...7=Sunday
>> + */
>> + tm->tm_wday = (!tm->tm_wday) ? 7 : tm->tm_wday;
>> +}
>> +
>> +/* Convert rtc_time structure from bcd to bin format */
>> +static void bcd2tm(struct rtc_time *tm)
>> +{
>> + tm->tm_sec = bcd2bin(tm->tm_sec);
>> + tm->tm_min = bcd2bin(tm->tm_min);
>> + tm->tm_hour = bcd2bin(tm->tm_hour);
>> +
>> + tm->tm_mday = bcd2bin(tm->tm_mday);
>> + tm->tm_mon = bcd2bin(tm->tm_mon) - 1;
>> + tm->tm_year = bcd2bin(tm->tm_year) + 100;
>> + /*
>> + * Number of days since Sunday
>> + * - on kernel side, 0=Sunday...6=Saturday
>> + * - on rtc side, 0=invalid,1=Monday...7=Sunday
>> + */
>> + tm->tm_wday %= 7;
>> +}
>> +
>> +static int stm32_rtc_read_time(struct device *dev, struct rtc_time *tm)
>> +{
>> + struct stm32_rtc *rtc = dev_get_drvdata(dev);
>> + unsigned int tr, dr;
>> + unsigned long irqflags;
>> +
>> + spin_lock_irqsave(&rtc->lock, irqflags);
>> +
>> + /* Time and Date in BCD format */
>> + tr = readl_relaxed(rtc->base + STM32_RTC_TR);
>> + dr = readl_relaxed(rtc->base + STM32_RTC_DR);
>> +
>> + spin_unlock_irqrestore(&rtc->lock, irqflags);
>> +
>> + tm->tm_sec = (tr & STM32_RTC_TR_SEC) >> STM32_RTC_TR_SEC_SHIFT;
>> + tm->tm_min = (tr & STM32_RTC_TR_MIN) >> STM32_RTC_TR_MIN_SHIFT;
>> + tm->tm_hour = (tr & STM32_RTC_TR_HOUR) >> STM32_RTC_TR_HOUR_SHIFT;
>> +
>> + tm->tm_mday = (dr & STM32_RTC_DR_DATE) >> STM32_RTC_DR_DATE_SHIFT;
>> + tm->tm_mon = (dr & STM32_RTC_DR_MONTH) >> STM32_RTC_DR_MONTH_SHIFT;
>> + tm->tm_year = (dr & STM32_RTC_DR_YEAR) >> STM32_RTC_DR_YEAR_SHIFT;
>> + tm->tm_wday = (dr & STM32_RTC_DR_WDAY) >> STM32_RTC_DR_WDAY_SHIFT;
>> +
>> + /* We don't report tm_yday and tm_isdst */
>> +
>> + bcd2tm(tm);
>> +
>> + if (rtc_valid_tm(tm) < 0) {
>> + dev_err(dev, "%s: rtc_time is not valid.\n", __func__);
>> + return -EINVAL;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int stm32_rtc_set_time(struct device *dev, struct rtc_time *tm)
>> +{
>> + struct stm32_rtc *rtc = dev_get_drvdata(dev);
>> + unsigned int tr, dr;
>> + unsigned long irqflags;
>> + int ret = 0;
>> +
>> + if (rtc_valid_tm(tm) < 0) {
>> + dev_err(dev, "%s: rtc_time is not valid.\n", __func__);
>> + return -EINVAL;
>> + }
>> +
>> + tm2bcd(tm);
>> +
>> + /* Time in BCD format */
>> + tr = ((tm->tm_sec << STM32_RTC_TR_SEC_SHIFT) & STM32_RTC_TR_SEC) |
>> + ((tm->tm_min << STM32_RTC_TR_MIN_SHIFT) & STM32_RTC_TR_MIN) |
>> + ((tm->tm_hour << STM32_RTC_TR_HOUR_SHIFT) & STM32_RTC_TR_HOUR);
>> +
>> + /* Date in BCD format */
>> + dr = ((tm->tm_mday << STM32_RTC_DR_DATE_SHIFT) & STM32_RTC_DR_DATE) |
>> + ((tm->tm_mon << STM32_RTC_DR_MONTH_SHIFT) & STM32_RTC_DR_MONTH) |
>> + ((tm->tm_year << STM32_RTC_DR_YEAR_SHIFT) & STM32_RTC_DR_YEAR) |
>> + ((tm->tm_wday << STM32_RTC_DR_WDAY_SHIFT) & STM32_RTC_DR_WDAY);
>> +
>> + spin_lock_irqsave(&rtc->lock, irqflags);
>> +
>> + stm32_rtc_wpr_unlock(rtc);
>> +
>> + ret = stm32_rtc_enter_init_mode(rtc);
>> + if (ret) {
>> + dev_err(dev, "Can't enter in init mode. Set time aborted.\n");
>> + goto end;
>> + }
>> +
>> + writel_relaxed(tr, rtc->base + STM32_RTC_TR);
>> + writel_relaxed(dr, rtc->base + STM32_RTC_DR);
>> +
>> + stm32_rtc_exit_init_mode(rtc);
>> +
>> + ret = stm32_rtc_wait_sync(rtc);
>> +end:
>> + stm32_rtc_wpr_lock(rtc);
>> +
>> + spin_unlock_irqrestore(&rtc->lock, irqflags);
>> +
>> + return ret;
>> +}
>> +
>> +static int stm32_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
>> +{
>> + struct stm32_rtc *rtc = dev_get_drvdata(dev);
>> + struct rtc_time *tm = &alrm->time;
>> + unsigned int alrmar, cr, isr;
>> + unsigned long irqflags;
>> +
>> + spin_lock_irqsave(&rtc->lock, irqflags);
>> +
>> + alrmar = readl_relaxed(rtc->base + STM32_RTC_ALRMAR);
>> + cr = readl_relaxed(rtc->base + STM32_RTC_CR);
>> + isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
>> +
>> + spin_unlock_irqrestore(&rtc->lock, irqflags);
>> +
>> + if (alrmar & STM32_RTC_ALRMXR_DATE_MASK) {
>> + /*
>> + * Date/day doesn't matter in Alarm comparison so alarm
>> + * triggers every day
>> + */
>> + tm->tm_mday = -1;
>> + tm->tm_wday = -1;
>> + } else {
>> + if (alrmar & STM32_RTC_ALRMXR_WDSEL) {
>> + /* Alarm is set to a day of week */
>> + tm->tm_mday = -1;
>> + tm->tm_wday = (alrmar & STM32_RTC_ALRMXR_WDAY) >>
>> + STM32_RTC_ALRMXR_WDAY_SHIFT;
>> + tm->tm_wday %= 7;
>> + } else {
>> + /* Alarm is set to a day of month */
>> + tm->tm_wday = -1;
>> + tm->tm_mday = (alrmar & STM32_RTC_ALRMXR_DATE) >>
>> + STM32_RTC_ALRMXR_DATE_SHIFT;
>> + }
>> + }
>> +
>> + if (alrmar & STM32_RTC_ALRMXR_HOUR_MASK) {
>> + /* Hours don't matter in Alarm comparison */
>> + tm->tm_hour = -1;
>> + } else {
>> + tm->tm_hour = (alrmar & STM32_RTC_ALRMXR_HOUR) >>
>> + STM32_RTC_ALRMXR_HOUR_SHIFT;
>> + if (alrmar & STM32_RTC_ALRMXR_PM)
>> + tm->tm_hour += 12;
>> + }
>> +
>> + if (alrmar & STM32_RTC_ALRMXR_MIN_MASK) {
>> + /* Minutes don't matter in Alarm comparison */
>> + tm->tm_min = -1;
>> + } else {
>> + tm->tm_min = (alrmar & STM32_RTC_ALRMXR_MIN) >>
>> + STM32_RTC_ALRMXR_MIN_SHIFT;
>> + }
>> +
>> + if (alrmar & STM32_RTC_ALRMXR_SEC_MASK) {
>> + /* Seconds don't matter in Alarm comparison */
>> + tm->tm_sec = -1;
>> + } else {
>> + tm->tm_sec = (alrmar & STM32_RTC_ALRMXR_SEC) >>
>> + STM32_RTC_ALRMXR_SEC_SHIFT;
>> + }
>> +
>> + bcd2tm(tm);
>> +
>> + alrm->enabled = (cr & STM32_RTC_CR_ALRAE) ? 1 : 0;
>> + alrm->pending = (isr & STM32_RTC_ISR_ALRAF) ? 1 : 0;
>> +
>> + return 0;
>> +}
>> +
>> +static int stm32_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
>> +{
>> + struct stm32_rtc *rtc = dev_get_drvdata(dev);
>> + unsigned long irqflags;
>> + unsigned int isr, cr;
>> +
>> + spin_lock_irqsave(&rtc->lock, irqflags);
>> +
>> + cr = readl_relaxed(rtc->base + STM32_RTC_CR);
>> +
>> + stm32_rtc_wpr_unlock(rtc);
>> +
>> + /* We expose Alarm A to the kernel */
>> + if (enabled)
>> + cr |= (STM32_RTC_CR_ALRAIE | STM32_RTC_CR_ALRAE);
>> + else
>> + cr &= ~(STM32_RTC_CR_ALRAIE | STM32_RTC_CR_ALRAE);
>> + writel_relaxed(cr, rtc->base + STM32_RTC_CR);
>> +
>> + /* Clear event irqflags, otherwise new events won't be received */
>> + isr = readl_relaxed(rtc->base + STM32_RTC_ISR);
>> + isr &= ~STM32_RTC_ISR_ALRAF;
>> + writel_relaxed(isr, rtc->base + STM32_RTC_ISR);
>> +
>> + stm32_rtc_wpr_lock(rtc);
>> +
>> + spin_unlock_irqrestore(&rtc->lock, irqflags);
>> +
>> + return 0;
>> +}
>> +
>> +static int stm32_rtc_valid_alrm(struct stm32_rtc *rtc, struct rtc_time *tm)
>> +{
>> + unsigned int cur_day, cur_mon, cur_year, cur_hour, cur_min, cur_sec;
>> + unsigned int dr = readl_relaxed(rtc->base + STM32_RTC_DR);
>> + unsigned int tr = readl_relaxed(rtc->base + STM32_RTC_TR);
>> +
>> + cur_day = (dr & STM32_RTC_DR_DATE) >> STM32_RTC_DR_DATE_SHIFT;
>> + cur_mon = (dr & STM32_RTC_DR_MONTH) >> STM32_RTC_DR_MONTH_SHIFT;
>> + cur_year = (dr & STM32_RTC_DR_YEAR) >> STM32_RTC_DR_YEAR_SHIFT;
>> + cur_sec = (tr & STM32_RTC_TR_SEC) >> STM32_RTC_TR_SEC_SHIFT;
>> + cur_min = (tr & STM32_RTC_TR_MIN) >> STM32_RTC_TR_MIN_SHIFT;
>> + cur_hour = (tr & STM32_RTC_TR_HOUR) >> STM32_RTC_TR_HOUR_SHIFT;
>> +
>> + /*
>> + * Assuming current date is M-D-Y H:M:S.
>> + * RTC alarm can't be set on a specific month and year.
>> + * So the valid alarm range is:
>> + * M-D-Y H:M:S < alarm <= (M+1)-D-Y H:M:S
>> + * with a specific case for December...
>> + */
>> + if ((((tm->tm_year > cur_year) &&
>> + (tm->tm_mon == 0x1) && (cur_mon == 0x12)) ||
>> + ((tm->tm_year == cur_year) &&
>> + (tm->tm_mon <= cur_mon + 1))) &&
>> + ((tm->tm_mday < cur_day) ||
>> + ((tm->tm_mday == cur_day) &&
>> + ((tm->tm_hour < cur_hour) ||
>> + ((tm->tm_hour == cur_hour) && (tm->tm_min < cur_min)) ||
>> + ((tm->tm_hour == cur_hour) && (tm->tm_min == cur_min) &&
>> + (tm->tm_sec <= cur_sec))))))
>> + return 0;
>> +
>> + return -EINVAL;
>> +}
>> +
>> +static int stm32_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
>> +{
>> + struct stm32_rtc *rtc = dev_get_drvdata(dev);
>> + struct rtc_time *tm = &alrm->time;
>> + unsigned long irqflags;
>> + unsigned int cr, isr, alrmar;
>> + int ret = 0;
>> +
>> + if (rtc_valid_tm(tm)) {
>> + dev_err(dev, "Alarm time not valid.\n");
>> + return -EINVAL;
>> + }
>> +
>> + tm2bcd(tm);
>> +
>> + /*
>> + * RTC alarm can't be set on a specific date, unless this date is
>> + * up to the same day of month next month.
>> + */
>> + if (stm32_rtc_valid_alrm(rtc, tm) < 0) {
>> + dev_err(dev, "Alarm can be set only on upcoming month.\n");
>> + return -EINVAL;
>> + }
>> +
>> + alrmar = 0;
>> + /* tm_year and tm_mon are not used because not supported by RTC */
>> + alrmar |= (tm->tm_mday << STM32_RTC_ALRMXR_DATE_SHIFT) &
>> + STM32_RTC_ALRMXR_DATE;
>> + /* 24-hour format */
>> + alrmar &= ~STM32_RTC_ALRMXR_PM;
>> + alrmar |= (tm->tm_hour << STM32_RTC_ALRMXR_HOUR_SHIFT) &
>> + STM32_RTC_ALRMXR_HOUR;
>> + alrmar |= (tm->tm_min << STM32_RTC_ALRMXR_MIN_SHIFT) &
>> + STM32_RTC_ALRMXR_MIN;
>> + alrmar |= (tm->tm_sec << STM32_RTC_ALRMXR_SEC_SHIFT) &
>> + STM32_RTC_ALRMXR_SEC;
>> +
>> + spin_lock_irqsave(&rtc->lock, irqflags);
>> +
>> + stm32_rtc_wpr_unlock(rtc);
>> +
>> + /* Disable Alarm */
>> + cr = readl_relaxed(rtc->base + STM32_RTC_CR);
>> + cr &= ~STM32_RTC_CR_ALRAE;
>> + writel_relaxed(cr, rtc->base + STM32_RTC_CR);
>> +
>> + /*
>> + * Poll Alarm write flag to be sure that Alarm update is allowed: it
>> + * takes around 2 ck_rtc clock cycles
>> + */
>> + ret = readl_relaxed_poll_timeout_atomic(rtc->base + STM32_RTC_ISR,
>> + isr,
>> + (isr & STM32_RTC_ISR_ALRAWF),
>> + 10, 100000);
>> +
>> + if (ret) {
>> + dev_err(dev, "Alarm update not allowed\n");
>> + goto end;
>> + }
>> +
>> + /* Write to Alarm register */
>> + writel_relaxed(alrmar, rtc->base + STM32_RTC_ALRMAR);
>> +
>> + if (alrm->enabled)
>> + stm32_rtc_alarm_irq_enable(dev, 1);
>> + else
>> + stm32_rtc_alarm_irq_enable(dev, 0);
>> +
>> +end:
>> + stm32_rtc_wpr_lock(rtc);
>> +
>> + spin_unlock_irqrestore(&rtc->lock, irqflags);
>> +
>> + return ret;
>> +}
>> +
>> +static const struct rtc_class_ops stm32_rtc_ops = {
>> + .read_time = stm32_rtc_read_time,
>> + .set_time = stm32_rtc_set_time,
>> + .read_alarm = stm32_rtc_read_alarm,
>> + .set_alarm = stm32_rtc_set_alarm,
>> + .alarm_irq_enable = stm32_rtc_alarm_irq_enable,
>> +};
>> +
>> +#ifdef CONFIG_OF
>> +static const struct of_device_id stm32_rtc_of_match[] = {
>> + { .compatible = "st,stm32-rtc" },
>> + {}
>> +};
>> +MODULE_DEVICE_TABLE(of, stm32_rtc_of_match);
>> +#endif
>> +
>> +static int stm32_rtc_init(struct platform_device *pdev,
>> + struct stm32_rtc *rtc)
>> +{
>> + unsigned int prer, pred_a, pred_s, pred_a_max, pred_s_max, cr;
>> + unsigned int rate;
>> + unsigned long irqflags;
>> + int ret = 0;
>> +
>> + rate = clk_get_rate(rtc->ck_rtc);
>> +
>> + /* Find prediv_a and prediv_s to obtain the 1Hz calendar clock */
>> + pred_a_max = STM32_RTC_PRER_PRED_A >> STM32_RTC_PRER_PRED_A_SHIFT;
>> + pred_s_max = STM32_RTC_PRER_PRED_S >> STM32_RTC_PRER_PRED_S_SHIFT;
>> +
>> + for (pred_a = pred_a_max; pred_a >= 0; pred_a--) {
>> + pred_s = (rate / (pred_a + 1)) - 1;
>> +
>> + if (((pred_s + 1) * (pred_a + 1)) == rate)
>> + break;
>> + }
>> +
>> + /*
>> + * Can't find a 1Hz, so give priority to RTC power consumption
>> + * by choosing the higher possible value for prediv_a
>> + */
>> + if ((pred_s > pred_s_max) || (pred_a > pred_a_max)) {
>> + pred_a = pred_a_max;
>> + pred_s = (rate / (pred_a + 1)) - 1;
>> +
>> + dev_warn(&pdev->dev, "ck_rtc is %s\n",
>> + (rate - ((pred_a + 1) * (pred_s + 1)) < 0) ?
>> + "fast" : "slow");
>> + }
>> +
>> + spin_lock_irqsave(&rtc->lock, irqflags);
>> +
>> + stm32_rtc_wpr_unlock(rtc);
>> +
>> + ret = stm32_rtc_enter_init_mode(rtc);
>> + if (ret) {
>> + dev_err(&pdev->dev,
>> + "Can't enter in init mode. Prescaler config failed.\n");
>> + goto end;
>> + }
>> +
>> + prer = (pred_s << STM32_RTC_PRER_PRED_S_SHIFT) & STM32_RTC_PRER_PRED_S;
>> + writel_relaxed(prer, rtc->base + STM32_RTC_PRER);
>> + prer |= (pred_a << STM32_RTC_PRER_PRED_A_SHIFT) & STM32_RTC_PRER_PRED_A;
>> + writel_relaxed(prer, rtc->base + STM32_RTC_PRER);
>> +
>> + /* Force 24h time format */
>> + cr = readl_relaxed(rtc->base + STM32_RTC_CR);
>> + cr &= ~STM32_RTC_CR_FMT;
>> + writel_relaxed(cr, rtc->base + STM32_RTC_CR);
>> +
>> + stm32_rtc_exit_init_mode(rtc);
>> +
>> + ret = stm32_rtc_wait_sync(rtc);
>> +end:
>> + stm32_rtc_wpr_lock(rtc);
>> +
>> + spin_unlock_irqrestore(&rtc->lock, irqflags);
>> +
>> + return ret;
>> +}
>> +
>> +static int stm32_rtc_probe(struct platform_device *pdev)
>> +{
>> + struct stm32_rtc *rtc;
>> + struct resource *res;
>> + int ret;
>> +
>> + rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
>> + if (!rtc)
>> + return -ENOMEM;
>> +
>> + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> + rtc->base = devm_ioremap_resource(&pdev->dev, res);
>> + if (IS_ERR(rtc->base))
>> + return PTR_ERR(rtc->base);
>> +
>> + dbp = syscon_regmap_lookup_by_phandle(pdev->dev.of_node, "st,syscfg");
>> + if (IS_ERR(dbp)) {
>> + dev_err(&pdev->dev, "no st,syscfg\n");
>> + return PTR_ERR(dbp);
>> + }
>> +
>> + spin_lock_init(&rtc->lock);
>> +
>> + rtc->ck_rtc = devm_clk_get(&pdev->dev, NULL);
>> + if (IS_ERR(rtc->ck_rtc)) {
>> + dev_err(&pdev->dev, "no ck_rtc clock");
>> + return PTR_ERR(rtc->ck_rtc);
>> + }
>> +
>> + ret = clk_prepare_enable(rtc->ck_rtc);
>> + if (ret)
>> + return ret;
>> +
>> + regmap_update_bits(dbp, PWR_CR, PWR_CR_DBP, PWR_CR_DBP);
>> +
>> + /*
>> + * After a system reset, RTC_ISR.INITS flag can be read to check if
>> + * the calendar has been initalized or not. INITS flag is reset by a
>> + * power-on reset (no vbat, no power-supply). It is not reset if
>> + * ck_rtc parent clock has changed (so RTC prescalers need to be
>> + * changed). That's why we cannot rely on this flag to know if RTC
>> + * init has to be done.
>> + */
>> + ret = stm32_rtc_init(pdev, rtc);
>> + if (ret)
>> + goto err;
>> +
>> + rtc->irq_alarm = platform_get_irq(pdev, 0);
>> + if (rtc->irq_alarm <= 0) {
>> + dev_err(&pdev->dev, "no alarm irq\n");
>> + ret = rtc->irq_alarm;
>> + goto err;
>> + }
>> +
>> + platform_set_drvdata(pdev, rtc);
>> +
>> + ret = device_init_wakeup(&pdev->dev, true);
>> + if (ret)
>> + dev_warn(&pdev->dev,
>> + "alarm won't be able to wake up the system");
>> +
>> + rtc->rtc_dev = devm_rtc_device_register(&pdev->dev, pdev->name,
>> + &stm32_rtc_ops, THIS_MODULE);
>> + if (IS_ERR(rtc->rtc_dev)) {
>> + ret = PTR_ERR(rtc->rtc_dev);
>> + dev_err(&pdev->dev, "rtc device registration failed, err=%d\n",
>> + ret);
>> + goto err;
>> + }
>> +
>> + /* Handle RTC alarm interrupts */
>> + ret = devm_request_threaded_irq(&pdev->dev, rtc->irq_alarm, NULL,
>> + stm32_rtc_alarm_irq,
>> + IRQF_TRIGGER_RISING | IRQF_ONESHOT,
>> + pdev->name, rtc);
>> + if (ret) {
>> + dev_err(&pdev->dev, "IRQ%d (alarm interrupt) already claimed\n",
>> + rtc->irq_alarm);
>> + goto err;
>> + }
>> +
>> + /*
>> + * If INITS flag is reset (calendar year field set to 0x00), calendar
>> + * must be initialized
>> + */
>> + if (!(readl_relaxed(rtc->base + STM32_RTC_ISR) & STM32_RTC_ISR_INITS))
>> + dev_warn(&pdev->dev, "Date/Time must be initialized\n");
>> +
>> + return 0;
>> +err:
>> + clk_disable_unprepare(rtc->ck_rtc);
>> +
>> + regmap_update_bits(dbp, PWR_CR, PWR_CR_DBP, ~PWR_CR_DBP);
>> +
>> + device_init_wakeup(&pdev->dev, false);
>> +
>> + return ret;
>> +}
>> +
>> +static int __exit stm32_rtc_remove(struct platform_device *pdev)
>> +{
>> + struct stm32_rtc *rtc = platform_get_drvdata(pdev);
>> + unsigned int cr;
>> +
>> + /* Disable interrupts */
>> + stm32_rtc_wpr_unlock(rtc);
>> + cr = readl_relaxed(rtc->base + STM32_RTC_CR);
>> + cr &= ~STM32_RTC_CR_ALRAIE;
>> + writel_relaxed(cr, rtc->base + STM32_RTC_CR);
>> + stm32_rtc_wpr_lock(rtc);
>> +
>> + clk_disable_unprepare(rtc->ck_rtc);
>> +
>> + /* Enable backup domain write protection */
>> + regmap_update_bits(dbp, PWR_CR, PWR_CR_DBP, ~PWR_CR_DBP);
>> +
>> + device_init_wakeup(&pdev->dev, false);
>> +
>> + return 0;
>> +}
>> +
>> +#ifdef CONFIG_PM_SLEEP
>> +static int stm32_rtc_suspend(struct device *dev)
>> +{
>> + struct stm32_rtc *rtc = dev_get_drvdata(dev);
>> +
>> + if (device_may_wakeup(dev))
>> + return enable_irq_wake(rtc->irq_alarm);
>> +
>> + return 0;
>> +}
>> +
>> +static int stm32_rtc_resume(struct device *dev)
>> +{
>> + struct stm32_rtc *rtc = dev_get_drvdata(dev);
>> + int ret = 0;
>> +
>> + ret = stm32_rtc_wait_sync(rtc);
>> + if (ret < 0)
>> + return ret;
>> +
>> + if (device_may_wakeup(dev))
>> + return disable_irq_wake(rtc->irq_alarm);
>> +
>> + return ret;
>> +}
>> +#endif
>> +
>> +static SIMPLE_DEV_PM_OPS(stm32_rtc_pm_ops,
>> + stm32_rtc_suspend, stm32_rtc_resume);
>> +
>> +static struct platform_driver stm32_rtc_driver = {
>> + .probe = stm32_rtc_probe,
>> + .remove = stm32_rtc_remove,
>> + .driver = {
>> + .name = DRIVER_NAME,
>> + .pm = &stm32_rtc_pm_ops,
>> + .of_match_table = stm32_rtc_of_match,
>> + },
>> +};
>> +
>> +module_platform_driver(stm32_rtc_driver);
>> +
>> +MODULE_ALIAS("platform:" DRIVER_NAME);
>> +MODULE_AUTHOR("Amelie Delaunay <amelie.delaunay@st.com>");
>> +MODULE_DESCRIPTION("STMicroelectronics STM32 Real Time Clock driver");
>> +MODULE_LICENSE("GPL v2");
>
> Looks much better now.
>
> Reviewed-by: Mathieu Poirier <mathieu.poirier@linaro.org>
>
>> --
>> 1.9.1
>>
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-kernel at lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
^ permalink raw reply
* [PATCH v2 3/6] mmc: sunxi: Add EMMC (MMC2) controller compatible
From: Maxime Ripard @ 2017-01-09 16:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.493dc67855a5f1837e875d37307319a03d14d1d0.1483980339.git-series.maxime.ripard@free-electrons.com>
The MMC2 controller on the A64 is kind of a special beast.
While the general controller design is the same than the other MMC
controllers in the SoC, it also has a bunch of features and changes that
prevent it to be driven in the same way.
It has for example a different bus width limit, a different maximum
frequency, and, for some reason, the maximum buffer size of a DMA
descriptor.
Add a new compatible specifically for this controller.
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
drivers/mmc/host/sunxi-mmc.c | 7 +++++++
1 file changed, 7 insertions(+), 0 deletions(-)
diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
index 9a860bcac154..bdcc87c9d8b8 100644
--- a/drivers/mmc/host/sunxi-mmc.c
+++ b/drivers/mmc/host/sunxi-mmc.c
@@ -1081,12 +1081,19 @@ static const struct sunxi_mmc_cfg sun50i_a64_cfg = {
.needs_new_timings = true,
};
+static const struct sunxi_mmc_cfg sun50i_a64_emmc_cfg = {
+ .idma_des_size_bits = 13,
+ .clk_delays = NULL,
+ .can_calibrate = true,
+};
+
static const struct of_device_id sunxi_mmc_of_match[] = {
{ .compatible = "allwinner,sun4i-a10-mmc", .data = &sun4i_a10_cfg },
{ .compatible = "allwinner,sun5i-a13-mmc", .data = &sun5i_a13_cfg },
{ .compatible = "allwinner,sun7i-a20-mmc", .data = &sun7i_a20_cfg },
{ .compatible = "allwinner,sun9i-a80-mmc", .data = &sun9i_a80_cfg },
{ .compatible = "allwinner,sun50i-a64-mmc", .data = &sun50i_a64_cfg },
+ { .compatible = "allwinner,sun50i-a64-emmc", .data = &sun50i_a64_emmc_cfg },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sunxi_mmc_of_match);
--
git-series 0.8.11
^ permalink raw reply related
* [PATCH v2 2/6] mmc: sunxi: Enable the new timings for the A64 MMC controllers
From: Maxime Ripard @ 2017-01-09 16:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.493dc67855a5f1837e875d37307319a03d14d1d0.1483980339.git-series.maxime.ripard@free-electrons.com>
The A64 MMC controllers need to set a "new timings" bit when a new rate is
set.
The actual meaning of that bit is not clear yet, but not setting it leads
to some corner-case issues, like the CMD53 failing, which is used to
implement SDIO packet aggregation.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
drivers/mmc/host/sunxi-mmc.c | 6 ++++++
1 file changed, 6 insertions(+), 0 deletions(-)
diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
index ea9552a0d820..9a860bcac154 100644
--- a/drivers/mmc/host/sunxi-mmc.c
+++ b/drivers/mmc/host/sunxi-mmc.c
@@ -253,6 +253,8 @@ struct sunxi_mmc_cfg {
/* does the IP block support autocalibration? */
bool can_calibrate;
+
+ bool needs_new_timings;
};
struct sunxi_mmc_host {
@@ -776,6 +778,9 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
}
mmc_writel(host, REG_CLKCR, rval);
+ if (host->cfg->needs_new_timings)
+ mmc_writel(host, REG_SD_NTSR, SDXC_2X_TIMING_MODE);
+
ret = sunxi_mmc_clk_set_phase(host, ios, rate);
if (ret)
return ret;
@@ -1073,6 +1078,7 @@ static const struct sunxi_mmc_cfg sun50i_a64_cfg = {
.idma_des_size_bits = 16,
.clk_delays = NULL,
.can_calibrate = true,
+ .needs_new_timings = true,
};
static const struct of_device_id sunxi_mmc_of_match[] = {
--
git-series 0.8.11
^ permalink raw reply related
* [PATCH v2 1/6] mmc: sunxi: Always set signal delay to 0 for A64
From: Maxime Ripard @ 2017-01-09 16:46 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <cover.493dc67855a5f1837e875d37307319a03d14d1d0.1483980339.git-series.maxime.ripard@free-electrons.com>
Experience have shown that the using the autocalibration could severely
degrade the performances of the MMC bus.
Allwinner is using in its BSP a delay set to 0 for all the modes but HS400.
Remove the calibration code for now, and add comments to document our
findings.
Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
drivers/mmc/host/sunxi-mmc.c | 50 ++++++++++++-------------------------
1 file changed, 17 insertions(+), 33 deletions(-)
diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c
index b1d1303389a7..ea9552a0d820 100644
--- a/drivers/mmc/host/sunxi-mmc.c
+++ b/drivers/mmc/host/sunxi-mmc.c
@@ -683,41 +683,19 @@ static int sunxi_mmc_oclk_onoff(struct sunxi_mmc_host *host, u32 oclk_en)
static int sunxi_mmc_calibrate(struct sunxi_mmc_host *host, int reg_off)
{
- u32 reg = readl(host->reg_base + reg_off);
- u32 delay;
- unsigned long timeout;
-
if (!host->cfg->can_calibrate)
return 0;
- reg &= ~(SDXC_CAL_DL_MASK << SDXC_CAL_DL_SW_SHIFT);
- reg &= ~SDXC_CAL_DL_SW_EN;
-
- writel(reg | SDXC_CAL_START, host->reg_base + reg_off);
-
- dev_dbg(mmc_dev(host->mmc), "calibration started\n");
-
- timeout = jiffies + HZ * SDXC_CAL_TIMEOUT;
-
- while (!((reg = readl(host->reg_base + reg_off)) & SDXC_CAL_DONE)) {
- if (time_before(jiffies, timeout))
- cpu_relax();
- else {
- reg &= ~SDXC_CAL_START;
- writel(reg, host->reg_base + reg_off);
-
- return -ETIMEDOUT;
- }
- }
-
- delay = (reg >> SDXC_CAL_DL_SHIFT) & SDXC_CAL_DL_MASK;
-
- reg &= ~SDXC_CAL_START;
- reg |= (delay << SDXC_CAL_DL_SW_SHIFT) | SDXC_CAL_DL_SW_EN;
-
- writel(reg, host->reg_base + reg_off);
-
- dev_dbg(mmc_dev(host->mmc), "calibration ended, reg is 0x%x\n", reg);
+ /*
+ * FIXME:
+ * This is not clear how the calibration is supposed to work
+ * yet. The best rate have been obtained by simply setting the
+ * delay to 0, as Allwinner does in its BSP.
+ *
+ * The only mode that doesn't have such a delay is HS400, that
+ * is in itself a TODO.
+ */
+ writel(SDXC_CAL_DL_SW_EN, host->reg_base + reg_off);
return 0;
}
@@ -806,7 +784,13 @@ static int sunxi_mmc_clk_set_rate(struct sunxi_mmc_host *host,
if (ret)
return ret;
- /* TODO: enable calibrate on sdc2 SDXC_REG_DS_DL_REG of A64 */
+ /*
+ * FIXME:
+ *
+ * In HS400 we'll also need to calibrate the data strobe
+ * signal. This should only happen on the MMC2 controller (at
+ * least on the A64 and older SoCs).
+ */
return sunxi_mmc_oclk_onoff(host, 1);
}
--
git-series 0.8.11
^ permalink raw reply related
* [PATCH v2 0/6] arm64: allwinner: a64: Enable MMC support
From: Maxime Ripard @ 2017-01-09 16:46 UTC (permalink / raw)
To: linux-arm-kernel
Hi,
Here is a second attempt at getting the MMC controllers running, the first
having been done by Andre.
This has been tested on a board with one SDIO device (a Marvell WiFi chip)
and a Kingston eMMC with 1.8V IOs.
For SDIO, the HS DDR mode works just fine. There's a little bit of work to
get to SDR104, but that will come eventually.
For the eMMC, HS200 with the voltage switch works. HS400 doesn't at the
moment, but since it's significantly more complex, and at the same time
Allwinner recommends to limit its frequency to 100MHz, this doesn't have
any benefits. If there's any at some point, this can be added later.
Let me know what you think,
Maxime
Andre Przywara (1):
arm64: allwinner: a64: Add MMC nodes
Maxime Ripard (5):
mmc: sunxi: Always set signal delay to 0 for A64
mmc: sunxi: Enable the new timings for the A64 MMC controllers
mmc: sunxi: Add EMMC (MMC2) controller compatible
arm64: allwinner: a64: Add MMC pinctrl nodes
arm64: allwinner: a64: Increase the MMC max frequency
arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi | 67 ++++++++++++++++++++-
drivers/mmc/host/sunxi-mmc.c | 63 +++++++++----------
2 files changed, 97 insertions(+), 33 deletions(-)
base-commit: f9ca9b952ee139fbb9cd4d354a33f440bc1049cd
--
git-series 0.8.11
^ permalink raw reply
* [RFC PATCH v3 3/5] ARM: NOMMU: Introduce dma operations for noMMU
From: Robin Murphy @ 2017-01-09 16:43 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483969669-4636-4-git-send-email-vladimir.murzin@arm.com>
Hi Vladimir,
On 09/01/17 13:47, Vladimir Murzin wrote:
> R/M classes of cpus can have memory covered by MPU which in turn might
> configure RAM as Normal i.e. bufferable and cacheable. It breaks
> dma_alloc_coherent() and friends, since data can stuck in caches now
> or be buffered.
>
> This patch factors out DMA support for NOMMU configuration into
> separate entity which provides dedicated dma_ops. We have to handle
> there several cases:
> - configurations with MMU/MPU setup
> - configurations without MMU/MPU setup
> - special case for M-class, since caches and MPU there are optional
>
> In general we rely on default DMA area for coherent allocations or/and
> per-device memory reserves suitable for coherent DMA, so if such
> regions are set coherent allocations go from there.
>
> In case MPU/MPU was not setup we fallback to normal page allocator for
> DMA memory allocation.
>
> In case we run M-class cpus, for configuration without cache support
> (like Cortex-M3/M4) dma operations are forced to be coherent and wired
> with dma-noop (such decision is made based on cacheid global
> variable); however, if caches are detected there and no DMA coherent
> region is given (either default or per-device), dma is disallowed even
> MPU is not set - it is because M-class implement system memory map
> which defines part of address space as Normal memory.
>
> Reported-by: Alexandre Torgue <alexandre.torgue@st.com>
> Reported-by: Andras Szemzo <sza@esh.hu>
> Signed-off-by: Vladimir Murzin <vladimir.murzin@arm.com>
> ---
> arch/arm/include/asm/dma-mapping.h | 3 +-
> arch/arm/mm/Makefile | 5 +-
> arch/arm/mm/dma-mapping-nommu.c | 252 +++++++++++++++++++++++++++++++++++++
> 3 files changed, 256 insertions(+), 4 deletions(-)
> create mode 100644 arch/arm/mm/dma-mapping-nommu.c
>
> diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h
> index bf02dbd..559faad 100644
> --- a/arch/arm/include/asm/dma-mapping.h
> +++ b/arch/arm/include/asm/dma-mapping.h
> @@ -20,7 +20,8 @@ static inline struct dma_map_ops *__generic_dma_ops(struct device *dev)
> {
> if (dev && dev->archdata.dma_ops)
> return dev->archdata.dma_ops;
> - return &arm_dma_ops;
> +
> + return IS_ENABLED(CONFIG_MMU) ? &arm_dma_ops : &dma_noop_ops;
> }
>
> static inline struct dma_map_ops *get_dma_ops(struct device *dev)
> diff --git a/arch/arm/mm/Makefile b/arch/arm/mm/Makefile
> index 2ac7988..5796357 100644
> --- a/arch/arm/mm/Makefile
> +++ b/arch/arm/mm/Makefile
> @@ -2,9 +2,8 @@
> # Makefile for the linux arm-specific parts of the memory manager.
> #
>
> -obj-y := dma-mapping.o extable.o fault.o init.o \
> - iomap.o
> -
> +obj-y := extable.o fault.o init.o iomap.o
> +obj-y += dma-mapping$(MMUEXT).o
> obj-$(CONFIG_MMU) += fault-armv.o flush.o idmap.o ioremap.o \
> mmap.o pgd.o mmu.o pageattr.o
>
> diff --git a/arch/arm/mm/dma-mapping-nommu.c b/arch/arm/mm/dma-mapping-nommu.c
> new file mode 100644
> index 0000000..a5c50fb
> --- /dev/null
> +++ b/arch/arm/mm/dma-mapping-nommu.c
> @@ -0,0 +1,252 @@
> +/*
> + * Based on linux/arch/arm/mm/dma-mapping.c
> + *
> + * Copyright (C) 2000-2004 Russell King
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + *
> + */
> +
> +#include <linux/export.h>
> +#include <linux/mm.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/scatterlist.h>
> +
> +#include <asm/cachetype.h>
> +#include <asm/cacheflush.h>
> +#include <asm/outercache.h>
> +#include <asm/cp15.h>
> +
> +#include "dma.h"
> +
> +/*
> + * dma_noop_ops is used if
> + * - MMU/MPU is off
> + * - cpu is v7m w/o cache support
> + * - device is coherent
> + * otherwise arm_nommu_dma_ops is used.
> + *
> + * arm_nommu_dma_ops rely on consistent DMA memory (please, refer to
> + * [1] on how to declare such memory).
> + *
> + * [1] Documentation/devicetree/bindings/reserved-memory/reserved-memory.txt
> + */
> +
> +static void *arm_nommu_dma_alloc(struct device *dev, size_t size,
> + dma_addr_t *dma_handle, gfp_t gfp,
> + unsigned long attrs)
> +
> +{
> + struct dma_map_ops *ops = &dma_noop_ops;
> +
> + /*
> + * We are here because:
> + * - no consistent DMA region has been defined, so we can't
> + * continue.
> + * - there is no space left in consistent DMA region, so we
> + * only can fallback to generic allocator if we are
> + * advertised that consistency is not required.
> + */
> +
> + if (attrs & DMA_ATTR_NON_CONSISTENT)
> + return ops->alloc(dev, size, dma_handle, gfp, attrs);
> +
> + WARN_ON_ONCE(1);
> + return NULL;
> +}
> +
> +static void arm_nommu_dma_free(struct device *dev, size_t size,
> + void *cpu_addr, dma_addr_t dma_addr,
> + unsigned long attrs)
> +{
> + struct dma_map_ops *ops = &dma_noop_ops;
> +
> + if (attrs & DMA_ATTR_NON_CONSISTENT)
> + ops->free(dev, size, cpu_addr, dma_addr, attrs);
> +
> + WARN_ON_ONCE(1);
> + return;
> +}
> +
> +static int arm_nommu_dma_mmap(struct device *dev, struct vm_area_struct *vma,
> + void *cpu_addr, dma_addr_t dma_addr, size_t size,
> + unsigned long attrs)
> +{
> + struct dma_map_ops *ops = &dma_noop_ops;
> + int ret;
> +
> + if (dma_mmap_from_coherent(dev, vma, cpu_addr, size, &ret))
> + return ret;
> +
> + if (attrs & DMA_ATTR_NON_CONSISTENT)
> + return ops->mmap(dev, vma, cpu_addr, dma_addr, size, attrs);
> +
> + WARN_ON_ONCE(1);
> + return -ENXIO;
> +}
> +
> +static void __dma_page_cpu_to_dev(dma_addr_t handle, size_t size,
> + enum dma_data_direction dir)
> +{
> + dmac_unmap_area(__va(handle), size, dir);
> +
> + if (dir == DMA_FROM_DEVICE)
> + outer_inv_range(handle, handle + size);
> + else
> + outer_clean_range(handle, handle + size);
> +}
> +
> +static void __dma_page_dev_to_cpu(dma_addr_t handle, size_t size,
> + enum dma_data_direction dir)
> +{
> + if (dir != DMA_TO_DEVICE) {
> + outer_inv_range(handle, handle + size);
> + dmac_unmap_area(__va(handle), size, dir);
> + }
> +}
Nit: I appreciate that the situation here makes it OK by construction,
but CPU cache maintenance on a DMA address just looks *so* wrong :)
Could we pass either the "virtual" or physical version of the address as
the argument to these helpers so that the code looks less crazy at a glance?
Robin.
> +static dma_addr_t arm_nommu_dma_map_page(struct device *dev, struct page *page,
> + unsigned long offset, size_t size,
> + enum dma_data_direction dir,
> + unsigned long attrs)
> +{
> + dma_addr_t handle = page_to_phys(page) + offset;
> +
> + __dma_page_cpu_to_dev(handle, size, dir);
> +
> + return handle;
> +}
> +
> +static void arm_nommu_dma_unmap_page(struct device *dev, dma_addr_t handle,
> + size_t size, enum dma_data_direction dir,
> + unsigned long attrs)
> +{
> + __dma_page_dev_to_cpu(handle, size, dir);
> +}
> +
> +
> +static int arm_nommu_dma_map_sg(struct device *dev, struct scatterlist *sgl,
> + int nents, enum dma_data_direction dir,
> + unsigned long attrs)
> +{
> + int i;
> + struct scatterlist *sg;
> +
> + for_each_sg(sgl, sg, nents, i) {
> + sg_dma_address(sg) = sg_phys(sg);
> + sg_dma_len(sg) = sg->length;
> + __dma_page_cpu_to_dev(sg_dma_address(sg), sg_dma_len(sg), dir);
> + }
> +
> + return nents;
> +}
> +
> +static void arm_nommu_dma_unmap_sg(struct device *dev, struct scatterlist *sgl,
> + int nents, enum dma_data_direction dir,
> + unsigned long attrs)
> +{
> + struct scatterlist *sg;
> + int i;
> +
> + for_each_sg(sgl, sg, nents, i)
> + __dma_page_dev_to_cpu(sg_dma_address(sg), sg_dma_len(sg), dir);
> +}
> +
> +static void arm_nommu_dma_sync_single_for_device(struct device *dev,
> + dma_addr_t handle, size_t size, enum dma_data_direction dir)
> +{
> + __dma_page_cpu_to_dev(handle, size, dir);
> +}
> +
> +static void arm_nommu_dma_sync_single_for_cpu(struct device *dev,
> + dma_addr_t handle, size_t size, enum dma_data_direction dir)
> +{
> + __dma_page_cpu_to_dev(handle, size, dir);
> +}
> +
> +static void arm_nommu_dma_sync_sg_for_device(struct device *dev, struct scatterlist *sgl,
> + int nents, enum dma_data_direction dir)
> +{
> + struct scatterlist *sg;
> + int i;
> +
> + for_each_sg(sgl, sg, nents, i)
> + __dma_page_cpu_to_dev(sg_dma_address(sg), sg_dma_len(sg), dir);
> +}
> +
> +static void arm_nommu_dma_sync_sg_for_cpu(struct device *dev, struct scatterlist *sgl,
> + int nents, enum dma_data_direction dir)
> +{
> + struct scatterlist *sg;
> + int i;
> +
> + for_each_sg(sgl, sg, nents, i)
> + __dma_page_dev_to_cpu(sg_dma_address(sg), sg_dma_len(sg), dir);
> +}
> +
> +struct dma_map_ops arm_nommu_dma_ops = {
> + .alloc = arm_nommu_dma_alloc,
> + .free = arm_nommu_dma_free,
> + .mmap = arm_nommu_dma_mmap,
> + .map_page = arm_nommu_dma_map_page,
> + .unmap_page = arm_nommu_dma_unmap_page,
> + .map_sg = arm_nommu_dma_map_sg,
> + .unmap_sg = arm_nommu_dma_unmap_sg,
> + .sync_single_for_device = arm_nommu_dma_sync_single_for_device,
> + .sync_single_for_cpu = arm_nommu_dma_sync_single_for_cpu,
> + .sync_sg_for_device = arm_nommu_dma_sync_sg_for_device,
> + .sync_sg_for_cpu = arm_nommu_dma_sync_sg_for_cpu,
> +};
> +EXPORT_SYMBOL(arm_nommu_dma_ops);
> +
> +static struct dma_map_ops *arm_nommu_get_dma_map_ops(bool coherent)
> +{
> + return coherent ? &dma_noop_ops : &arm_nommu_dma_ops;
> +}
> +
> +void arch_setup_dma_ops(struct device *dev, u64 dma_base, u64 size,
> + const struct iommu_ops *iommu, bool coherent)
> +{
> + struct dma_map_ops *dma_ops;
> +
> + if (IS_ENABLED(CONFIG_CPU_V7M)) {
> + /*
> + * Cache support for v7m is optional, so can be treated as
> + * coherent if no cache has been detected. Note that it is not
> + * enough to check if MPU is in use or not since in absense of
> + * MPU system memory map is used.
> + */
> + dev->archdata.dma_coherent = (cacheid) ? coherent : true;
> + } else {
> + /*
> + * Assume coherent DMA in case MMU/MPU has not been set up.
> + */
> + dev->archdata.dma_coherent = (get_cr() & CR_M) ? coherent : true;
> + }
> +
> + dma_ops = arm_nommu_get_dma_map_ops(dev->archdata.dma_coherent);
> +
> + set_dma_ops(dev, dma_ops);
> +}
> +
> +void arch_teardown_dma_ops(struct device *dev)
> +{
> +}
> +
> +int dma_supported(struct device *dev, u64 mask)
> +{
> + return 1;
> +}
> +
> +EXPORT_SYMBOL(dma_supported);
> +
> +#define PREALLOC_DMA_DEBUG_ENTRIES 4096
> +
> +static int __init dma_debug_do_init(void)
> +{
> + dma_debug_init(PREALLOC_DMA_DEBUG_ENTRIES);
> + return 0;
> +}
> +core_initcall(dma_debug_do_init);
>
^ permalink raw reply
* [PATCH v4 1/9] clk: stm32f4: Update DT bindings documentation
From: Alexandre Torgue @ 2017-01-09 16:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161222001051.GP8288@codeaurora.org>
Hi Stephen,
On 12/22/2016 01:10 AM, Stephen Boyd wrote:
> On 12/13, gabriel.fernandez at st.com wrote:
>> From: Gabriel Fernandez <gabriel.fernandez@st.com>
>>
>> Creation of dt include file for specific stm32f4 clocks.
>> These specific clocks are not derived from system clock (SYSCLOCK)
>> We should use index 1 to use these clocks in DT.
>> e.g. <&rcc 1 CLK_LSI>
>>
>> Signed-off-by: Gabriel Fernandez <gabriel.fernandez@st.com>
>> Acked-by: Rob Herring <robh@kernel.org>
>> ---
>
> Applied to clk-stm32f4 and merged into clk-next.
>
I'm preparing pull request branch for STM32 DT part. This patch is also
requested to build correctly DT patches. Do you know how could we
synchronize our pull request ?
Thanks
Alex
^ permalink raw reply
* [PATCH v4 3/3] drm: zte: add overlay plane support
From: Sean Paul @ 2017-01-09 16:27 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1483972544-12731-4-git-send-email-shawnguo@kernel.org>
On Mon, Jan 9, 2017 at 9:35 AM, Shawn Guo <shawnguo@kernel.org> wrote:
> From: Shawn Guo <shawn.guo@linaro.org>
>
> It enables VOU VL (Video Layer) to support overlay plane with scaling
> function. VL0 has some quirks on scaling support. We choose to skip it
> and only adds VL1 and VL2 into DRM core for now.
>
> Function zx_plane_atomic_disable() gets moved around with no changes to
> save a forward declaration.
>
> Signed-off-by: Shawn Guo <shawn.guo@linaro.org>
> ---
> drivers/gpu/drm/zte/zx_plane.c | 301 +++++++++++++++++++++++++++++++++---
> drivers/gpu/drm/zte/zx_plane.h | 1 +
> drivers/gpu/drm/zte/zx_plane_regs.h | 51 ++++++
> drivers/gpu/drm/zte/zx_vou.c | 84 +++++++++-
> drivers/gpu/drm/zte/zx_vou_regs.h | 18 +++
> 5 files changed, 426 insertions(+), 29 deletions(-)
>
> diff --git a/drivers/gpu/drm/zte/zx_plane.c b/drivers/gpu/drm/zte/zx_plane.c
> index 5445eebf830f..24426c2b4b8f 100644
<snip>
> diff --git a/drivers/gpu/drm/zte/zx_vou.c b/drivers/gpu/drm/zte/zx_vou.c
> index 3fb4fc04e693..8e7edda184d0 100644
> --- a/drivers/gpu/drm/zte/zx_vou.c
> +++ b/drivers/gpu/drm/zte/zx_vou.c
> @@ -112,6 +112,22 @@ struct vou_layer_bits {
> },
> };
>
> +static const struct vou_layer_bits zx_vl_bits[VL_NUM] = {
> + {
> + .enable = OSD_CTRL0_VL0_EN,
> + .chnsel = OSD_CTRL0_VL0_SEL,
> + .clksel = VOU_CLK_VL0_SEL,
> + }, {
> + .enable = OSD_CTRL0_VL1_EN,
> + .chnsel = OSD_CTRL0_VL1_SEL,
> + .clksel = VOU_CLK_VL1_SEL,
> + }, {
> + .enable = OSD_CTRL0_VL2_EN,
> + .chnsel = OSD_CTRL0_VL2_SEL,
> + .clksel = VOU_CLK_VL2_SEL,
> + },
> +};
> +
> struct zx_vou_hw {
> struct device *dev;
> void __iomem *osd;
> @@ -125,6 +141,7 @@ struct zx_vou_hw {
> struct clk *aux_clk;
> struct zx_crtc *main_crtc;
> struct zx_crtc *aux_crtc;
> + struct drm_plane *overlays[VL_NUM];
> };
>
> static inline struct zx_vou_hw *crtc_to_vou(struct drm_crtc *crtc)
> @@ -439,6 +456,8 @@ void zx_vou_layer_enable(struct drm_plane *plane)
> }
>
> zx_writel_mask(vou->osd + OSD_CTRL0, bits->enable, bits->enable);
> +
> + zplane->enabled = true;
> }
>
> void zx_vou_layer_disable(struct drm_plane *plane)
> @@ -449,6 +468,57 @@ void zx_vou_layer_disable(struct drm_plane *plane)
> const struct vou_layer_bits *bits = zplane->bits;
>
> zx_writel_mask(vou->osd + OSD_CTRL0, bits->enable, 0);
> +
> + zplane->enabled = false;
> +}
> +
> +static void zx_overlay_init(struct drm_device *drm, struct zx_vou_hw *vou)
> +{
> + struct device *dev = vou->dev;
> + struct zx_plane *zplane;
> + int i;
> + int ret;
> +
> + /*
> + * VL0 has some quirks on scaling support which need special handling.
> + * Let's leave it out for now.
> + */
> + for (i = 1; i < VL_NUM; i++) {
> + zplane = devm_kzalloc(dev, sizeof(*zplane), GFP_KERNEL);
> + if (!zplane) {
> + DRM_DEV_ERROR(dev, "failed to allocate zplane %d\n", i);
> + return;
> + }
> +
> + zplane->layer = vou->osd + OSD_VL_OFFSET(i);
> + zplane->hbsc = vou->osd + HBSC_VL_OFFSET(i);
> + zplane->rsz = vou->otfppu + RSZ_VL_OFFSET(i);
> + zplane->bits = &zx_vl_bits[i];
> +
> + ret = zx_plane_init(drm, zplane, DRM_PLANE_TYPE_OVERLAY);
> + if (ret) {
> + DRM_DEV_ERROR(dev, "failed to init overlay %d\n", i);
> + continue;
> + }
> +
> + vou->overlays[i] = &zplane->plane;
> + }
> +}
> +
> +static inline void zx_osd_int_update(struct zx_crtc *zcrtc)
> +{
> + struct zx_vou_hw *vou = zcrtc->vou;
> + int i;
> +
> + vou_chn_set_update(zcrtc);
> + zx_plane_set_update(zcrtc->primary);
> +
> + for (i = 0; i < VL_NUM; i++) {
> + struct drm_plane *overlay = vou->overlays[i];
> +
> + if (overlay)
> + zx_plane_set_update(overlay);
> + }
Hi Shawn,
Thanks so much for revving this patch, it's looking really good. I
just have one (1.5, really) suggestion.
I don't think we need to keep vou->overlays around. You should be able
to loop through all the planes registered with drm core and use
crtc->state->plane_mask to determine which are active for a given crtc
(this would also encapsulate the zcrtc->primary update above).
I think you can also use if (plane->state->crtc) as your
enable/disable check in zx_plane_set_update() and eliminate the new
enabled flag. I fully realize this was my suggestion, and I apologize
for the churn. I'll try not to do reviews past midnight again :-)
Sean
> }
>
> static irqreturn_t vou_irq_handler(int irq, void *dev_id)
> @@ -470,15 +540,11 @@ static irqreturn_t vou_irq_handler(int irq, void *dev_id)
> state = zx_readl(vou->osd + OSD_INT_STA);
> zx_writel(vou->osd + OSD_INT_CLRSTA, state);
>
> - if (state & OSD_INT_MAIN_UPT) {
> - vou_chn_set_update(vou->main_crtc);
> - zx_plane_set_update(vou->main_crtc->primary);
> - }
> + if (state & OSD_INT_MAIN_UPT)
> + zx_osd_int_update(vou->main_crtc);
>
> - if (state & OSD_INT_AUX_UPT) {
> - vou_chn_set_update(vou->aux_crtc);
> - zx_plane_set_update(vou->aux_crtc->primary);
> - }
> + if (state & OSD_INT_AUX_UPT)
> + zx_osd_int_update(vou->aux_crtc);
>
> if (state & OSD_INT_ERROR)
> DRM_DEV_ERROR(vou->dev, "OSD ERROR: 0x%08x!\n", state);
> @@ -648,6 +714,8 @@ static int zx_crtc_bind(struct device *dev, struct device *master, void *data)
> goto disable_ppu_clk;
> }
>
> + zx_overlay_init(drm, vou);
> +
> return 0;
>
> disable_ppu_clk:
> diff --git a/drivers/gpu/drm/zte/zx_vou_regs.h b/drivers/gpu/drm/zte/zx_vou_regs.h
> index f44e7a4ae441..193c1ce01fe7 100644
> --- a/drivers/gpu/drm/zte/zx_vou_regs.h
> +++ b/drivers/gpu/drm/zte/zx_vou_regs.h
> @@ -22,6 +22,15 @@
> #define AUX_HBSC_OFFSET 0x860
> #define AUX_RSZ_OFFSET 0x800
>
> +#define OSD_VL0_OFFSET 0x040
> +#define OSD_VL_OFFSET(i) (OSD_VL0_OFFSET + 0x050 * (i))
> +
> +#define HBSC_VL0_OFFSET 0x760
> +#define HBSC_VL_OFFSET(i) (HBSC_VL0_OFFSET + 0x040 * (i))
> +
> +#define RSZ_VL1_U0 0xa00
> +#define RSZ_VL_OFFSET(i) (RSZ_VL1_U0 + 0x200 * (i))
> +
> /* OSD (GPC_GLOBAL) registers */
> #define OSD_INT_STA 0x04
> #define OSD_INT_CLRSTA 0x08
> @@ -42,6 +51,12 @@
> )
> #define OSD_INT_ENABLE (OSD_INT_ERROR | OSD_INT_AUX_UPT | OSD_INT_MAIN_UPT)
> #define OSD_CTRL0 0x10
> +#define OSD_CTRL0_VL0_EN BIT(13)
> +#define OSD_CTRL0_VL0_SEL BIT(12)
> +#define OSD_CTRL0_VL1_EN BIT(11)
> +#define OSD_CTRL0_VL1_SEL BIT(10)
> +#define OSD_CTRL0_VL2_EN BIT(9)
> +#define OSD_CTRL0_VL2_SEL BIT(8)
> #define OSD_CTRL0_GL0_EN BIT(7)
> #define OSD_CTRL0_GL0_SEL BIT(6)
> #define OSD_CTRL0_GL1_EN BIT(5)
> @@ -146,6 +161,9 @@
> #define VOU_INF_DATA_SEL 0x08
> #define VOU_SOFT_RST 0x14
> #define VOU_CLK_SEL 0x18
> +#define VOU_CLK_VL2_SEL BIT(8)
> +#define VOU_CLK_VL1_SEL BIT(7)
> +#define VOU_CLK_VL0_SEL BIT(6)
> #define VOU_CLK_GL1_SEL BIT(5)
> #define VOU_CLK_GL0_SEL BIT(4)
> #define VOU_CLK_REQEN 0x20
> --
> 1.9.1
>
--
Sean Paul, Software Engineer, Google / Chromium OS
^ permalink raw reply
* [PATCH v1.1] ARM: multi_v7_defconfig: Enable power sequence for Odroid U3
From: Javier Martinez Canillas @ 2017-01-09 16:24 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170107091651.10560-1-krzk@kernel.org>
Hello Krzysztof,
I think it would had been clearer if the subject prefix was "[PATCH v1.1 4/4]" :)
On 01/07/2017 06:16 AM, Krzysztof Kozlowski wrote:
> Odroid U3 needs a power sequence for lan9730, if it was enabled by
> bootloader. Also enable the USB3503 HSCI to USB2.0 driver (device
> is present on Odroid U3).
>
> Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
>
> ---
>
Do you think that makes sense to also enable GPIO_SYS for debugging
purposes as you do in patch 3/4?
In any case the patch looks good to me:
Reviewed-by: Javier Martinez Canillas <javier@osg.samsung.com>
Best regards,
--
Javier Martinez Canillas
Open Source Group
Samsung Research America
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox