* [PATCH v1.1] ARM: multi_v7_defconfig: Enable power sequence for Odroid U3
From: Krzysztof Kozlowski @ 2017-01-09 17:38 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <d8ee24b1-9d6b-e76f-b5ef-71706efbf9e5@osg.samsung.com>
On Mon, Jan 9, 2017 at 6:24 PM, Javier Martinez Canillas
<javier@osg.samsung.com> wrote:
> Hello Krzysztof,
>
> I think it would had been clearer if the subject prefix was "[PATCH v1.1 4/4]" :)
Ah, yes.
>
> 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?
I like the GPIO_SYSFS interface because it is easy to use. However
now, after your question, I found this:
http://lists.infradead.org/pipermail/linux-arm-kernel/2016-November/470154.html
ARM/ARM64: defconfig: drop GPIO_SYSFS on multiplatforms
... so instead I will drop it from exynos_defconfig.
Best regards,
Krzysztof
^ permalink raw reply
* APM smmu implementation
From: Feng Kan @ 2017-01-09 17:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20170109105421.GA21398@arm.com>
On Mon, Jan 9, 2017 at 2:54 AM, Will Deacon <will.deacon@arm.com> wrote:
> On Fri, Jan 06, 2017 at 03:21:34PM -0800, Feng Kan wrote:
>> The APM IOMMU implementation is mostly just the ARM SMMU 500 variant.
>> However, our internal bus is only 42 bits wide. Our IAS field is coded
>> as 48 bits, which cause IPA to truncated to 42 bits on the physical
>> bus. In order for our system to work with the arm-smmu.c, there needs
>> to be a way to force the ipa_size to 42. The current internal solution
>> is to use the cpuid, but that is quite ugly. I was thinking of using
>> the model
>
> Just so I understand, what are the UBS and OAS values on your part?
UBS/OAS/IAS are all default to 5.
>
>> as indication to right the ipa_size, but I am not too sure of the ACPI
>> side. Would it be okay to add an APM MMU500 variant? I would also
>> appreciated it if you guys have any alternate solutions.
>
> For this sort of implementation erratum, I think using the model field
> is the right thing to do. However, you'll need to get your model registered
> with IORT, so I've added Charles to cc since he maintains that document.
>
> Also, please add a device-tree property to override the IAS once you
> start writing patches.
>
> Will
^ permalink raw reply
* [PATCH net-next v4 2/2] net: stmmac: dwmac-meson8b: make the RGMII TX delay configurable
From: Martin Blumenstingl @ 2017-01-09 17:37 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAFBinCDZ_mqt8q2rG6zDy8ke5sX6L+4sVYOOhWUdO3apwFiRYw@mail.gmail.com>
Hi David,
On Sun, Dec 18, 2016 at 5:13 PM, Martin Blumenstingl
<martin.blumenstingl@googlemail.com> wrote:
> On Sun, Dec 18, 2016 at 4:49 PM, David Miller <davem@davemloft.net> wrote:
>> From: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
>> Date: Sat, 17 Dec 2016 19:21:19 +0100
>>
>>> Prior to this patch we were using a hardcoded RGMII TX clock delay of
>>> 2ns (= 1/4 cycle of the 125MHz RGMII TX clock). This value works for
>>> many boards, but unfortunately not for all (due to the way the actual
>>> circuit is designed, sometimes because the TX delay is enabled in the
>>> PHY, etc.). Making the TX delay on the MAC side configurable allows us
>>> to support all possible hardware combinations.
>>>
>>> This allows fixing a compatibility issue on some boards, where the
>>> RTL8211F PHY is configured to generate the TX delay. We can now turn
>>> off the TX delay in the MAC, because otherwise we would be applying the
>>> delay twice (which results in non-working TX traffic).
>>>
>>> Signed-off-by: Martin Blumenstingl <martin.blumenstingl@googlemail.com>
>>> Tested-by: Neil Armstrong <narmstrong@baylibre.com>
>>
>> Is this really the safest thing to do?
>>
>> If you say the existing hard-coded setting of 1/4 cycle works on most
>> boards, and what you're trying to do is override it with an OF
>> property value for boards where the existing setting does not work,
>> then you _must_ use a default value that corresponds to what the
>> existing code does not when you don't see this new OF property.
> it's a bit more complicated in reality: 1/4 cycle works when the TX
> delay of the RTL8211F PHY is turned off (until recently it was always
> enabled for phy-mode RGMII).
>
>> So please retain the current behavior of the 1/4 cycle TX delay
>> setting when you don't see the amlogic,tx-delay-ns property.
>>
>> I really think you risk breaking existing boards by not doing so,
>> unless you can have this patch tested on every such board that exists
>> and I don't think you really can feasibly and rigorously do that.
> there's a patch in my follow-up series which adds the 2ns to the .dts
> for all RGMII based boards: [0] (and I would keep these even if we had
> a default value, just to make it explicit and thus easier to
> understand for other people).
> however, we can add the 2ns default back (I can do this if you want -
> Rob Herring was unhappy with the missing documentation of this default
> value [1] - so note to myself: take care of that as well). but then we
> have to decide when to apply this default value: only when we're in
> RGMII mode or also in any of the RGMII_*ID modes?
>
> please let me know how we should proceed
gentle ping - what is your opinion on this?
Regards,
Martin
^ permalink raw reply
* [PATCH v4 9/9] arm64: Documentation - Expose CPU feature registers
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>
Documentation for the infrastructure to expose CPU feature
register by emulating MRS.
Cc: Catalin Marinas <catalin.marinas@arm.com>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Dave Martin <dave.martin@arm.com>
Reviewed-by: Catalin Marinas <catalin.marinas@arm.com>
Signed-off-by: Suzuki K Poulose <suzuki.poulose@arm.com>
---
Changes since V3:
- Mark RES0 fields as hidden and remove the RAZ registers from the
exposed list, as RES0 fields are hidden.
- Removed obsolete references
Changes since V2:
- Include the sample program in the documentation
---
Documentation/arm64/cpu-feature-registers.txt | 240 ++++++++++++++++++++++++++
arch/arm64/kernel/cpufeature.c | 4 +
2 files changed, 244 insertions(+)
create mode 100644 Documentation/arm64/cpu-feature-registers.txt
diff --git a/Documentation/arm64/cpu-feature-registers.txt b/Documentation/arm64/cpu-feature-registers.txt
new file mode 100644
index 0000000..61ca21e
--- /dev/null
+++ b/Documentation/arm64/cpu-feature-registers.txt
@@ -0,0 +1,240 @@
+ ARM64 CPU Feature Registers
+ ===========================
+
+Author: Suzuki K Poulose <suzuki.poulose@arm.com>
+
+
+This file describes the ABI for exporting the AArch64 CPU ID/feature
+registers to userspace. The availability of this ABI is advertised
+via the HWCAP_CPUID in HWCAPs.
+
+1. Motivation
+---------------
+
+The ARM architecture defines a set of feature registers, which describe
+the capabilities of the CPU/system. Access to these system registers is
+restricted from EL0 and there is no reliable way for an application to
+extract this information to make better decisions at runtime. There is
+limited information available to the application via HWCAPs, however
+there are some issues with their usage.
+
+ a) Any change to the HWCAPs requires an update to userspace (e.g libc)
+ to detect the new changes, which can take a long time to appear in
+ distributions. Exposing the registers allows applications to get the
+ information without requiring updates to the toolchains.
+
+ b) Access to HWCAPs is sometimes limited (e.g prior to libc, or
+ when ld is initialised at startup time).
+
+ c) HWCAPs cannot represent non-boolean information effectively. The
+ architecture defines a canonical format for representing features
+ in the ID registers; this is well defined and is capable of
+ representing all valid architecture variations.
+
+
+2. Requirements
+-----------------
+
+ a) Safety :
+ Applications should be able to use the information provided by the
+ infrastructure to run safely across the system. This has greater
+ implications on a system with heterogeneous CPUs.
+ The infrastructure exports a value that is safe across all the
+ available CPU on the system.
+
+ e.g, If at least one CPU doesn't implement CRC32 instructions, while
+ others do, we should report that the CRC32 is not implemented.
+ Otherwise an application could crash when scheduled on the CPU
+ which doesn't support CRC32.
+
+ b) Security :
+ Applications should only be able to receive information that is
+ relevant to the normal operation in userspace. Hence, some of the
+ fields are masked out(i.e, made invisible) and their values are set to
+ indicate the feature is 'not supported'. See Section 4 for the list
+ of visible features. Also, the kernel may manipulate the fields
+ based on what it supports. e.g, If FP is not supported by the
+ kernel, the values could indicate that the FP is not available
+ (even when the CPU provides it).
+
+ c) Implementation Defined Features
+ The infrastructure doesn't expose any register which is
+ IMPLEMENTATION DEFINED as per ARMv8-A Architecture.
+
+ d) CPU Identification :
+ MIDR_EL1 is exposed to help identify the processor. On a
+ heterogeneous system, this could be racy (just like getcpu()). The
+ process could be migrated to another CPU by the time it uses the
+ register value, unless the CPU affinity is set. Hence, there is no
+ guarantee that the value reflects the processor that it is
+ currently executing on. The REVIDR is not exposed due to this
+ constraint, as REVIDR makes sense only in conjunction with the
+ MIDR. Alternately, MIDR_EL1 and REVIDR_EL1 are exposed via sysfs
+ at:
+
+ /sys/devices/system/cpu/cpu$ID/regs/identification/
+ \- midr
+ \- revidr
+
+3. Implementation
+--------------------
+
+The infrastructure is built on the emulation of the 'MRS' instruction.
+Accessing a restricted system register from an application generates an
+exception and ends up in SIGILL being delivered to the process.
+The infrastructure hooks into the exception handler and emulates the
+operation if the source belongs to the supported system register space.
+
+The infrastructure emulates only the following system register space:
+ Op0=3, Op1=0, CRn=0, CRm=0,4,5,6,7
+
+(See Table C5-6 'System instruction encodings for non-Debug System
+register accesses' in ARMv8 ARM DDI 0487A.h, for the list of
+registers).
+
+The following rules are applied to the value returned by the
+infrastructure:
+
+ a) The value of an 'IMPLEMENTATION DEFINED' field is set to 0.
+ b) The value of a reserved field is populated with the reserved
+ value as defined by the architecture.
+ c) The value of a 'visible' field holds the system wide safe value
+ for the particular feature (except for MIDR_EL1, see section 4).
+ d) All other fields (i.e, invisible fields) are set to indicate
+ the feature is missing (as defined by the architecture).
+
+4. List of registers with visible features
+-------------------------------------------
+
+ 1) ID_AA64ISAR0_EL1 - Instruction Set Attribute Register 0
+ x--------------------------------------------------x
+ | Name | bits | visible |
+ |--------------------------------------------------|
+ | RES0 | [63-32] | n |
+ |--------------------------------------------------|
+ | RDM | [31-28] | y |
+ |--------------------------------------------------|
+ | ATOMICS | [23-20] | y |
+ |--------------------------------------------------|
+ | CRC32 | [19-16] | y |
+ |--------------------------------------------------|
+ | SHA2 | [15-12] | y |
+ |--------------------------------------------------|
+ | SHA1 | [11-8] | y |
+ |--------------------------------------------------|
+ | AES | [7-4] | y |
+ |--------------------------------------------------|
+ | RES0 | [3-0] | n |
+ x--------------------------------------------------x
+
+
+ 2) ID_AA64PFR0_EL1 - Processor Feature Register 0
+ x--------------------------------------------------x
+ | Name | bits | visible |
+ |--------------------------------------------------|
+ | RES0 | [63-28] | n |
+ |--------------------------------------------------|
+ | GIC | [27-24] | n |
+ |--------------------------------------------------|
+ | AdvSIMD | [23-20] | y |
+ |--------------------------------------------------|
+ | FP | [19-16] | y |
+ |--------------------------------------------------|
+ | EL3 | [15-12] | n |
+ |--------------------------------------------------|
+ | EL2 | [11-8] | n |
+ |--------------------------------------------------|
+ | EL1 | [7-4] | n |
+ |--------------------------------------------------|
+ | EL0 | [3-0] | n |
+ x--------------------------------------------------x
+
+
+ 3) MIDR_EL1 - Main ID Register
+ x--------------------------------------------------x
+ | Name | bits | visible |
+ |--------------------------------------------------|
+ | Implementer | [31-24] | y |
+ |--------------------------------------------------|
+ | Variant | [23-20] | y |
+ |--------------------------------------------------|
+ | Architecture | [19-16] | y |
+ |--------------------------------------------------|
+ | PartNum | [15-4] | y |
+ |--------------------------------------------------|
+ | Revision | [3-0] | y |
+ x--------------------------------------------------x
+
+ NOTE: The 'visible' fields of MIDR_EL1 will contain the value
+ as available on the CPU where it is fetched and is not a system
+ wide safe value.
+
+Appendix I: Example
+---------------------------
+
+/*
+ * Sample program to demonstrate the MRS emulation ABI.
+ *
+ * Copyright (C) 2015-2016, ARM Ltd
+ *
+ * Author: Suzuki K Poulose <suzuki.poulose@arm.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/hwcap.h>
+#include <stdio.h>
+#include <sys/auxv.h>
+
+#define get_cpu_ftr(id) ({ \
+ unsigned long __val; \
+ asm("mrs %0, "#id : "=r" (__val)); \
+ printf("%-20s: 0x%016lx\n", #id, __val); \
+ })
+
+int main(void)
+{
+
+ if (!(getauxval(AT_HWCAP) & HWCAP_CPUID)) {
+ fputs("CPUID registers unavailable\n", stderr);
+ return 1;
+ }
+
+ get_cpu_ftr(ID_AA64ISAR0_EL1);
+ get_cpu_ftr(ID_AA64ISAR1_EL1);
+ get_cpu_ftr(ID_AA64MMFR0_EL1);
+ get_cpu_ftr(ID_AA64MMFR1_EL1);
+ get_cpu_ftr(ID_AA64PFR0_EL1);
+ get_cpu_ftr(ID_AA64PFR1_EL1);
+ get_cpu_ftr(ID_AA64DFR0_EL1);
+ get_cpu_ftr(ID_AA64DFR1_EL1);
+
+ get_cpu_ftr(MIDR_EL1);
+ get_cpu_ftr(MPIDR_EL1);
+ get_cpu_ftr(REVIDR_EL1);
+
+#if 0
+ /* Unexposed register access causes SIGILL */
+ get_cpu_ftr(ID_MMFR0_EL1);
+#endif
+
+ return 0;
+}
+
+
+
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 6494f9b..fa707e3 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -82,6 +82,10 @@ static bool __maybe_unused
cpufeature_pan_not_uao(const struct arm64_cpu_capabilities *entry, int __unused);
+/*
+ * NOTE: Any changes to the visibility of features should be kept in
+ * sync with the documentation of the CPU feature register ABI.
+ */
static const struct arm64_ftr_bits ftr_id_aa64isar0[] = {
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_EXACT, ID_AA64ISAR0_RDM_SHIFT, 4, 0),
ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR0_ATOMICS_SHIFT, 4, 0),
--
2.7.4
^ permalink raw reply related
* [PATCH v4 8/9] arm64: cpufeature: Expose CPUID registers by emulation
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 adds the hook for emulating MRS instruction to
export the 'user visible' value of supported system registers.
We emulate only the following id space for system registers:
Op0=3, Op1=0, CRn=0, CRm=[0, 4-7]
The rest will fall back to SIGILL. This capability is also
advertised via a new HWCAP_CPUID.
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 | 4 ++
arch/arm64/include/uapi/asm/hwcap.h | 1 +
arch/arm64/kernel/cpufeature.c | 101 ++++++++++++++++++++++++++++++++++++
arch/arm64/kernel/cpuinfo.c | 1 +
4 files changed, 107 insertions(+)
diff --git a/arch/arm64/include/asm/sysreg.h b/arch/arm64/include/asm/sysreg.h
index cf43064..86a207a 100644
--- a/arch/arm64/include/asm/sysreg.h
+++ b/arch/arm64/include/asm/sysreg.h
@@ -264,6 +264,10 @@
#define ID_AA64MMFR0_TGRAN_SUPPORTED ID_AA64MMFR0_TGRAN64_SUPPORTED
#endif
+
+/* Safe value for MPIDR_EL1: Bit31:RES1, Bit30:U:0, Bit24:MT:0 */
+#define SYS_MPIDR_SAFE_VAL (1UL << 31)
+
#ifdef __ASSEMBLY__
.irp num,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30
diff --git a/arch/arm64/include/uapi/asm/hwcap.h b/arch/arm64/include/uapi/asm/hwcap.h
index a739287..773c90b 100644
--- a/arch/arm64/include/uapi/asm/hwcap.h
+++ b/arch/arm64/include/uapi/asm/hwcap.h
@@ -30,5 +30,6 @@
#define HWCAP_ATOMICS (1 << 8)
#define HWCAP_FPHP (1 << 9)
#define HWCAP_ASIMDHP (1 << 10)
+#define HWCAP_CPUID (1 << 11)
#endif /* _UAPI__ASM_HWCAP_H */
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index 346faec..6494f9b 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -29,6 +29,7 @@
#include <asm/mmu_context.h>
#include <asm/processor.h>
#include <asm/sysreg.h>
+#include <asm/traps.h>
#include <asm/virt.h>
unsigned long elf_hwcap __read_mostly;
@@ -932,6 +933,8 @@ static bool cpus_have_elf_hwcap(const struct arm64_cpu_capabilities *cap)
static void __init setup_elf_hwcaps(const struct arm64_cpu_capabilities *hwcaps)
{
+ /* We support emulation of accesses to CPU ID feature registers */
+ elf_hwcap |= HWCAP_CPUID;
for (; hwcaps->matches; hwcaps++)
if (hwcaps->matches(hwcaps, hwcaps->def_scope))
cap_set_elf_hwcap(hwcaps);
@@ -1119,3 +1122,101 @@ cpufeature_pan_not_uao(const struct arm64_cpu_capabilities *entry, int __unused)
{
return (cpus_have_const_cap(ARM64_HAS_PAN) && !cpus_have_const_cap(ARM64_HAS_UAO));
}
+
+/*
+ * We emulate only the following system register space.
+ * Op0 = 0x3, CRn = 0x0, Op1 = 0x0, CRm = [0, 4 - 7]
+ * See Table C5-6 System instruction encodings for System register accesses,
+ * ARMv8 ARM(ARM DDI 0487A.f) for more details.
+ */
+static inline bool __attribute_const__ is_emulated(u32 id)
+{
+ return (sys_reg_Op0(id) == 0x3 &&
+ sys_reg_CRn(id) == 0x0 &&
+ sys_reg_Op1(id) == 0x0 &&
+ (sys_reg_CRm(id) == 0 ||
+ ((sys_reg_CRm(id) >= 4) && (sys_reg_CRm(id) <= 7))));
+}
+
+/*
+ * With CRm == 0, reg should be one of :
+ * MIDR_EL1, MPIDR_EL1 or REVIDR_EL1.
+ */
+static inline int emulate_id_reg(u32 id, u64 *valp)
+{
+ switch (id) {
+ case SYS_MIDR_EL1:
+ *valp = read_cpuid_id();
+ break;
+ case SYS_MPIDR_EL1:
+ *valp = SYS_MPIDR_SAFE_VAL;
+ break;
+ case SYS_REVIDR_EL1:
+ /* IMPLEMENTATION DEFINED values are emulated with 0 */
+ *valp = 0;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int emulate_sys_reg(u32 id, u64 *valp)
+{
+ struct arm64_ftr_reg *regp;
+
+ if (!is_emulated(id))
+ return -EINVAL;
+
+ if (sys_reg_CRm(id) == 0)
+ return emulate_id_reg(id, valp);
+
+ regp = get_arm64_ftr_reg(id);
+ if (regp)
+ *valp = arm64_ftr_reg_user_value(regp);
+ else
+ /*
+ * The untracked registers are either IMPLEMENTATION DEFINED
+ * (e.g, ID_AFR0_EL1) or reserved RAZ.
+ */
+ *valp = 0;
+ return 0;
+}
+
+static int emulate_mrs(struct pt_regs *regs, u32 insn)
+{
+ int rc;
+ u32 sys_reg, dst;
+ u64 val;
+
+ /*
+ * sys_reg values are defined as used in mrs/msr instruction.
+ * shift the imm value to get the encoding.
+ */
+ sys_reg = (u32)aarch64_insn_decode_immediate(AARCH64_INSN_IMM_16, insn) << 5;
+ rc = emulate_sys_reg(sys_reg, &val);
+ if (!rc) {
+ dst = aarch64_insn_decode_register(AARCH64_INSN_REGTYPE_RT, insn);
+ regs->user_regs.regs[dst] = val;
+ regs->pc += 4;
+ }
+
+ return rc;
+}
+
+static struct undef_hook mrs_hook = {
+ .instr_mask = 0xfff00000,
+ .instr_val = 0xd5300000,
+ .pstate_mask = COMPAT_PSR_MODE_MASK,
+ .pstate_val = PSR_MODE_EL0t,
+ .fn = emulate_mrs,
+};
+
+int __init enable_mrs_emulation(void)
+{
+ register_undef_hook(&mrs_hook);
+ return 0;
+}
+
+late_initcall(enable_mrs_emulation);
diff --git a/arch/arm64/kernel/cpuinfo.c b/arch/arm64/kernel/cpuinfo.c
index 7b7be71..4d44edd 100644
--- a/arch/arm64/kernel/cpuinfo.c
+++ b/arch/arm64/kernel/cpuinfo.c
@@ -63,6 +63,7 @@ static const char *const hwcap_str[] = {
"atomics",
"fphp",
"asimdhp",
+ "cpuid",
NULL
};
--
2.7.4
^ permalink raw reply related
* [PATCH v4 7/9] arm64: cpufeature: Track user visible 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>
Track the user visible fields of a CPU feature register. This will be
used for exposing the value to the userspace. All the user visible
fields of a feature register will be passed on as it is, while the
others would be filled with their respective safe value.
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:
- Mark ID_AA64ISAR0_EL1:RDM visible
Changes since V2:
- Mark DCZID_EL0 fields visible, as the register is already accessible
to EL0.
---
arch/arm64/include/asm/cpufeature.h | 11 +++
arch/arm64/kernel/cpufeature.c | 189 +++++++++++++++++++-----------------
arch/arm64/kernel/traps.c | 2 +-
3 files changed, 111 insertions(+), 91 deletions(-)
diff --git a/arch/arm64/include/asm/cpufeature.h b/arch/arm64/include/asm/cpufeature.h
index 10d5624..4ce82ed 100644
--- a/arch/arm64/include/asm/cpufeature.h
+++ b/arch/arm64/include/asm/cpufeature.h
@@ -55,8 +55,12 @@ enum ftr_type {
#define FTR_SIGNED true /* Value should be treated as signed */
#define FTR_UNSIGNED false /* Value should be treated as unsigned */
+#define FTR_VISIBLE true /* Feature visible to the user space */
+#define FTR_HIDDEN false /* Feature is hidden from the user */
+
struct arm64_ftr_bits {
bool sign; /* Value is signed ? */
+ bool visible;
bool strict; /* CPU Sanity check: strict matching required ? */
enum ftr_type type;
u8 shift;
@@ -72,7 +76,9 @@ struct arm64_ftr_bits {
struct arm64_ftr_reg {
const char *name;
u64 strict_mask;
+ u64 user_mask;
u64 sys_val;
+ u64 user_val;
const struct arm64_ftr_bits *ftr_bits;
};
@@ -172,6 +178,11 @@ static inline u64 arm64_ftr_mask(const struct arm64_ftr_bits *ftrp)
return (u64)GENMASK(ftrp->shift + ftrp->width - 1, ftrp->shift);
}
+static inline u64 arm64_ftr_reg_user_value(const struct arm64_ftr_reg *reg)
+{
+ return (reg->user_val | (reg->sys_val & reg->user_mask));
+}
+
static inline int __attribute_const__
cpuid_feature_extract_field(u64 features, int field, bool sign)
{
diff --git a/arch/arm64/kernel/cpufeature.c b/arch/arm64/kernel/cpufeature.c
index fb519e1..346faec 100644
--- a/arch/arm64/kernel/cpufeature.c
+++ b/arch/arm64/kernel/cpufeature.c
@@ -52,9 +52,10 @@ EXPORT_SYMBOL(cpu_hwcaps);
DEFINE_STATIC_KEY_ARRAY_FALSE(cpu_hwcap_keys, ARM64_NCAPS);
EXPORT_SYMBOL(cpu_hwcap_keys);
-#define __ARM64_FTR_BITS(SIGNED, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL) \
+#define __ARM64_FTR_BITS(SIGNED, VISIBLE, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL) \
{ \
.sign = SIGNED, \
+ .visible = VISIBLE, \
.strict = STRICT, \
.type = TYPE, \
.shift = SHIFT, \
@@ -63,12 +64,12 @@ EXPORT_SYMBOL(cpu_hwcap_keys);
}
/* Define a feature with unsigned values */
-#define ARM64_FTR_BITS(STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL) \
- __ARM64_FTR_BITS(FTR_UNSIGNED, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL)
+#define ARM64_FTR_BITS(VISIBLE, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL) \
+ __ARM64_FTR_BITS(FTR_UNSIGNED, VISIBLE, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL)
/* Define a feature with a signed value */
-#define S_ARM64_FTR_BITS(STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL) \
- __ARM64_FTR_BITS(FTR_SIGNED, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL)
+#define S_ARM64_FTR_BITS(VISIBLE, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL) \
+ __ARM64_FTR_BITS(FTR_SIGNED, VISIBLE, STRICT, TYPE, SHIFT, WIDTH, SAFE_VAL)
#define ARM64_FTR_END \
{ \
@@ -81,75 +82,75 @@ 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, ID_AA64ISAR0_RDM_SHIFT, 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_VISIBLE, FTR_STRICT, FTR_EXACT, ID_AA64ISAR0_RDM_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR0_ATOMICS_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR0_CRC32_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR0_SHA2_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR0_SHA1_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64ISAR0_AES_SHIFT, 4, 0),
ARM64_FTR_END,
};
static const struct arm64_ftr_bits ftr_id_aa64pfr0[] = {
- 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),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64PFR0_GIC_SHIFT, 4, 0),
+ S_ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_ASIMD_SHIFT, 4, ID_AA64PFR0_ASIMD_NI),
+ S_ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64PFR0_FP_SHIFT, 4, ID_AA64PFR0_FP_NI),
/* Linux doesn't care about the EL3 */
- ARM64_FTR_BITS(FTR_NONSTRICT, FTR_EXACT, ID_AA64PFR0_EL3_SHIFT, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64PFR0_EL2_SHIFT, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64PFR0_EL1_SHIFT, 4, ID_AA64PFR0_EL1_64BIT_ONLY),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64PFR0_EL0_SHIFT, 4, ID_AA64PFR0_EL0_64BIT_ONLY),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_EXACT, ID_AA64PFR0_EL3_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64PFR0_EL2_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64PFR0_EL1_SHIFT, 4, ID_AA64PFR0_EL1_64BIT_ONLY),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64PFR0_EL0_SHIFT, 4, ID_AA64PFR0_EL0_64BIT_ONLY),
ARM64_FTR_END,
};
static const struct arm64_ftr_bits ftr_id_aa64mmfr0[] = {
- 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),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR0_BIGENDEL0_SHIFT, 4, 0),
+ S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64MMFR0_TGRAN4_SHIFT, 4, ID_AA64MMFR0_TGRAN4_NI),
+ S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64MMFR0_TGRAN64_SHIFT, 4, ID_AA64MMFR0_TGRAN64_NI),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64MMFR0_TGRAN16_SHIFT, 4, ID_AA64MMFR0_TGRAN16_NI),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64MMFR0_BIGENDEL0_SHIFT, 4, 0),
/* Linux shouldn't care about secure memory */
- ARM64_FTR_BITS(FTR_NONSTRICT, FTR_EXACT, ID_AA64MMFR0_SNSMEM_SHIFT, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR0_BIGENDEL_SHIFT, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR0_ASID_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_EXACT, ID_AA64MMFR0_SNSMEM_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64MMFR0_BIGENDEL_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64MMFR0_ASID_SHIFT, 4, 0),
/*
* Differing PARange is fine as long as all peripherals and memory are mapped
* within the minimum PARange of all CPUs
*/
- ARM64_FTR_BITS(FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64MMFR0_PARANGE_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, ID_AA64MMFR0_PARANGE_SHIFT, 4, 0),
ARM64_FTR_END,
};
static const struct arm64_ftr_bits ftr_id_aa64mmfr1[] = {
- 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),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR1_VHE_SHIFT, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR1_VMIDBITS_SHIFT, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR1_HADBS_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64MMFR1_PAN_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64MMFR1_LOR_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64MMFR1_HPD_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64MMFR1_VHE_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64MMFR1_VMIDBITS_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64MMFR1_HADBS_SHIFT, 4, 0),
ARM64_FTR_END,
};
static const struct arm64_ftr_bits ftr_id_aa64mmfr2[] = {
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR2_LVA_SHIFT, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR2_IESB_SHIFT, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR2_LSM_SHIFT, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR2_UAO_SHIFT, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64MMFR2_CNP_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64MMFR2_LVA_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64MMFR2_IESB_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64MMFR2_LSM_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64MMFR2_UAO_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64MMFR2_CNP_SHIFT, 4, 0),
ARM64_FTR_END,
};
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_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 */
+ ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_EXACT, 31, 1, 1), /* RAO */
+ ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_HIGHER_SAFE, 24, 4, 0), /* CWG */
+ ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, 20, 4, 0), /* ERG */
+ ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, 16, 4, 1), /* DminLine */
/*
* Linux can handle differing I-cache policies. Userspace JITs will
* make use of *minLine.
* 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_LOWER_SAFE, 0, 4, 0), /* IminLine */
+ ARM64_FTR_BITS(FTR_VISIBLE, FTR_NONSTRICT, FTR_EXACT, 14, 2, ICACHE_POLICY_AIVIVT), /* L1Ip */
+ ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, 0, 4, 0), /* IminLine */
ARM64_FTR_END,
};
@@ -159,73 +160,73 @@ struct arm64_ftr_reg arm64_ftr_reg_ctrel0 = {
};
static const struct arm64_ftr_bits ftr_id_mmfr0[] = {
- S_ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 28, 4, 0xf), /* InnerShr */
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 24, 4, 0), /* FCSE */
- ARM64_FTR_BITS(FTR_NONSTRICT, FTR_LOWER_SAFE, 20, 4, 0), /* AuxReg */
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 16, 4, 0), /* TCM */
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 12, 4, 0), /* ShareLvl */
- S_ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 8, 4, 0xf), /* OuterShr */
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 4, 4, 0), /* PMSA */
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 0, 4, 0), /* VMSA */
+ S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, 28, 4, 0xf), /* InnerShr */
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, 24, 4, 0), /* FCSE */
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_NONSTRICT, FTR_LOWER_SAFE, 20, 4, 0), /* AuxReg */
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, 16, 4, 0), /* TCM */
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, 12, 4, 0), /* ShareLvl */
+ S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, 8, 4, 0xf), /* OuterShr */
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, 4, 4, 0), /* PMSA */
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, 0, 4, 0), /* VMSA */
ARM64_FTR_END,
};
static const struct arm64_ftr_bits ftr_id_aa64dfr0[] = {
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 32, 32, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_CTX_CMPS_SHIFT, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_WRPS_SHIFT, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_BRPS_SHIFT, 4, 0),
- S_ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64DFR0_TRACEVER_SHIFT, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, 32, 32, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_CTX_CMPS_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_WRPS_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, ID_AA64DFR0_BRPS_SHIFT, 4, 0),
+ S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64DFR0_PMUVER_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64DFR0_TRACEVER_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_AA64DFR0_DEBUGVER_SHIFT, 4, 0x6),
ARM64_FTR_END,
};
static const struct arm64_ftr_bits ftr_mvfr2[] = {
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 4, 4, 0), /* FPMisc */
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 0, 4, 0), /* SIMDMisc */
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, 4, 4, 0), /* FPMisc */
+ ARM64_FTR_BITS(FTR_HIDDEN, 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, 4, 1, 1), /* DZP */
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 0, 4, 0), /* BS */
+ ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_EXACT, 4, 1, 1), /* DZP */
+ ARM64_FTR_BITS(FTR_VISIBLE, FTR_STRICT, FTR_LOWER_SAFE, 0, 4, 0), /* BS */
ARM64_FTR_END,
};
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, 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),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_ISAR5_AES_SHIFT, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, ID_ISAR5_SEVL_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_ISAR5_RDM_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_ISAR5_CRC32_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_ISAR5_SHA2_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_ISAR5_SHA1_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_ISAR5_AES_SHIFT, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, ID_ISAR5_SEVL_SHIFT, 4, 0),
ARM64_FTR_END,
};
static const struct arm64_ftr_bits ftr_id_mmfr4[] = {
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 4, 4, 0), /* ac2 */
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, 4, 4, 0), /* ac2 */
ARM64_FTR_END,
};
static const struct arm64_ftr_bits ftr_id_pfr0[] = {
- 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 */
- ARM64_FTR_BITS(FTR_STRICT, FTR_EXACT, 0, 4, 0), /* State0 */
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, 12, 4, 0), /* State3 */
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, 8, 4, 0), /* State2 */
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, 4, 4, 0), /* State1 */
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, 0, 4, 0), /* State0 */
ARM64_FTR_END,
};
static const struct arm64_ftr_bits ftr_id_dfr0[] = {
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 28, 4, 0),
- S_ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 24, 4, 0xf), /* PerfMon */
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 20, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 16, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 12, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 8, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 4, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 0, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 28, 4, 0),
+ S_ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 24, 4, 0xf), /* PerfMon */
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 20, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 16, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 12, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 8, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 4, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 0, 4, 0),
ARM64_FTR_END,
};
@@ -236,20 +237,20 @@ static const struct arm64_ftr_bits ftr_id_dfr0[] = {
* id_isar[0-4], id_mmfr[1-3], id_pfr1, mvfr[0-1]
*/
static const struct arm64_ftr_bits ftr_generic_32bits[] = {
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 28, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 24, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 20, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 16, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 12, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 8, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 4, 4, 0),
- ARM64_FTR_BITS(FTR_STRICT, FTR_LOWER_SAFE, 0, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 28, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 24, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 20, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 16, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 12, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 8, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 4, 4, 0),
+ ARM64_FTR_BITS(FTR_HIDDEN, FTR_STRICT, FTR_LOWER_SAFE, 0, 4, 0),
ARM64_FTR_END,
};
/* 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_BITS(FTR_HIDDEN, FTR_STRICT, FTR_EXACT, 0, 32, 0),
ARM64_FTR_END,
};
@@ -397,6 +398,7 @@ static void __init init_cpu_ftr_reg(u32 sys_reg, u64 new)
{
u64 val = 0;
u64 strict_mask = ~0x0ULL;
+ u64 user_mask = 0;
u64 valid_mask = 0;
const struct arm64_ftr_bits *ftrp;
@@ -413,12 +415,19 @@ static void __init init_cpu_ftr_reg(u32 sys_reg, u64 new)
valid_mask |= ftr_mask;
if (!ftrp->strict)
strict_mask &= ~ftr_mask;
+ if (ftrp->visible)
+ user_mask |= ftr_mask;
+ else
+ reg->user_val = arm64_ftr_set_value(ftrp,
+ reg->user_val,
+ ftrp->safe_val);
}
val &= valid_mask;
reg->sys_val = val;
reg->strict_mask = strict_mask;
+ reg->user_mask = user_mask;
}
void __init init_cpu_features(struct cpuinfo_arm64 *info)
diff --git a/arch/arm64/kernel/traps.c b/arch/arm64/kernel/traps.c
index 5b830be..8187229 100644
--- a/arch/arm64/kernel/traps.c
+++ b/arch/arm64/kernel/traps.c
@@ -496,7 +496,7 @@ static void ctr_read_handler(unsigned int esr, struct pt_regs *regs)
{
int rt = (esr & ESR_ELx_SYS64_ISS_RT_MASK) >> ESR_ELx_SYS64_ISS_RT_SHIFT;
- regs->regs[rt] = arm64_ftr_reg_ctrel0.sys_val;
+ regs->regs[rt] = arm64_ftr_reg_user_value(&arm64_ftr_reg_ctrel0);
regs->pc += 4;
}
--
2.7.4
^ permalink raw reply related
* [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
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