* [PATCH v8 4/8] ARM: EXYNOS: refactor firmware specific routines
From: Pankaj Dubey @ 2016-12-10 13:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1481375323-29724-1-git-send-email-pankaj.dubey@samsung.com>
To remove dependency on soc_is_exynosMMMM macros and remove multiple
checks for such macros lets refactor code in firmware.c file.
SoC specific firmware_ops are separated and registered during
exynos_firmware_init based on matching machine compatible.
Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
---
arch/arm/mach-exynos/firmware.c | 100 ++++++++++++++++++++++++++++++----------
1 file changed, 75 insertions(+), 25 deletions(-)
diff --git a/arch/arm/mach-exynos/firmware.c b/arch/arm/mach-exynos/firmware.c
index fd6da54..525fbd9 100644
--- a/arch/arm/mach-exynos/firmware.c
+++ b/arch/arm/mach-exynos/firmware.c
@@ -35,6 +35,25 @@ static void exynos_save_cp15(void)
: : "cc");
}
+static int exynos3250_do_idle(unsigned long mode)
+{
+ switch (mode) {
+ case FW_DO_IDLE_AFTR:
+ writel_relaxed(virt_to_phys(exynos_cpu_resume_ns),
+ sysram_ns_base_addr + 0x24);
+ writel_relaxed(EXYNOS_AFTR_MAGIC, sysram_ns_base_addr + 0x20);
+ flush_cache_all();
+ exynos_smc(SMC_CMD_SAVE, OP_TYPE_CORE,
+ SMC_POWERSTATE_IDLE, 0);
+ exynos_smc(SMC_CMD_SHUTDOWN, OP_TYPE_CLUSTER,
+ SMC_POWERSTATE_IDLE, 0);
+ break;
+ case FW_DO_IDLE_SLEEP:
+ exynos_smc(SMC_CMD_SLEEP, 0, 0, 0);
+ }
+ return 0;
+}
+
static int exynos_do_idle(unsigned long mode)
{
switch (mode) {
@@ -44,14 +63,7 @@ static int exynos_do_idle(unsigned long mode)
writel_relaxed(virt_to_phys(exynos_cpu_resume_ns),
sysram_ns_base_addr + 0x24);
writel_relaxed(EXYNOS_AFTR_MAGIC, sysram_ns_base_addr + 0x20);
- if (soc_is_exynos3250()) {
- flush_cache_all();
- exynos_smc(SMC_CMD_SAVE, OP_TYPE_CORE,
- SMC_POWERSTATE_IDLE, 0);
- exynos_smc(SMC_CMD_SHUTDOWN, OP_TYPE_CLUSTER,
- SMC_POWERSTATE_IDLE, 0);
- } else
- exynos_smc(SMC_CMD_CPU0AFTR, 0, 0, 0);
+ exynos_smc(SMC_CMD_CPU0AFTR, 0, 0, 0);
break;
case FW_DO_IDLE_SLEEP:
exynos_smc(SMC_CMD_SLEEP, 0, 0, 0);
@@ -59,28 +71,25 @@ static int exynos_do_idle(unsigned long mode)
return 0;
}
-static int exynos_cpu_boot(int cpu)
+static int exynos4412_cpu_boot(int cpu)
{
/*
- * Exynos3250 doesn't need to send smc command for secondary CPU boot
- * because Exynos3250 removes WFE in secure mode.
- */
- if (soc_is_exynos3250())
- return 0;
-
- /*
* The second parameter of SMC_CMD_CPU1BOOT command means CPU id.
* But, Exynos4212 has only one secondary CPU so second parameter
* isn't used for informing secure firmware about CPU id.
*/
- if (soc_is_exynos4212())
- cpu = 0;
+ cpu = 0;
+ exynos_smc(SMC_CMD_CPU1BOOT, cpu, 0, 0);
+ return 0;
+}
+static int exynos_cpu_boot(int cpu)
+{
exynos_smc(SMC_CMD_CPU1BOOT, cpu, 0, 0);
return 0;
}
-static int exynos_set_cpu_boot_addr(int cpu, unsigned long boot_addr)
+static int exynos4412_set_cpu_boot_addr(int cpu, unsigned long boot_addr)
{
void __iomem *boot_reg;
@@ -94,14 +103,24 @@ static int exynos_set_cpu_boot_addr(int cpu, unsigned long boot_addr)
* additional offset for every CPU, with Exynos4412 being the only
* exception.
*/
- if (soc_is_exynos4412())
- boot_reg += 4 * cpu;
+ boot_reg += 4 * cpu;
+ writel_relaxed(boot_addr, boot_reg);
+ return 0;
+}
+
+static int exynos_set_cpu_boot_addr(int cpu, unsigned long boot_addr)
+{
+ void __iomem *boot_reg;
+ if (!sysram_ns_base_addr)
+ return -ENODEV;
+
+ boot_reg = sysram_ns_base_addr + 0x1c;
writel_relaxed(boot_addr, boot_reg);
return 0;
}
-static int exynos_get_cpu_boot_addr(int cpu, unsigned long *boot_addr)
+static int exynos4412_get_cpu_boot_addr(int cpu, unsigned long *boot_addr)
{
void __iomem *boot_reg;
@@ -109,10 +128,19 @@ static int exynos_get_cpu_boot_addr(int cpu, unsigned long *boot_addr)
return -ENODEV;
boot_reg = sysram_ns_base_addr + 0x1c;
+ boot_reg += 4 * cpu;
+ *boot_addr = readl_relaxed(boot_reg);
+ return 0;
+}
+
+static int exynos_get_cpu_boot_addr(int cpu, unsigned long *boot_addr)
+{
+ void __iomem *boot_reg;
- if (soc_is_exynos4412())
- boot_reg += 4 * cpu;
+ if (!sysram_ns_base_addr)
+ return -ENODEV;
+ boot_reg = sysram_ns_base_addr + 0x1c;
*boot_addr = readl_relaxed(boot_reg);
return 0;
}
@@ -148,6 +176,23 @@ static int exynos_resume(void)
return 0;
}
+static const struct firmware_ops exynos3250_firmware_ops = {
+ .do_idle = IS_ENABLED(CONFIG_EXYNOS_CPU_SUSPEND) ? exynos3250_do_idle : NULL,
+ .set_cpu_boot_addr = exynos_set_cpu_boot_addr,
+ .get_cpu_boot_addr = exynos_get_cpu_boot_addr,
+ .suspend = IS_ENABLED(CONFIG_PM_SLEEP) ? exynos_suspend : NULL,
+ .resume = IS_ENABLED(CONFIG_EXYNOS_CPU_SUSPEND) ? exynos_resume : NULL,
+};
+
+static const struct firmware_ops exynos4412_firmware_ops = {
+ .do_idle = IS_ENABLED(CONFIG_EXYNOS_CPU_SUSPEND) ? exynos_do_idle : NULL,
+ .set_cpu_boot_addr = exynos4412_set_cpu_boot_addr,
+ .get_cpu_boot_addr = exynos4412_get_cpu_boot_addr,
+ .cpu_boot = exynos4412_cpu_boot,
+ .suspend = IS_ENABLED(CONFIG_PM_SLEEP) ? exynos_suspend : NULL,
+ .resume = IS_ENABLED(CONFIG_EXYNOS_CPU_SUSPEND) ? exynos_resume : NULL,
+};
+
static const struct firmware_ops exynos_firmware_ops = {
.do_idle = IS_ENABLED(CONFIG_EXYNOS_CPU_SUSPEND) ? exynos_do_idle : NULL,
.set_cpu_boot_addr = exynos_set_cpu_boot_addr,
@@ -212,7 +257,12 @@ void __init exynos_firmware_init(void)
pr_info("Running under secure firmware.\n");
- register_firmware_ops(&exynos_firmware_ops);
+ if (of_machine_is_compatible("samsung,exynos3250"))
+ register_firmware_ops(&exynos3250_firmware_ops);
+ else if (of_machine_is_compatible("samsung,exynos4412"))
+ register_firmware_ops(&exynos4412_firmware_ops);
+ else
+ register_firmware_ops(&exynos_firmware_ops);
/*
* Exynos 4 SoCs (based on Cortex A9 and equipped with L2C-310),
--
2.7.4
^ permalink raw reply related
* [PATCH v8 5/8] ARM: EXYNOS: refactor power management specific routines
From: Pankaj Dubey @ 2016-12-10 13:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1481375323-29724-1-git-send-email-pankaj.dubey@samsung.com>
To remove dependency on soc_is_exynosMMMM macros and remove
multiple checks for such macros lets refactor code in pm.c
This patch matches SoC specific information via private data
of soc_device_attribute and initialize it at one place and then
uses it all other places
Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
---
arch/arm/mach-exynos/pm.c | 185 ++++++++++++++++++++++++++++++++++++++--------
1 file changed, 155 insertions(+), 30 deletions(-)
diff --git a/arch/arm/mach-exynos/pm.c b/arch/arm/mach-exynos/pm.c
index c0b46c3..d43bea8 100644
--- a/arch/arm/mach-exynos/pm.c
+++ b/arch/arm/mach-exynos/pm.c
@@ -20,6 +20,7 @@
#include <linux/of.h>
#include <linux/soc/samsung/exynos-regs-pmu.h>
#include <linux/soc/samsung/exynos-pmu.h>
+#include <linux/sys_soc.h>
#include <asm/firmware.h>
#include <asm/smp_scu.h>
@@ -28,22 +29,49 @@
#include "common.h"
+struct exynos_s2r_data {
+ void __iomem* (*boot_vector_addr)(void);
+ void __iomem* (*boot_vector_flag)(void);
+ void (*set_wakeupmask)(void);
+ void (*enter_aftr)(void);
+};
+
+static const struct exynos_s2r_data *s2r_data;
+
+static void __iomem *exynos4210_rev11_boot_vector_addr(void)
+{
+ return pmu_base_addr + S5P_INFORM7;
+}
+
+static void __iomem *exynos4210_rev10_boot_vector_addr(void)
+{
+ return sysram_base_addr + 0x24;
+}
+
static inline void __iomem *exynos_boot_vector_addr(void)
{
- if (samsung_rev() == EXYNOS4210_REV_1_1)
- return pmu_base_addr + S5P_INFORM7;
- else if (samsung_rev() == EXYNOS4210_REV_1_0)
- return sysram_base_addr + 0x24;
- return pmu_base_addr + S5P_INFORM0;
+ if (s2r_data && s2r_data->boot_vector_addr)
+ return s2r_data->boot_vector_addr();
+ else
+ return pmu_base_addr + S5P_INFORM0;
+}
+
+static void __iomem *exynos4210_rev11_boot_vector_flag(void)
+{
+ return pmu_base_addr + S5P_INFORM6;
+}
+
+static void __iomem *exynos4210_rev10_boot_vector_flag(void)
+{
+ return sysram_base_addr + 0x20;
}
static inline void __iomem *exynos_boot_vector_flag(void)
{
- if (samsung_rev() == EXYNOS4210_REV_1_1)
- return pmu_base_addr + S5P_INFORM6;
- else if (samsung_rev() == EXYNOS4210_REV_1_0)
- return sysram_base_addr + 0x20;
- return pmu_base_addr + S5P_INFORM1;
+ if (s2r_data && s2r_data->boot_vector_flag)
+ return s2r_data->boot_vector_flag();
+ else
+ return pmu_base_addr + S5P_INFORM1;
}
#define S5P_CHECK_AFTR 0xFCBA0D10
@@ -120,12 +148,19 @@ int exynos_pm_central_resume(void)
return 0;
}
+static void exynos3250_set_wakeupmask(void)
+{
+ pmu_raw_writel(0x40003ffe, S5P_WAKEUP_MASK);
+ pmu_raw_writel(0x0, S5P_WAKEUP_MASK2);
+}
+
/* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */
-static void exynos_set_wakeupmask(long mask)
+static void exynos_set_wakeupmask(void)
{
- pmu_raw_writel(mask, S5P_WAKEUP_MASK);
- if (soc_is_exynos3250())
- pmu_raw_writel(0x0, S5P_WAKEUP_MASK2);
+ if (s2r_data && s2r_data->set_wakeupmask)
+ s2r_data->set_wakeupmask();
+ else
+ pmu_raw_writel(0x0000ff3e, S5P_WAKEUP_MASK);
}
static void exynos_cpu_set_boot_vector(long flags)
@@ -139,7 +174,7 @@ static int exynos_aftr_finisher(unsigned long flags)
{
int ret;
- exynos_set_wakeupmask(soc_is_exynos3250() ? 0x40003ffe : 0x0000ff3e);
+ exynos_set_wakeupmask();
/* Set value of power down register for aftr mode */
exynos_sys_powerdown_conf(SYS_AFTR);
@@ -154,23 +189,30 @@ static int exynos_aftr_finisher(unsigned long flags)
return 1;
}
-void exynos_enter_aftr(void)
+static void exynos3250_enter_aftr(void)
{
unsigned int cpuid = smp_processor_id();
cpu_pm_enter();
- if (soc_is_exynos3250())
- exynos_set_boot_flag(cpuid, C2_STATE);
+ exynos_set_boot_flag(cpuid, C2_STATE);
+ exynos_pm_central_suspend();
+ cpu_suspend(0, exynos_aftr_finisher);
+ exynos_pm_central_resume();
+ exynos_clear_boot_flag(cpuid, C2_STATE);
+
+ cpu_pm_exit();
+}
+
+void exynos4x12_enter_aftr(void)
+{
+ cpu_pm_enter();
exynos_pm_central_suspend();
- if (of_machine_is_compatible("samsung,exynos4212") ||
- of_machine_is_compatible("samsung,exynos4412")) {
- /* Setting SEQ_OPTION register */
- pmu_raw_writel(S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0,
- S5P_CENTRAL_SEQ_OPTION);
- }
+ /* Setting SEQ_OPTION register */
+ pmu_raw_writel(S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0,
+ S5P_CENTRAL_SEQ_OPTION);
cpu_suspend(0, exynos_aftr_finisher);
@@ -182,12 +224,95 @@ void exynos_enter_aftr(void)
exynos_pm_central_resume();
- if (soc_is_exynos3250())
- exynos_clear_boot_flag(cpuid, C2_STATE);
+ cpu_pm_exit();
+}
+
+void exynos_common_enter_aftr(void)
+{
+ cpu_pm_enter();
+
+ exynos_pm_central_suspend();
+ cpu_suspend(0, exynos_aftr_finisher);
+
+ if (read_cpuid_part() == ARM_CPU_PART_CORTEX_A9) {
+ exynos_scu_enable();
+ if (call_firmware_op(resume) == -ENOSYS)
+ exynos_cpu_restore_register();
+ }
+
+ exynos_pm_central_resume();
cpu_pm_exit();
}
+void exynos_enter_aftr(void)
+{
+ if (s2r_data && s2r_data->enter_aftr)
+ s2r_data->enter_aftr();
+}
+
+static const struct exynos_s2r_data exynos_common_s2r_data = {
+ .enter_aftr = exynos_common_enter_aftr,
+};
+
+static const struct exynos_s2r_data exynos3250_s2r_data = {
+ .set_wakeupmask = exynos3250_set_wakeupmask,
+ .enter_aftr = exynos3250_enter_aftr,
+};
+
+static const struct exynos_s2r_data exynos4210_rev11_s2r_data = {
+ .boot_vector_addr = exynos4210_rev11_boot_vector_addr,
+ .boot_vector_flag = exynos4210_rev11_boot_vector_flag,
+ .enter_aftr = exynos_common_enter_aftr,
+};
+
+static const struct exynos_s2r_data exynos4210_rev10_s2r_data = {
+ .boot_vector_addr = exynos4210_rev10_boot_vector_addr,
+ .boot_vector_flag = exynos4210_rev10_boot_vector_flag,
+ .enter_aftr = exynos_common_enter_aftr,
+};
+
+static const struct exynos_s2r_data exynos4x12_s2r_data = {
+ .enter_aftr = exynos4x12_enter_aftr,
+};
+
+static const struct soc_device_attribute exynos_soc_revision[] = {
+ { .soc_id = "EXYNOS3250", .data = &exynos3250_s2r_data },
+ {
+ .soc_id = "EXYNOS4210", .revision = "11",
+ .data = &exynos4210_rev11_s2r_data
+ },
+ {
+ .soc_id = "EXYNOS4210", .revision = "10",
+ .data = &exynos4210_rev10_s2r_data
+ },
+ { .soc_id = "EXYNOS4212", .data = &exynos4x12_s2r_data },
+ { .soc_id = "EXYNOS4412", .data = &exynos4x12_s2r_data },
+ { .soc_id = "EXYNOS4415", .data = &exynos_common_s2r_data },
+ { .soc_id = "EXYNOS5250", .data = &exynos_common_s2r_data },
+ { .soc_id = "EXYNOS5260", .data = &exynos_common_s2r_data },
+ { .soc_id = "EXYNOS5410", .data = &exynos_common_s2r_data },
+ { .soc_id = "EXYNOS5420", .data = &exynos_common_s2r_data },
+ { .soc_id = "EXYNOS5440", .data = &exynos_common_s2r_data },
+ { .soc_id = "EXYNOS5800", .data = &exynos_common_s2r_data },
+};
+
+int __init exynos_s2r_init(void)
+{
+ const struct soc_device_attribute *match;
+
+ match = soc_device_match(exynos_soc_revision);
+
+ if (match)
+ s2r_data = (const struct exynos_s2r_data *) match->data;
+
+ if (!s2r_data)
+ return -ENODEV;
+
+ return 0;
+}
+arch_initcall(exynos_s2r_init);
+
#if defined(CONFIG_SMP) && defined(CONFIG_ARM_EXYNOS_CPUIDLE)
static atomic_t cpu1_wakeup = ATOMIC_INIT(0);
@@ -253,7 +378,7 @@ static int exynos_cpu0_enter_aftr(void)
while (exynos_cpu_power_state(1) != S5P_CORE_LOCAL_PWR_EN)
cpu_relax();
- if (soc_is_exynos3250()) {
+ if (of_machine_is_compatible("samsung,exynos3250")) {
while (!pmu_raw_readl(S5P_PMU_SPARE2) &&
!atomic_read(&cpu1_wakeup))
cpu_relax();
@@ -275,7 +400,7 @@ static int exynos_cpu0_enter_aftr(void)
call_firmware_op(cpu_boot, 1);
- if (soc_is_exynos3250())
+ if (of_machine_is_compatible("samsung,exynos3250"))
dsb_sev();
else
arch_send_wakeup_ipi_mask(cpumask_of(1));
@@ -287,7 +412,7 @@ static int exynos_cpu0_enter_aftr(void)
static int exynos_wfi_finisher(unsigned long flags)
{
- if (soc_is_exynos3250())
+ if (of_machine_is_compatible("samsung,exynos3250"))
flush_cache_all();
cpu_do_idle();
@@ -309,7 +434,7 @@ static int exynos_cpu1_powerdown(void)
*/
exynos_cpu_power_down(1);
- if (soc_is_exynos3250())
+ if (of_machine_is_compatible("samsung,exynos3250"))
pmu_raw_writel(0, S5P_PMU_SPARE2);
ret = cpu_suspend(0, exynos_wfi_finisher);
--
2.7.4
^ permalink raw reply related
* [PATCH v8 6/8] ARM: EXYNOS: remove secondary startup initialization from smp_prepare_cpus
From: Pankaj Dubey @ 2016-12-10 13:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1481375323-29724-1-git-send-email-pankaj.dubey@samsung.com>
We are taking care of setting secondary cpu boot address in
exynos_boot_secondary just before sending ipi to secondary CPUs,
so we can safely remove this setting from smp_prepare_cpus.
Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
---
arch/arm/mach-exynos/platsmp.c | 25 -------------------------
1 file changed, 25 deletions(-)
diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c
index 43eec10..4de254e 100644
--- a/arch/arm/mach-exynos/platsmp.c
+++ b/arch/arm/mach-exynos/platsmp.c
@@ -403,8 +403,6 @@ static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
static void __init exynos_smp_prepare_cpus(unsigned int max_cpus)
{
- int i;
-
exynos_sysram_init();
exynos_set_delayed_reset_assertion(true);
@@ -414,29 +412,6 @@ static void __init exynos_smp_prepare_cpus(unsigned int max_cpus)
if (exynos_scu_enable())
return;
}
- /*
- * Write the address of secondary startup into the
- * system-wide flags register. The boot monitor waits
- * until it receives a soft interrupt, and then the
- * secondary CPU branches to this address.
- *
- * Try using firmware operation first and fall back to
- * boot register if it fails.
- */
- for (i = 1; i < max_cpus; ++i) {
- unsigned long boot_addr;
- u32 mpidr;
- u32 core_id;
- int ret;
-
- mpidr = cpu_logical_map(i);
- core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
- boot_addr = virt_to_phys(exynos4_secondary_startup);
-
- ret = exynos_set_boot_addr(core_id, boot_addr);
- if (ret)
- break;
- }
}
#ifdef CONFIG_HOTPLUG_CPU
--
2.7.4
^ permalink raw reply related
* [PATCH v8 7/8] ARM: EXYNOS: refactor smp specific code and routines
From: Pankaj Dubey @ 2016-12-10 13:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1481375323-29724-1-git-send-email-pankaj.dubey@samsung.com>
To remove dependency on soc_is_exynosMMMM macros and remove
multiple checks for such macros lets refactor code in platsmp.c.
This patch introduces new structure as exynos_cpu_info to
separate such variable information and routines which vary from
one Exynos SoC to other SoC. During smp_prepare_cpus lets match
SoC specific information to select appropriate exynos_cpu_info
and use it in all other places
Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
---
arch/arm/mach-exynos/platsmp.c | 244 +++++++++++++++++++++++++++++++++++------
1 file changed, 210 insertions(+), 34 deletions(-)
diff --git a/arch/arm/mach-exynos/platsmp.c b/arch/arm/mach-exynos/platsmp.c
index 4de254e..759a184 100644
--- a/arch/arm/mach-exynos/platsmp.c
+++ b/arch/arm/mach-exynos/platsmp.c
@@ -19,6 +19,7 @@
#include <linux/smp.h>
#include <linux/io.h>
#include <linux/of_address.h>
+#include <linux/sys_soc.h>
#include <linux/soc/samsung/exynos-regs-pmu.h>
#include <asm/cacheflush.h>
@@ -27,12 +28,26 @@
#include <asm/smp_scu.h>
#include <asm/firmware.h>
-#include <mach/map.h>
-
#include "common.h"
extern void exynos4_secondary_startup(void);
+/*
+ * struct exynos_cpu_info - Exynos CPU related info/operations
+ * @cpu_boot_reg: computes cpu boot address for requested cpu
+ * @cpu_power_down: handles cpu power down routine for requested cpu
+ * @cpu_power_up: handles cpu power up routine for requested cpu
+ * @cpu_restart: handles cpu restart routine for requested cpu
+ */
+struct exynos_cpu_info {
+ void __iomem* (*cpu_boot_reg)(u32 cpu);
+ void (*cpu_power_down)(u32 cpu);
+ void (*cpu_power_up)(u32 cpu);
+ void (*cpu_restart)(u32 cpu);
+};
+
+static const struct exynos_cpu_info *cpu_info;
+
#ifdef CONFIG_HOTPLUG_CPU
static inline void cpu_leave_lowpower(u32 core_id)
{
@@ -81,19 +96,39 @@ static inline void platform_do_lowpower(unsigned int cpu, int *spurious)
}
#endif /* CONFIG_HOTPLUG_CPU */
-/**
- * exynos_core_power_down : power down the specified cpu
+/*
+ * exynos_cpu_power_down - power down the specified cpu
* @cpu : the cpu to power down
*
- * Power down the specified cpu. The sequence must be finished by a
- * call to cpu_do_idle()
- *
+ * The sequence must be finished by a call to cpu_do_idle()
*/
void exynos_cpu_power_down(int cpu)
{
+ if (cpu_info && cpu_info->cpu_power_down)
+ cpu_info->cpu_power_down(cpu);
+}
+
+/*
+ * exynos_common_cpu_power_down - common cpu power down routine for Exynos SoC
+ * @cpu : the cpu to power down
+ */
+static void exynos_common_cpu_power_down(u32 cpu)
+{
u32 core_conf;
- if (cpu == 0 && (soc_is_exynos5420() || soc_is_exynos5800())) {
+ core_conf = pmu_raw_readl(EXYNOS_ARM_CORE_CONFIGURATION(cpu));
+ core_conf &= ~S5P_CORE_LOCAL_PWR_EN;
+ pmu_raw_writel(core_conf, EXYNOS_ARM_CORE_CONFIGURATION(cpu));
+}
+
+/*
+ * exynos5420_cpu_power_down - Exynos5420/Exynos5800 specific cpu power down
+ * routine
+ * @cpu : the cpu to power down
+ */
+static void exynos5420_cpu_power_down(u32 cpu)
+{
+ if (cpu == 0) {
/*
* Bypass power down for CPU0 during suspend. Check for
* the SYS_PWR_REG value to decide if we are suspending
@@ -105,24 +140,39 @@ void exynos_cpu_power_down(int cpu)
return;
}
- core_conf = pmu_raw_readl(EXYNOS_ARM_CORE_CONFIGURATION(cpu));
- core_conf &= ~S5P_CORE_LOCAL_PWR_EN;
- pmu_raw_writel(core_conf, EXYNOS_ARM_CORE_CONFIGURATION(cpu));
+ exynos_common_cpu_power_down(cpu);
}
/**
* exynos_cpu_power_up : power up the specified cpu
* @cpu : the cpu to power up
- *
- * Power up the specified cpu
*/
void exynos_cpu_power_up(int cpu)
{
+ if (cpu_info && cpu_info->cpu_power_up)
+ cpu_info->cpu_power_up(cpu);
+}
+
+/*
+ * exynos_common_cpu_power_up - common cpu power up routine for Exynos SoC
+ * @cpu : the cpu to power up
+ */
+static void exynos_common_cpu_power_up(u32 cpu)
+{
u32 core_conf = S5P_CORE_LOCAL_PWR_EN;
+ pmu_raw_writel(core_conf,
+ EXYNOS_ARM_CORE_CONFIGURATION(cpu));
+}
- if (soc_is_exynos3250())
- core_conf |= S5P_CORE_AUTOWAKEUP_EN;
+/*
+ * exynos3250_cpu_power_up - Exynos3250 specific cpu power up routine
+ * @cpu : the cpu to power down
+ */
+static void exynos3250_cpu_power_up(u32 cpu)
+{
+ u32 core_conf = S5P_CORE_LOCAL_PWR_EN;
+ core_conf |= S5P_CORE_AUTOWAKEUP_EN;
pmu_raw_writel(core_conf,
EXYNOS_ARM_CORE_CONFIGURATION(cpu));
}
@@ -130,7 +180,6 @@ void exynos_cpu_power_up(int cpu)
/**
* exynos_cpu_power_state : returns the power state of the cpu
* @cpu : the cpu to retrieve the power state from
- *
*/
int exynos_cpu_power_state(int cpu)
{
@@ -189,39 +238,76 @@ int exynos_scu_enable(void)
return 0;
}
-static void __iomem *cpu_boot_reg_base(void)
+static inline void __iomem *exynos_cpu_boot_reg(int cpu)
+{
+ if (cpu_info && cpu_info->cpu_boot_reg)
+ return cpu_info->cpu_boot_reg(cpu);
+ return NULL;
+}
+
+static void __iomem *exynos_common_cpu_boot_reg(u32 cpu)
{
- if (soc_is_exynos4210() && samsung_rev() == EXYNOS4210_REV_1_1)
- return pmu_base_addr + S5P_INFORM5;
+ if (!sysram_base_addr)
+ return IOMEM_ERR_PTR(-ENODEV);
+
return sysram_base_addr;
}
-static inline void __iomem *cpu_boot_reg(int cpu)
+static void __iomem *exynos4210_cpu_boot_reg(u32 cpu)
{
void __iomem *boot_reg;
- boot_reg = cpu_boot_reg_base();
- if (!boot_reg)
+ if (!pmu_base_addr)
return IOMEM_ERR_PTR(-ENODEV);
- if (soc_is_exynos4412())
- boot_reg += 4*cpu;
- else if (soc_is_exynos5420() || soc_is_exynos5800())
- boot_reg += 4;
+ boot_reg = pmu_base_addr + S5P_INFORM5;
+
+ return boot_reg;
+}
+
+static void __iomem *exynos4412_cpu_boot_reg(u32 cpu)
+{
+ void __iomem *boot_reg;
+
+ if (!sysram_base_addr)
+ return IOMEM_ERR_PTR(-ENODEV);
+
+ boot_reg = sysram_base_addr;
+ boot_reg += 4*cpu;
+
+ return boot_reg;
+}
+
+static void __iomem *exynos5420_cpu_boot_reg(u32 cpu)
+{
+ void __iomem *boot_reg;
+
+ if (!sysram_base_addr)
+ return IOMEM_ERR_PTR(-ENODEV);
+
+ boot_reg = sysram_base_addr;
+ boot_reg += 4;
+
return boot_reg;
}
+/**
+ * exynos_core_restart : restart the specified cpu
+ * @core_id : the cpu to be restarted
+ */
+void exynos_core_restart(u32 core_id)
+{
+ if (cpu_info && cpu_info->cpu_restart)
+ cpu_info->cpu_restart(core_id);
+}
+
/*
* Set wake up by local power mode and execute software reset for given core.
- *
* Currently this is needed only when booting secondary CPU on Exynos3250.
*/
-void exynos_core_restart(u32 core_id)
+static void exynos3250_core_restart(u32 core_id)
{
u32 val;
- if (!of_machine_is_compatible("samsung,exynos3250"))
- return;
-
while (!pmu_raw_readl(S5P_PMU_SPARE2))
udelay(10);
udelay(10);
@@ -274,7 +360,7 @@ int exynos_set_boot_addr(u32 core_id, unsigned long boot_addr)
if (ret && ret != -ENOSYS)
goto fail;
if (ret == -ENOSYS) {
- void __iomem *boot_reg = cpu_boot_reg(core_id);
+ void __iomem *boot_reg = exynos_cpu_boot_reg(core_id);
if (IS_ERR(boot_reg)) {
ret = PTR_ERR(boot_reg);
@@ -299,7 +385,7 @@ int exynos_get_boot_addr(u32 core_id, unsigned long *boot_addr)
if (ret && ret != -ENOSYS)
goto fail;
if (ret == -ENOSYS) {
- void __iomem *boot_reg = cpu_boot_reg(core_id);
+ void __iomem *boot_reg = exynos_cpu_boot_reg(core_id);
if (IS_ERR(boot_reg)) {
ret = PTR_ERR(boot_reg);
@@ -312,13 +398,93 @@ int exynos_get_boot_addr(u32 core_id, unsigned long *boot_addr)
return ret;
}
+static const struct exynos_cpu_info exynos3250_cpu_info = {
+ .cpu_boot_reg = exynos_common_cpu_boot_reg,
+ .cpu_power_down = exynos_common_cpu_power_down,
+ .cpu_power_up = exynos3250_cpu_power_up,
+ .cpu_restart = exynos3250_core_restart,
+};
+
+static const struct exynos_cpu_info exynos5420_cpu_info = {
+ .cpu_boot_reg = exynos5420_cpu_boot_reg,
+ .cpu_power_down = exynos5420_cpu_power_down,
+ .cpu_power_up = exynos_common_cpu_power_up,
+};
+
+static const struct exynos_cpu_info exynos4210_cpu_info = {
+ .cpu_boot_reg = exynos4210_cpu_boot_reg,
+ .cpu_power_down = exynos_common_cpu_power_down,
+ .cpu_power_up = exynos_common_cpu_power_up,
+};
+
+static const struct exynos_cpu_info exynos4412_cpu_info = {
+ .cpu_boot_reg = exynos4412_cpu_boot_reg,
+ .cpu_power_down = exynos_common_cpu_power_down,
+ .cpu_power_up = exynos_common_cpu_power_up,
+};
+
+static const struct exynos_cpu_info exynos_common_cpu_info = {
+ .cpu_boot_reg = exynos_common_cpu_boot_reg,
+ .cpu_power_down = exynos_common_cpu_power_down,
+ .cpu_power_up = exynos_common_cpu_power_up,
+};
+
+static const struct soc_device_attribute exynos_soc_revision[] __initconst = {
+ {
+ .soc_id = "EXYNOS4210", .revision = "11",
+ .data = &exynos4210_cpu_info
+ }, {
+ .soc_id = "EXYNOS4210", .revision = "10",
+ .data = &exynos_common_cpu_info
+ }
+};
+
+static const struct of_device_id exynos_pmu_of_device_ids[] __initconst = {
+ {
+ .compatible = "samsung,exynos3250",
+ .data = &exynos3250_cpu_info
+ }, {
+ .compatible = "samsung,exynos4212",
+ .data = &exynos_common_cpu_info
+ }, {
+ .compatible = "samsung,exynos4412",
+ .data = &exynos4412_cpu_info
+ }, {
+ .compatible = "samsung,exynos5250",
+ .data = &exynos_common_cpu_info
+ }, {
+ .compatible = "samsung,exynos5260",
+ .data = &exynos_common_cpu_info
+ }, {
+ .compatible = "samsung,exynos5410",
+ .data = &exynos_common_cpu_info
+ }, {
+ .compatible = "samsung,exynos5420",
+ .data = &exynos5420_cpu_info
+ }, {
+ .compatible = "samsung,exynos5440",
+ .data = &exynos_common_cpu_info
+ }, {
+ .compatible = "samsung,exynos5800",
+ .data = &exynos5420_cpu_info
+ },
+ { /*sentinel*/ },
+};
+
static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
{
unsigned long timeout;
+ const struct soc_device_attribute *match;
u32 mpidr = cpu_logical_map(cpu);
u32 core_id = MPIDR_AFFINITY_LEVEL(mpidr, 0);
int ret = -ENOSYS;
+ if (of_machine_is_compatible("samsung,exynos4210")) {
+ match = soc_device_match(exynos_soc_revision);
+ if (match)
+ cpu_info = (const struct exynos_cpu_info *) match->data;
+ }
+
/*
* Set synchronisation state between this boot processor
* and the secondary one
@@ -377,7 +543,7 @@ static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
call_firmware_op(cpu_boot, core_id);
- if (soc_is_exynos3250())
+ if (of_machine_is_compatible("samsung,exynos3250"))
dsb_sev();
else
arch_send_wakeup_ipi_mask(cpumask_of(cpu));
@@ -403,6 +569,16 @@ static int exynos_boot_secondary(unsigned int cpu, struct task_struct *idle)
static void __init exynos_smp_prepare_cpus(unsigned int max_cpus)
{
+ const struct of_device_id *match;
+ struct device_node *np;
+
+ np = of_find_matching_node_and_match(NULL,
+ exynos_pmu_of_device_ids, &match);
+ if (!np)
+ pr_err("failed to find supported CPU\n");
+ else
+ cpu_info = (const struct exynos_cpu_info *) match->data;
+
exynos_sysram_init();
exynos_set_delayed_reset_assertion(true);
--
2.7.4
^ permalink raw reply related
* [PATCH v8 8/8] ARM: EXYNOS: refactor of mach-exynos to use chipid information
From: Pankaj Dubey @ 2016-12-10 13:08 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1481375323-29724-1-git-send-email-pankaj.dubey@samsung.com>
Since now we have chipid driver in place and all dependencies of
soc_is_exynosMMMM macros have been address, lets remove all such
macros. Also remove static mapping of chipid SFR in exynos.c and
related helper functions.
Signed-off-by: Pankaj Dubey <pankaj.dubey@samsung.com>
---
arch/arm/mach-exynos/common.h | 92 ----------------------------
arch/arm/mach-exynos/exynos.c | 38 ------------
arch/arm/mach-exynos/include/mach/map.h | 21 -------
arch/arm/plat-samsung/cpu.c | 14 -----
arch/arm/plat-samsung/include/plat/cpu.h | 2 -
arch/arm/plat-samsung/include/plat/map-s5p.h | 2 -
6 files changed, 169 deletions(-)
delete mode 100644 arch/arm/mach-exynos/include/mach/map.h
diff --git a/arch/arm/mach-exynos/common.h b/arch/arm/mach-exynos/common.h
index cfd55ba..5886646 100644
--- a/arch/arm/mach-exynos/common.h
+++ b/arch/arm/mach-exynos/common.h
@@ -14,97 +14,6 @@
#include <linux/platform_data/cpuidle-exynos.h>
-#define EXYNOS3250_SOC_ID 0xE3472000
-#define EXYNOS3_SOC_MASK 0xFFFFF000
-
-#define EXYNOS4210_CPU_ID 0x43210000
-#define EXYNOS4212_CPU_ID 0x43220000
-#define EXYNOS4412_CPU_ID 0xE4412200
-#define EXYNOS4_CPU_MASK 0xFFFE0000
-
-#define EXYNOS5250_SOC_ID 0x43520000
-#define EXYNOS5410_SOC_ID 0xE5410000
-#define EXYNOS5420_SOC_ID 0xE5420000
-#define EXYNOS5440_SOC_ID 0xE5440000
-#define EXYNOS5800_SOC_ID 0xE5422000
-#define EXYNOS5_SOC_MASK 0xFFFFF000
-
-extern unsigned long samsung_cpu_id;
-
-#define IS_SAMSUNG_CPU(name, id, mask) \
-static inline int is_samsung_##name(void) \
-{ \
- return ((samsung_cpu_id & mask) == (id & mask)); \
-}
-
-IS_SAMSUNG_CPU(exynos3250, EXYNOS3250_SOC_ID, EXYNOS3_SOC_MASK)
-IS_SAMSUNG_CPU(exynos4210, EXYNOS4210_CPU_ID, EXYNOS4_CPU_MASK)
-IS_SAMSUNG_CPU(exynos4212, EXYNOS4212_CPU_ID, EXYNOS4_CPU_MASK)
-IS_SAMSUNG_CPU(exynos4412, EXYNOS4412_CPU_ID, EXYNOS4_CPU_MASK)
-IS_SAMSUNG_CPU(exynos5250, EXYNOS5250_SOC_ID, EXYNOS5_SOC_MASK)
-IS_SAMSUNG_CPU(exynos5410, EXYNOS5410_SOC_ID, EXYNOS5_SOC_MASK)
-IS_SAMSUNG_CPU(exynos5420, EXYNOS5420_SOC_ID, EXYNOS5_SOC_MASK)
-IS_SAMSUNG_CPU(exynos5440, EXYNOS5440_SOC_ID, EXYNOS5_SOC_MASK)
-IS_SAMSUNG_CPU(exynos5800, EXYNOS5800_SOC_ID, EXYNOS5_SOC_MASK)
-
-#if defined(CONFIG_SOC_EXYNOS3250)
-# define soc_is_exynos3250() is_samsung_exynos3250()
-#else
-# define soc_is_exynos3250() 0
-#endif
-
-#if defined(CONFIG_CPU_EXYNOS4210)
-# define soc_is_exynos4210() is_samsung_exynos4210()
-#else
-# define soc_is_exynos4210() 0
-#endif
-
-#if defined(CONFIG_SOC_EXYNOS4212)
-# define soc_is_exynos4212() is_samsung_exynos4212()
-#else
-# define soc_is_exynos4212() 0
-#endif
-
-#if defined(CONFIG_SOC_EXYNOS4412)
-# define soc_is_exynos4412() is_samsung_exynos4412()
-#else
-# define soc_is_exynos4412() 0
-#endif
-
-#define EXYNOS4210_REV_0 (0x0)
-#define EXYNOS4210_REV_1_0 (0x10)
-#define EXYNOS4210_REV_1_1 (0x11)
-
-#if defined(CONFIG_SOC_EXYNOS5250)
-# define soc_is_exynos5250() is_samsung_exynos5250()
-#else
-# define soc_is_exynos5250() 0
-#endif
-
-#if defined(CONFIG_SOC_EXYNOS5410)
-# define soc_is_exynos5410() is_samsung_exynos5410()
-#else
-# define soc_is_exynos5410() 0
-#endif
-
-#if defined(CONFIG_SOC_EXYNOS5420)
-# define soc_is_exynos5420() is_samsung_exynos5420()
-#else
-# define soc_is_exynos5420() 0
-#endif
-
-#if defined(CONFIG_SOC_EXYNOS5440)
-# define soc_is_exynos5440() is_samsung_exynos5440()
-#else
-# define soc_is_exynos5440() 0
-#endif
-
-#if defined(CONFIG_SOC_EXYNOS5800)
-# define soc_is_exynos5800() is_samsung_exynos5800()
-#else
-# define soc_is_exynos5800() 0
-#endif
-
extern u32 cp15_save_diag;
extern u32 cp15_save_power;
@@ -156,7 +65,6 @@ extern struct cpuidle_exynos_data cpuidle_coupled_exynos_data;
extern void exynos_set_delayed_reset_assertion(bool enable);
-extern unsigned int samsung_rev(void);
extern void exynos_core_restart(u32 core_id);
extern int exynos_set_boot_addr(u32 core_id, unsigned long boot_addr);
extern int exynos_get_boot_addr(u32 core_id, unsigned long *boot_addr);
diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c
index 040ea66..66bd612 100644
--- a/arch/arm/mach-exynos/exynos.c
+++ b/arch/arm/mach-exynos/exynos.c
@@ -21,10 +21,6 @@
#include <asm/cacheflush.h>
#include <asm/hardware/cache-l2x0.h>
#include <asm/mach/arch.h>
-#include <asm/mach/map.h>
-
-#include <mach/map.h>
-#include <plat/cpu.h>
#include "common.h"
@@ -58,39 +54,6 @@ void __init exynos_sysram_init(void)
}
}
-static int __init exynos_fdt_map_chipid(unsigned long node, const char *uname,
- int depth, void *data)
-{
- struct map_desc iodesc;
- const __be32 *reg;
- int len;
-
- if (!of_flat_dt_is_compatible(node, "samsung,exynos4210-chipid") &&
- !of_flat_dt_is_compatible(node, "samsung,exynos5440-clock"))
- return 0;
-
- reg = of_get_flat_dt_prop(node, "reg", &len);
- if (reg == NULL || len != (sizeof(unsigned long) * 2))
- return 0;
-
- iodesc.pfn = __phys_to_pfn(be32_to_cpu(reg[0]));
- iodesc.length = be32_to_cpu(reg[1]) - 1;
- iodesc.virtual = (unsigned long)S5P_VA_CHIPID;
- iodesc.type = MT_DEVICE;
- iotable_init(&iodesc, 1);
- return 1;
-}
-
-static void __init exynos_init_io(void)
-{
- debug_ll_io_init();
-
- of_scan_flat_dt(exynos_fdt_map_chipid, NULL);
-
- /* detect cpu id and rev. */
- s5p_init_cpu(S5P_VA_CHIPID);
-}
-
/*
* Set or clear the USE_DELAYED_RESET_ASSERTION option. Used by smp code
* and suspend.
@@ -203,7 +166,6 @@ DT_MACHINE_START(EXYNOS_DT, "SAMSUNG EXYNOS (Flattened Device Tree)")
.l2c_aux_val = 0x3c400001,
.l2c_aux_mask = 0xc20fffff,
.smp = smp_ops(exynos_smp_ops),
- .map_io = exynos_init_io,
.init_early = exynos_firmware_init,
.init_irq = exynos_init_irq,
.init_machine = exynos_dt_machine_init,
diff --git a/arch/arm/mach-exynos/include/mach/map.h b/arch/arm/mach-exynos/include/mach/map.h
deleted file mode 100644
index 0eef407..0000000
--- a/arch/arm/mach-exynos/include/mach/map.h
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * Copyright (c) 2010-2011 Samsung Electronics Co., Ltd.
- * http://www.samsung.com/
- *
- * EXYNOS - Memory map definitions
- *
- * 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.
-*/
-
-#ifndef __ASM_ARCH_MAP_H
-#define __ASM_ARCH_MAP_H __FILE__
-
-#include <plat/map-base.h>
-
-#include <plat/map-s5p.h>
-
-#define EXYNOS_PA_CHIPID 0x10000000
-
-#endif /* __ASM_ARCH_MAP_H */
diff --git a/arch/arm/plat-samsung/cpu.c b/arch/arm/plat-samsung/cpu.c
index a107b3a..e58f0f6 100644
--- a/arch/arm/plat-samsung/cpu.c
+++ b/arch/arm/plat-samsung/cpu.c
@@ -21,12 +21,6 @@
unsigned long samsung_cpu_id;
static unsigned int samsung_cpu_rev;
-unsigned int samsung_rev(void)
-{
- return samsung_cpu_rev;
-}
-EXPORT_SYMBOL(samsung_rev);
-
void __init s3c64xx_init_cpu(void)
{
samsung_cpu_id = readl_relaxed(S3C_VA_SYS + 0x118);
@@ -43,11 +37,3 @@ void __init s3c64xx_init_cpu(void)
pr_info("Samsung CPU ID: 0x%08lx\n", samsung_cpu_id);
}
-
-void __init s5p_init_cpu(const void __iomem *cpuid_addr)
-{
- samsung_cpu_id = readl_relaxed(cpuid_addr);
- samsung_cpu_rev = samsung_cpu_id & 0xFF;
-
- pr_info("Samsung CPU ID: 0x%08lx\n", samsung_cpu_id);
-}
diff --git a/arch/arm/plat-samsung/include/plat/cpu.h b/arch/arm/plat-samsung/include/plat/cpu.h
index b7b702a..913c176 100644
--- a/arch/arm/plat-samsung/include/plat/cpu.h
+++ b/arch/arm/plat-samsung/include/plat/cpu.h
@@ -115,8 +115,6 @@ extern void s3c24xx_init_io(struct map_desc *mach_desc, int size);
extern void s3c64xx_init_cpu(void);
extern void s5p_init_cpu(const void __iomem *cpuid_addr);
-extern unsigned int samsung_rev(void);
-
extern void s3c24xx_init_uarts(struct s3c2410_uartcfg *cfg, int no);
extern void s3c24xx_init_clocks(int xtal);
diff --git a/arch/arm/plat-samsung/include/plat/map-s5p.h b/arch/arm/plat-samsung/include/plat/map-s5p.h
index 512ed1f..d6853f1 100644
--- a/arch/arm/plat-samsung/include/plat/map-s5p.h
+++ b/arch/arm/plat-samsung/include/plat/map-s5p.h
@@ -13,8 +13,6 @@
#ifndef __ASM_PLAT_MAP_S5P_H
#define __ASM_PLAT_MAP_S5P_H __FILE__
-#define S5P_VA_CHIPID S3C_ADDR(0x02000000)
-
#define VA_VIC(x) (S3C_VA_IRQ + ((x) * 0x10000))
#define VA_VIC0 VA_VIC(0)
#define VA_VIC1 VA_VIC(1)
--
2.7.4
^ permalink raw reply related
* [PATCH 1/7] ARM: EXYNOS: Constify list of retention registers
From: Krzysztof Kozlowski @ 2016-12-10 13:47 UTC (permalink / raw)
To: linux-arm-kernel
The list of retention registers (release_ret_regs field of struct
exynos_pm_data and arrays with values) are not modified and can be made
const to improve the const safeness.
Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
---
arch/arm/mach-exynos/suspend.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/arm/mach-exynos/suspend.c b/arch/arm/mach-exynos/suspend.c
index 73df9f3ffbf7..d0e6ac7938f3 100644
--- a/arch/arm/mach-exynos/suspend.c
+++ b/arch/arm/mach-exynos/suspend.c
@@ -55,7 +55,7 @@ struct exynos_wkup_irq {
struct exynos_pm_data {
const struct exynos_wkup_irq *wkup_irq;
unsigned int wake_disable_mask;
- unsigned int *release_ret_regs;
+ const unsigned int *release_ret_regs;
void (*pm_prepare)(void);
void (*pm_resume_prepare)(void);
@@ -93,7 +93,7 @@ static const struct exynos_wkup_irq exynos5250_wkup_irq[] = {
{ /* sentinel */ },
};
-static unsigned int exynos_release_ret_regs[] = {
+static const unsigned int exynos_release_ret_regs[] = {
S5P_PAD_RET_MAUDIO_OPTION,
S5P_PAD_RET_GPIO_OPTION,
S5P_PAD_RET_UART_OPTION,
@@ -104,7 +104,7 @@ static unsigned int exynos_release_ret_regs[] = {
REG_TABLE_END,
};
-static unsigned int exynos3250_release_ret_regs[] = {
+static const unsigned int exynos3250_release_ret_regs[] = {
S5P_PAD_RET_MAUDIO_OPTION,
S5P_PAD_RET_GPIO_OPTION,
S5P_PAD_RET_UART_OPTION,
@@ -117,7 +117,7 @@ static unsigned int exynos3250_release_ret_regs[] = {
REG_TABLE_END,
};
-static unsigned int exynos5420_release_ret_regs[] = {
+static const unsigned int exynos5420_release_ret_regs[] = {
EXYNOS_PAD_RET_DRAM_OPTION,
EXYNOS_PAD_RET_MAUDIO_OPTION,
EXYNOS_PAD_RET_JTAG_OPTION,
--
2.7.4
^ permalink raw reply related
* [PATCH 2/7] ARM: EXYNOS: Annotate iomem and pm_data pointers __ro_after_init
From: Krzysztof Kozlowski @ 2016-12-10 13:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1481377658-22603-1-git-send-email-krzk@kernel.org>
The pointers to __iomem sysram and exynos_pm_data are set only during
initcalls. Later the pointers itself are used only in read-only way so
we can mark them __ro_after_init to increase code safeness.
Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
---
arch/arm/mach-exynos/exynos.c | 4 ++--
arch/arm/mach-exynos/mcpm-exynos.c | 2 +-
arch/arm/mach-exynos/suspend.c | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/arch/arm/mach-exynos/exynos.c b/arch/arm/mach-exynos/exynos.c
index fa08ef99b4ad..a863f8ec0f7a 100644
--- a/arch/arm/mach-exynos/exynos.c
+++ b/arch/arm/mach-exynos/exynos.c
@@ -36,8 +36,8 @@ static struct platform_device exynos_cpuidle = {
.id = -1,
};
-void __iomem *sysram_base_addr;
-void __iomem *sysram_ns_base_addr;
+void __iomem *sysram_base_addr __ro_after_init;
+void __iomem *sysram_ns_base_addr __ro_after_init;
void __init exynos_sysram_init(void)
{
diff --git a/arch/arm/mach-exynos/mcpm-exynos.c b/arch/arm/mach-exynos/mcpm-exynos.c
index f086bf615b29..038fd8c993d0 100644
--- a/arch/arm/mach-exynos/mcpm-exynos.c
+++ b/arch/arm/mach-exynos/mcpm-exynos.c
@@ -32,7 +32,7 @@
#define EXYNOS5420_USE_ARM_CORE_DOWN_STATE BIT(29)
#define EXYNOS5420_USE_L2_COMMON_UP_STATE BIT(30)
-static void __iomem *ns_sram_base_addr;
+static void __iomem *ns_sram_base_addr __ro_after_init;
/*
* The common v7_exit_coherency_flush API could not be used because of the
diff --git a/arch/arm/mach-exynos/suspend.c b/arch/arm/mach-exynos/suspend.c
index d0e6ac7938f3..339fe011d658 100644
--- a/arch/arm/mach-exynos/suspend.c
+++ b/arch/arm/mach-exynos/suspend.c
@@ -64,7 +64,7 @@ struct exynos_pm_data {
int (*cpu_suspend)(unsigned long);
};
-static const struct exynos_pm_data *pm_data;
+static const struct exynos_pm_data *pm_data __ro_after_init;
static int exynos5420_cpu_state;
static unsigned int exynos_pmu_spare3;
--
2.7.4
^ permalink raw reply related
* [PATCH 3/7] ARM: s3c24xx: Constify few integer tables
From: Krzysztof Kozlowski @ 2016-12-10 13:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1481377658-22603-1-git-send-email-krzk@kernel.org>
These arrays are not modified so they can be made const.
Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
---
arch/arm/mach-s3c24xx/bast-irq.c | 4 ++--
arch/arm/mach-s3c24xx/iotiming-s3c2410.c | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/arch/arm/mach-s3c24xx/bast-irq.c b/arch/arm/mach-s3c24xx/bast-irq.c
index 2bb08961e934..ad8f4cd7c327 100644
--- a/arch/arm/mach-s3c24xx/bast-irq.c
+++ b/arch/arm/mach-s3c24xx/bast-irq.c
@@ -44,7 +44,7 @@
/* table of ISA irq nos to the relevant mask... zero means
* the irq is not implemented
*/
-static unsigned char bast_pc104_irqmasks[] = {
+static const unsigned char bast_pc104_irqmasks[] = {
0, /* 0 */
0, /* 1 */
0, /* 2 */
@@ -63,7 +63,7 @@ static unsigned char bast_pc104_irqmasks[] = {
0, /* 15 */
};
-static unsigned char bast_pc104_irqs[] = { 3, 5, 7, 10 };
+static const unsigned char bast_pc104_irqs[] = { 3, 5, 7, 10 };
static void
bast_pc104_mask(struct irq_data *data)
diff --git a/arch/arm/mach-s3c24xx/iotiming-s3c2410.c b/arch/arm/mach-s3c24xx/iotiming-s3c2410.c
index 65e5f9cb650f..b7970f1fa3d5 100644
--- a/arch/arm/mach-s3c24xx/iotiming-s3c2410.c
+++ b/arch/arm/mach-s3c24xx/iotiming-s3c2410.c
@@ -249,7 +249,7 @@ static int s3c2410_calc_bank(struct s3c_cpufreq_config *cfg,
return 0;
}
-static unsigned int tacc_tab[] = {
+static const unsigned int tacc_tab[] = {
[0] = 1,
[1] = 2,
[2] = 3,
--
2.7.4
^ permalink raw reply related
* [RFC 4/7] ARM: s3c64xx: Annotate external clock frequencies __ro_after_init
From: Krzysztof Kozlowski @ 2016-12-10 13:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1481377658-22603-1-git-send-email-krzk@kernel.org>
The xtal_f and xusbxti_f static variables are modified only through
__init accessors (like s3c64xx_set_xtal_freq()). Later these variables
are used only in read-only way so we can mark them __ro_after_init to
increase code safeness.
Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
---
Looks safe, but not tested.
---
arch/arm/mach-s3c64xx/common.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mach-s3c64xx/common.c b/arch/arm/mach-s3c64xx/common.c
index 7c66ce1a6bb6..9843eb4dd04e 100644
--- a/arch/arm/mach-s3c64xx/common.c
+++ b/arch/arm/mach-s3c64xx/common.c
@@ -56,7 +56,8 @@
#include "watchdog-reset.h"
/* External clock frequency */
-static unsigned long xtal_f = 12000000, xusbxti_f = 48000000;
+static unsigned long xtal_f __ro_after_init = 12000000;
+static unsigned long xusbxti_f __ro_after_init = 48000000;
void __init s3c64xx_set_xtal_freq(unsigned long freq)
{
--
2.7.4
^ permalink raw reply related
* [PATCH 5/7] ARM: SAMSUNG: Constify array of wake irqs passed to samsung_sync_wakemask
From: Krzysztof Kozlowski @ 2016-12-10 13:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1481377658-22603-1-git-send-email-krzk@kernel.org>
The samsung_sync_wakemask() iterates over passed array of wake irqs but
does not modify it.
Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
---
arch/arm/plat-samsung/include/plat/wakeup-mask.h | 2 +-
arch/arm/plat-samsung/wakeup-mask.c | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/arch/arm/plat-samsung/include/plat/wakeup-mask.h b/arch/arm/plat-samsung/include/plat/wakeup-mask.h
index 43e4acd2e1c6..bbfa84b0505a 100644
--- a/arch/arm/plat-samsung/include/plat/wakeup-mask.h
+++ b/arch/arm/plat-samsung/include/plat/wakeup-mask.h
@@ -38,7 +38,7 @@ struct samsung_wakeup_mask {
* required to be correct before we enter sleep.
*/
extern void samsung_sync_wakemask(void __iomem *reg,
- struct samsung_wakeup_mask *masks,
+ const struct samsung_wakeup_mask *masks,
int nr_masks);
#endif /* __PLAT_WAKEUP_MASK_H */
diff --git a/arch/arm/plat-samsung/wakeup-mask.c b/arch/arm/plat-samsung/wakeup-mask.c
index 20c3d9117cc2..b9de6b543330 100644
--- a/arch/arm/plat-samsung/wakeup-mask.c
+++ b/arch/arm/plat-samsung/wakeup-mask.c
@@ -20,7 +20,7 @@
#include <plat/pm.h>
void samsung_sync_wakemask(void __iomem *reg,
- struct samsung_wakeup_mask *mask, int nr_mask)
+ const struct samsung_wakeup_mask *mask, int nr_mask)
{
struct irq_data *data;
u32 val;
--
2.7.4
^ permalink raw reply related
* [PATCH 6/7] ARM: s3c24xx: Constify wake_irqs
From: Krzysztof Kozlowski @ 2016-12-10 13:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1481377658-22603-1-git-send-email-krzk@kernel.org>
samsung_sync_wakemask() accepts pointer to const data so wake_irqs can
be made const to increase safeness.
Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
---
arch/arm/mach-s3c24xx/pm-s3c2412.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/mach-s3c24xx/pm-s3c2412.c b/arch/arm/mach-s3c24xx/pm-s3c2412.c
index d75f95e487ee..0ae4d47a4663 100644
--- a/arch/arm/mach-s3c24xx/pm-s3c2412.c
+++ b/arch/arm/mach-s3c24xx/pm-s3c2412.c
@@ -53,7 +53,7 @@ static int s3c2412_cpu_suspend(unsigned long arg)
}
/* mapping of interrupts to parts of the wakeup mask */
-static struct samsung_wakeup_mask wake_irqs[] = {
+static const struct samsung_wakeup_mask wake_irqs[] = {
{ .irq = IRQ_RTC, .bit = S3C2412_PWRCFG_RTC_MASKIRQ, },
};
--
2.7.4
^ permalink raw reply related
* [PATCH 7/7] ARM: s3c64xx: Constify wake_irqs
From: Krzysztof Kozlowski @ 2016-12-10 13:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1481377658-22603-1-git-send-email-krzk@kernel.org>
samsung_sync_wakemask() accepts pointer to const data so wake_irqs can
be made const to increase safeness.
Signed-off-by: Krzysztof Kozlowski <krzk@kernel.org>
---
arch/arm/mach-s3c64xx/pm.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/mach-s3c64xx/pm.c b/arch/arm/mach-s3c64xx/pm.c
index 59d91b83b03d..b0be382ff6bb 100644
--- a/arch/arm/mach-s3c64xx/pm.c
+++ b/arch/arm/mach-s3c64xx/pm.c
@@ -285,7 +285,7 @@ static int s3c64xx_cpu_suspend(unsigned long arg)
}
/* mapping of interrupts to parts of the wakeup mask */
-static struct samsung_wakeup_mask wake_irqs[] = {
+static const struct samsung_wakeup_mask wake_irqs[] = {
{ .irq = IRQ_RTC_ALARM, .bit = S3C64XX_PWRCFG_RTC_ALARM_DISABLE, },
{ .irq = IRQ_RTC_TIC, .bit = S3C64XX_PWRCFG_RTC_TICK_DISABLE, },
{ .irq = IRQ_PENDN, .bit = S3C64XX_PWRCFG_TS_DISABLE, },
--
2.7.4
^ permalink raw reply related
* [PATCH 3/4] iommu/arm-smmu: Disable stalling faults for all endpoints
From: Sricharan @ 2016-12-10 15:44 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161207000025.GB21862@jcrouse-lnx.qualcomm.com>
Hi Rob,
>> > Although it's not really Linux's job to save hardware integrators from
>> > their own misfortune, it *is* our job to stop userspace (e.g. VFIO
>> > clients) from hosing the system for everybody else, even if they might
>> > already be required to have elevated privileges.
>> >
>> > Given that the fault handling code currently executes entirely in IRQ
>> > context, there is nothing that can sensibly be done to recover from
>> > things like page faults anyway, so let's rip this code out for now and
>> > avoid the potential for deadlock.
>>
>> Hi Will,
>>
>> so, I'd like to re-introduce this feature, I *guess* as some sort of
>> opt-in quirk (ie. disabled by default unless something in DT tells you
>> otherwise?? But I'm open to suggestions. I'm not entirely sure what
>> hw was having problems due to this feature.)
>>
>> On newer snapdragon devices we are using arm-smmu for the GPU, and
>> halting the GPU so the driver's fault handler can dump some GPU state
>> on faults is enormously helpful for debugging and tracking down where
>> in the gpu cmdstream the fault was triggered. In addition, we will
>> eventually want the ability to update pagetables from fault handler
>> and resuming the faulting transition.
>>
>> Some additional comments below..
>>
>> > Cc: <stable@vger.kernel.org>
>> > Reported-by: Matt Evans <matt.evans@arm.com>
>> > Signed-off-by: Will Deacon <will.deacon@arm.com>
>> > ---
>> > drivers/iommu/arm-smmu.c | 34 +++++++---------------------------
>> > 1 file changed, 7 insertions(+), 27 deletions(-)
>> >
>> > diff --git a/drivers/iommu/arm-smmu.c b/drivers/iommu/arm-smmu.c
>> > index 4f49fe29f202..2db74ebc3240 100644
>> > --- a/drivers/iommu/arm-smmu.c
>> > +++ b/drivers/iommu/arm-smmu.c
>> > @@ -686,8 +686,7 @@ static struct iommu_gather_ops arm_smmu_gather_ops = {
>> >
>> > static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
>> > {
>> > - int flags, ret;
>> > - u32 fsr, fsynr, resume;
>> > + u32 fsr, fsynr;
>> > unsigned long iova;
>> > struct iommu_domain *domain = dev;
>> > struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain);
>> > @@ -701,34 +700,15 @@ static irqreturn_t arm_smmu_context_fault(int irq, void *dev)
>> > if (!(fsr & FSR_FAULT))
>> > return IRQ_NONE;
>> >
>> > - if (fsr & FSR_IGN)
>> > - dev_err_ratelimited(smmu->dev,
>> > - "Unexpected context fault (fsr 0x%x)\n",
>> > - fsr);
>> > -
>> > fsynr = readl_relaxed(cb_base + ARM_SMMU_CB_FSYNR0);
>> > - flags = fsynr & FSYNR0_WNR ? IOMMU_FAULT_WRITE : IOMMU_FAULT_READ;
>> > -
>> > iova = readq_relaxed(cb_base + ARM_SMMU_CB_FAR);
>> > - if (!report_iommu_fault(domain, smmu->dev, iova, flags)) {
>> > - ret = IRQ_HANDLED;
>> > - resume = RESUME_RETRY;
>> > - } else {
>> > - dev_err_ratelimited(smmu->dev,
>> > - "Unhandled context fault: iova=0x%08lx, fsynr=0x%x, cb=%d\n",
>> > - iova, fsynr, cfg->cbndx);
>>
>> I would like to decouple this dev_err_ratelimit() print from the
>> RESUME_RETRY vs RESUME_TERMINATE behaviour. I need the ability to
>> indicate by return from my fault handler whether to resume or
>> terminate. But I already have my own ratelimted prints and would
>> prefer not to spam dmesg twice.
>>
>> I'm thinking about report_iommu_fault() returning:
>>
>> 0 => RESUME_RETRY
>> -EFAULT => RESUME_TERMINATE but don't print
>> anything else (or specifically -ENOSYS?) => RESUME_TERMINATE and print
>>
>> thoughts?
>>
>> > - ret = IRQ_NONE;
>> > - resume = RESUME_TERMINATE;
>> > - }
>> > -
>> > - /* Clear the faulting FSR */
>> > - writel(fsr, cb_base + ARM_SMMU_CB_FSR);
>> >
>> > - /* Retry or terminate any stalled transactions */
>> > - if (fsr & FSR_SS)
>> > - writel_relaxed(resume, cb_base + ARM_SMMU_CB_RESUME);
>>
>> This might be a bug in qcom's implementation of the smmu spec, but
>> seems like we don't have SS bit set, yet we still require RESUME reg
>> to be written, otherwise gpu is perma-wedged. Maybe topic for a
>> separate quirk? I'm not sure if writing RESUME reg on other hw when
>> SS bit is not set is likely to cause problems? If not I suppose we
>> could just unconditionally write it.
>>
>> Anyways, I'm not super-familiar w/ arm-smmu so suggestions welcome..
>> in between debugging freedreno I'll try to put together some patches.
>
>From what I can tell we need SCTLR_CFCFG to make the stall happen otherwise
>the operation just gets terminated immediately and *then* we get notification
>but by then the system keeps going.
>
Yes thats right, SCTLR_CFCFG was removed as a part of this patch.
>I think SCTLR_HUPCF helps control that behavior (i.e. we don't go off faulting
>through eternity) but I don't know how it works.
>
>From my very unlearned understanding I think we do want to set CFCFG and then
>stall and let the interrupt handler decide to retry/terminate.
Yes, infact i was thinking of adding this as a new patch, but now that this was
a reverted one. As you said, it would be good to know the hw which was having
problem with this and probably having this has a quirk otherwise.
Also i see that FSR_SS is implemented by the qcom and the
downstream code uses it.
Regards,
Sricharan
^ permalink raw reply
* [RFC v3 PATCH 00/25] Allow NOMMU for MULTIPLATFORM
From: Afzal Mohammed @ 2016-12-10 18:16 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <1480691143-19845-1-git-send-email-vladimir.murzin@arm.com>
Hi,
On Fri, Dec 02, 2016 at 03:05:18PM +0000, Vladimir Murzin wrote:
> there was little interest to run A-class with MMU disabled (or
> 1:1 MMU mapping) as well.
With a few more changes, on a Cortex-A platform, Kernel reached
maximum it can proceed w/o user space help, i.e. upto,
"Kernel panic - not syncing: No working init found."
Creating user space executables suitable for Cortex-A !MMU is the
distance to reach prompt.
Seems no !MMU A-class suitable toolchains are available, the existing
!MMU toolchains available are for either M or R class. Looks like
FDPIC one's also are so (cc'ing Maxime & Mickeal in case they have
some input here).
And compiling a suitable compiler failed.
Let know if any signposts to travel the remaining distance.
PS: Version used was v2 instead of this v3 series.
Regards
afzal
^ permalink raw reply
* [RFC v3 PATCH 00/25] Allow NOMMU for MULTIPLATFORM
From: Arnd Bergmann @ 2016-12-10 19:19 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161210181639.GA5660@afzalpc>
On Saturday, December 10, 2016 11:46:39 PM CET Afzal Mohammed wrote:
> Hi,
>
> On Fri, Dec 02, 2016 at 03:05:18PM +0000, Vladimir Murzin wrote:
>
> > there was little interest to run A-class with MMU disabled (or
> > 1:1 MMU mapping) as well.
>
> With a few more changes, on a Cortex-A platform, Kernel reached
> maximum it can proceed w/o user space help, i.e. upto,
>
> "Kernel panic - not syncing: No working init found."
>
> Creating user space executables suitable for Cortex-A !MMU is the
> distance to reach prompt.
>
> Seems no !MMU A-class suitable toolchains are available, the existing
> !MMU toolchains available are for either M or R class. Looks like
> FDPIC one's also are so (cc'ing Maxime & Mickeal in case they have
> some input here).
>
> And compiling a suitable compiler failed.
>
> Let know if any signposts to travel the remaining distance.
>
> PS: Version used was v2 instead of this v3 series.
What's wrong with the R class toolchain?
Arnd
^ permalink raw reply
* [RFC PATCH 1/2] watchdog: imx2: fix hang-up on boot for i.MX21, i.MX27 and i.MX31 SoCs
From: Magnus Lilja @ 2016-12-10 19:28 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <ebece25b-7a3b-3fdf-d117-dd0a3a2975b7@roeck-us.net>
Hi,
On 26 September 2016 at 15:02, Guenter Roeck <linux@roeck-us.net> wrote:
> On 09/25/2016 05:39 PM, Vladimir Zapolskiy wrote:
>>
>> Power down counter enable/disable bit switch is located in WMCR
>> register, but watchdog controllers found on legacy i.MX21, i.MX27 and
>> i.MX31 SoCs don't have this register. As a result of writing data to
>> the non-existing register on driver probe the SoC hangs up, to fix the
>> problem add more OF compatible strings and on this basis get
>> information about availability of the WMCR register.
>>
>> Fixes: 5fe65ce7ccbb ("watchdog: imx2_wdt: Disable power down counter on
>> boot")
>> Signed-off-by: Vladimir Zapolskiy <vz@mleia.com>
>> ---
>> drivers/watchdog/imx2_wdt.c | 47
>> +++++++++++++++++++++++++++++++++++++++++++--
>> 1 file changed, 45 insertions(+), 2 deletions(-)
>>
>> diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c
>> index 62f346b..b6763e0 100644
>> --- a/drivers/watchdog/imx2_wdt.c
>> +++ b/drivers/watchdog/imx2_wdt.c
>> @@ -29,6 +29,7 @@
>> #include <linux/module.h>
>> #include <linux/moduleparam.h>
>> #include <linux/of_address.h>
>> +#include <linux/of_device.h>
>> #include <linux/platform_device.h>
>> #include <linux/regmap.h>
>> #include <linux/watchdog.h>
>> @@ -57,6 +58,10 @@
>>
>> #define WDOG_SEC_TO_COUNT(s) ((s * 2 - 1) << 8)
>>
>> +struct imx2_wdt_data {
>> + bool has_pdc;
>> +};
>> +
>> struct imx2_wdt_device {
>> struct clk *clk;
>> struct regmap *regmap;
>> @@ -64,6 +69,8 @@ struct imx2_wdt_device {
>> bool ext_reset;
>> };
>>
>> +static const struct of_device_id imx2_wdt_dt_ids[];
>> +
>> static bool nowayout = WATCHDOG_NOWAYOUT;
>> module_param(nowayout, bool, 0);
>> MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started
>> (default="
>> @@ -200,10 +207,13 @@ static const struct regmap_config
>> imx2_wdt_regmap_config = {
>>
>> static int __init imx2_wdt_probe(struct platform_device *pdev)
>> {
>> + const struct of_device_id *of_id;
>> + const struct imx2_wdt_data *data;
>> struct imx2_wdt_device *wdev;
>> struct watchdog_device *wdog;
>> struct resource *res;
>> void __iomem *base;
>> + bool has_pdc;
>> int ret;
>> u32 val;
>>
>> @@ -261,12 +271,24 @@ static int __init imx2_wdt_probe(struct
>> platform_device *pdev)
>> set_bit(WDOG_HW_RUNNING, &wdog->status);
>> }
>>
>> + if (pdev->dev.of_node) {
>> + of_id = of_match_device(imx2_wdt_dt_ids, &pdev->dev);
>> + if (!of_id)
>> + return -ENODEV;
>> +
>> + data = of_id->data;
>> + has_pdc = data->has_pdc;
>> + } else {
>> + has_pdc = false;
>> + }
>> +
>> /*
>> * Disable the watchdog power down counter at boot. Otherwise the
>> power
>> * down counter will pull down the #WDOG interrupt line for one
>> clock
>> * cycle.
>> */
>> - regmap_write(wdev->regmap, IMX2_WDT_WMCR, 0);
>> + if (has_pdc)
>> + regmap_write(wdev->regmap, IMX2_WDT_WMCR, 0);
>>
>> ret = watchdog_register_device(wdog);
>> if (ret) {
>> @@ -363,8 +385,29 @@ static int imx2_wdt_resume(struct device *dev)
>> static SIMPLE_DEV_PM_OPS(imx2_wdt_pm_ops, imx2_wdt_suspend,
>> imx2_wdt_resume);
>>
>> +static const struct imx2_wdt_data imx21_wdt_data = {
>> + .has_pdc = false,
>> +};
>> +
>> +static const struct imx2_wdt_data imx25_wdt_data = {
>> + .has_pdc = true,
>> +};
>> +
>> static const struct of_device_id imx2_wdt_dt_ids[] = {
>> - { .compatible = "fsl,imx21-wdt", },
>> + { .compatible = "fsl,imx21-wdt", .data = &imx21_wdt_data },
>
>
> Please just specify the flag directly, as in
> .data = (void *) true
> or, if you prefer, use defines.
> .data = (void *) IMX_SUPPORTS_PDC
>
> Then, in above code:
> has_pdc = (bool) of_id->data;
>
> Guenter
Has this patch (or an updated one) been merged in any tree? I've
tested it on a i.MX31 board with positive result.
Regards, Magnus
^ permalink raw reply
* [PATCH] ARM: dts: vexpress: Support GICC_DIR operations
From: Christoffer Dall @ 2016-12-10 20:13 UTC (permalink / raw)
To: linux-arm-kernel
The GICv2 CPU interface registers span across 8K, not 4K as indicated in
the DT. Only the GICC_DIR register is located after the initial 4K
boundary, leaving a functional system but without support for separately
EOI'ing and deactivating interrupts.
After this change the system support split priority drop and interrupt
deactivation.
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
---
arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
index 0205c97..2e0cf39 100644
--- a/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
+++ b/arch/arm/boot/dts/vexpress-v2p-ca15_a7.dts
@@ -126,7 +126,7 @@
#address-cells = <0>;
interrupt-controller;
reg = <0 0x2c001000 0 0x1000>,
- <0 0x2c002000 0 0x1000>,
+ <0 0x2c002000 0 0x2000>,
<0 0x2c004000 0 0x2000>,
<0 0x2c006000 0 0x2000>;
interrupts = <1 9 0xf04>;
--
2.9.0
^ permalink raw reply related
* [RFC PATCH 1/2] watchdog: imx2: fix hang-up on boot for i.MX21, i.MX27 and i.MX31 SoCs
From: Guenter Roeck @ 2016-12-10 20:14 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <CAM=E1R6hzob5ZzyTLmrLL5s-6=vbZQbCig1sLHMSLChJEz9YgA@mail.gmail.com>
On 12/10/2016 11:28 AM, Magnus Lilja wrote:
> Hi,
>
> On 26 September 2016 at 15:02, Guenter Roeck <linux@roeck-us.net> wrote:
>> On 09/25/2016 05:39 PM, Vladimir Zapolskiy wrote:
>>>
>>> Power down counter enable/disable bit switch is located in WMCR
>>> register, but watchdog controllers found on legacy i.MX21, i.MX27 and
>>> i.MX31 SoCs don't have this register. As a result of writing data to
>>> the non-existing register on driver probe the SoC hangs up, to fix the
>>> problem add more OF compatible strings and on this basis get
>>> information about availability of the WMCR register.
>>>
>>> Fixes: 5fe65ce7ccbb ("watchdog: imx2_wdt: Disable power down counter on
>>> boot")
>>> Signed-off-by: Vladimir Zapolskiy <vz@mleia.com>
>>> ---
>>> drivers/watchdog/imx2_wdt.c | 47
>>> +++++++++++++++++++++++++++++++++++++++++++--
>>> 1 file changed, 45 insertions(+), 2 deletions(-)
>>>
>>> diff --git a/drivers/watchdog/imx2_wdt.c b/drivers/watchdog/imx2_wdt.c
>>> index 62f346b..b6763e0 100644
>>> --- a/drivers/watchdog/imx2_wdt.c
>>> +++ b/drivers/watchdog/imx2_wdt.c
>>> @@ -29,6 +29,7 @@
>>> #include <linux/module.h>
>>> #include <linux/moduleparam.h>
>>> #include <linux/of_address.h>
>>> +#include <linux/of_device.h>
>>> #include <linux/platform_device.h>
>>> #include <linux/regmap.h>
>>> #include <linux/watchdog.h>
>>> @@ -57,6 +58,10 @@
>>>
>>> #define WDOG_SEC_TO_COUNT(s) ((s * 2 - 1) << 8)
>>>
>>> +struct imx2_wdt_data {
>>> + bool has_pdc;
>>> +};
>>> +
>>> struct imx2_wdt_device {
>>> struct clk *clk;
>>> struct regmap *regmap;
>>> @@ -64,6 +69,8 @@ struct imx2_wdt_device {
>>> bool ext_reset;
>>> };
>>>
>>> +static const struct of_device_id imx2_wdt_dt_ids[];
>>> +
>>> static bool nowayout = WATCHDOG_NOWAYOUT;
>>> module_param(nowayout, bool, 0);
>>> MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started
>>> (default="
>>> @@ -200,10 +207,13 @@ static const struct regmap_config
>>> imx2_wdt_regmap_config = {
>>>
>>> static int __init imx2_wdt_probe(struct platform_device *pdev)
>>> {
>>> + const struct of_device_id *of_id;
>>> + const struct imx2_wdt_data *data;
>>> struct imx2_wdt_device *wdev;
>>> struct watchdog_device *wdog;
>>> struct resource *res;
>>> void __iomem *base;
>>> + bool has_pdc;
>>> int ret;
>>> u32 val;
>>>
>>> @@ -261,12 +271,24 @@ static int __init imx2_wdt_probe(struct
>>> platform_device *pdev)
>>> set_bit(WDOG_HW_RUNNING, &wdog->status);
>>> }
>>>
>>> + if (pdev->dev.of_node) {
>>> + of_id = of_match_device(imx2_wdt_dt_ids, &pdev->dev);
>>> + if (!of_id)
>>> + return -ENODEV;
>>> +
>>> + data = of_id->data;
>>> + has_pdc = data->has_pdc;
>>> + } else {
>>> + has_pdc = false;
>>> + }
>>> +
>>> /*
>>> * Disable the watchdog power down counter at boot. Otherwise the
>>> power
>>> * down counter will pull down the #WDOG interrupt line for one
>>> clock
>>> * cycle.
>>> */
>>> - regmap_write(wdev->regmap, IMX2_WDT_WMCR, 0);
>>> + if (has_pdc)
>>> + regmap_write(wdev->regmap, IMX2_WDT_WMCR, 0);
>>>
>>> ret = watchdog_register_device(wdog);
>>> if (ret) {
>>> @@ -363,8 +385,29 @@ static int imx2_wdt_resume(struct device *dev)
>>> static SIMPLE_DEV_PM_OPS(imx2_wdt_pm_ops, imx2_wdt_suspend,
>>> imx2_wdt_resume);
>>>
>>> +static const struct imx2_wdt_data imx21_wdt_data = {
>>> + .has_pdc = false,
>>> +};
>>> +
>>> +static const struct imx2_wdt_data imx25_wdt_data = {
>>> + .has_pdc = true,
>>> +};
>>> +
>>> static const struct of_device_id imx2_wdt_dt_ids[] = {
>>> - { .compatible = "fsl,imx21-wdt", },
>>> + { .compatible = "fsl,imx21-wdt", .data = &imx21_wdt_data },
>>
>>
>> Please just specify the flag directly, as in
>> .data = (void *) true
>> or, if you prefer, use defines.
>> .data = (void *) IMX_SUPPORTS_PDC
>>
>> Then, in above code:
>> has_pdc = (bool) of_id->data;
>>
>> Guenter
>
> Has this patch (or an updated one) been merged in any tree? I've
> tested it on a i.MX31 board with positive result.
>
I had asked for a change, and I don't recall seeing v2. So, at least as far
as I know, the answer is no.
Guenter
^ permalink raw reply
* [RFC PATCH 0/7] KVM: arm/arm64: Optimize arch timer register handling
From: Christoffer Dall @ 2016-12-10 20:47 UTC (permalink / raw)
To: linux-arm-kernel
We currently spend around ~400 cycles on each entry/exit to the guest
dealing with arch timer registers, even when the timer is not pending
and not doing anything.
We can do much better by moving the arch timer save/restore to the
vcpu_load and vcpu_put functions, but this means that if we don't read
back the timer state on every exit from the guest, then we have to be
able to start taking timer interrupts for the virtual timer in KVM and
handle that properly.
That has a number of funny consequences, such as having to make sure we
don't deadlock between any of the vgic code and interrupt injection
happening from an ISR. On the plus side, being able to inject
virtual interrupts corresponding to a physical interrupt directly from
an ISR is probably a good system design change.
We also have to change the use of the physical vs. virtual counter in
the arm64 kernel to avoid having to save/restore the CNTVOFF_EL2
register on every return to the hypervisor. The only reason I could
find for using the virtual counter for the kernel on systems with access
to the physical counter is to detect if firmware did not properly clear
CNTVOFF_EL2, and this change has to weighed against the existing check
(assuming I got this right).
On a non-VHE system (AMD Seattle) I have measured this to improve the
world-switch time by about ~100 cycles, but on an EL2 kernel (emulating
VHE behavior on the same hardware) this gives us around ~250 cycles
worth of improvement, because we can avoid the extra configuration of
trapping accesses to the physical timer from EL1 on every switch.
I'm not sure if the benefits outweigh the complexity of this patch set,
nor am I sure if I'm missing an overall better approach, hence the RFC
tag on the series.
I'm looking forward to overall comments on the approach.
These patches are based on arm64/for-next/core as of a few days ago with
Jintacks CNTHCTL_EL2 patch on top, because they give us has_vhe() in the
hyp code using static keys.
Code is also available here:
git://git.kernel.org/pub/scm/linux/kernel/git/cdall/linux.git timer-optimize-rfc
Thanks,
Christoffer
Christoffer Dall (7):
arm64: Use physical counter for in-kernel reads
KVM: arm/arm64: Move kvm_vgic_flush_hwstate under disabled irq
KVM: arm/arm64: Support calling vgic_update_irq_pending from irq
context
KVM: arm/arm64: Check that system supports split eoi/deactivate
KVM: arm/arm64: Move timer save/restore out of hyp code where possible
KVM: arm/arm64: Remove unnecessary timer BUG_ON operations
KVM: arm/arm64: Guard kvm_vgic_map_is_active against !vgic_initialized
arch/arm/include/asm/kvm_asm.h | 2 +
arch/arm/include/asm/kvm_hyp.h | 4 +-
arch/arm/kvm/arm.c | 17 ++-
arch/arm/kvm/hyp/switch.c | 5 +-
arch/arm64/include/asm/arch_timer.h | 6 +-
arch/arm64/include/asm/kvm_asm.h | 2 +
arch/arm64/include/asm/kvm_hyp.h | 4 +-
arch/arm64/kvm/hyp/switch.c | 4 +-
drivers/clocksource/arm_arch_timer.c | 2 +-
include/kvm/arm_arch_timer.h | 7 +-
virt/kvm/arm/arch_timer.c | 222 ++++++++++++++++++++++++-----------
virt/kvm/arm/hyp/timer-sr.c | 32 ++---
virt/kvm/arm/vgic/vgic-its.c | 17 +--
virt/kvm/arm/vgic/vgic-mmio-v2.c | 22 ++--
virt/kvm/arm/vgic/vgic-mmio-v3.c | 10 +-
virt/kvm/arm/vgic/vgic-mmio.c | 38 +++---
virt/kvm/arm/vgic/vgic-v2.c | 5 +-
virt/kvm/arm/vgic/vgic-v3.c | 5 +-
virt/kvm/arm/vgic/vgic.c | 59 ++++++----
virt/kvm/arm/vgic/vgic.h | 3 +-
20 files changed, 292 insertions(+), 174 deletions(-)
--
2.9.0
^ permalink raw reply
* [RFC PATCH 1/7] arm64: Use physical counter for in-kernel reads
From: Christoffer Dall @ 2016-12-10 20:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161210204712.21830-1-christoffer.dall@linaro.org>
Using the physical counter allows KVM to retain the offset between the
virtual and physical counter as long as it is actively running a VCPU.
As soon as a VCPU is released, another thread is scheduled or we start
running userspace applications, we reset the offset to 0, so that VDSO
operations can still read the virtual counter and get the same view of
time as the kernel.
This opens up potential improvements for KVM performance.
VHE kernels or kernels using the virtual timer are unaffected by this.
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
---
arch/arm64/include/asm/arch_timer.h | 6 ++++--
drivers/clocksource/arm_arch_timer.c | 2 +-
2 files changed, 5 insertions(+), 3 deletions(-)
diff --git a/arch/arm64/include/asm/arch_timer.h b/arch/arm64/include/asm/arch_timer.h
index eaa5bbe..cec2549 100644
--- a/arch/arm64/include/asm/arch_timer.h
+++ b/arch/arm64/include/asm/arch_timer.h
@@ -139,11 +139,13 @@ static inline void arch_timer_set_cntkctl(u32 cntkctl)
static inline u64 arch_counter_get_cntpct(void)
{
+ u64 cval;
/*
* AArch64 kernel and user space mandate the use of CNTVCT.
*/
- BUG();
- return 0;
+ isb();
+ asm volatile("mrs %0, cntpct_el0" : "=r" (cval));
+ return cval;
}
static inline u64 arch_counter_get_cntvct(void)
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 73c487d..a5b0789 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -597,7 +597,7 @@ static void __init arch_counter_register(unsigned type)
/* Register the CP15 based counter if we have one */
if (type & ARCH_CP15_TIMER) {
- if (IS_ENABLED(CONFIG_ARM64) || arch_timer_uses_ppi == VIRT_PPI)
+ if (arch_timer_uses_ppi == VIRT_PPI || is_kernel_in_hyp_mode())
arch_timer_read_counter = arch_counter_get_cntvct;
else
arch_timer_read_counter = arch_counter_get_cntpct;
--
2.9.0
^ permalink raw reply related
* [RFC PATCH 2/7] KVM: arm/arm64: Move kvm_vgic_flush_hwstate under disabled irq
From: Christoffer Dall @ 2016-12-10 20:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161210204712.21830-1-christoffer.dall@linaro.org>
As we are about to play tricks with the timer to be more lazy in saving
and restoring state, we need to move the timer flush function under a
disabled irq section and since we hav to flush the vgic state after the
timer state, move it as well.
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
---
arch/arm/kvm/arm.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index 13e54e8..6591af7 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -627,11 +627,12 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
*/
preempt_disable();
kvm_pmu_flush_hwstate(vcpu);
- kvm_timer_flush_hwstate(vcpu);
- kvm_vgic_flush_hwstate(vcpu);
local_irq_disable();
+ kvm_timer_flush_hwstate(vcpu);
+ kvm_vgic_flush_hwstate(vcpu);
+
/*
* Re-check atomic conditions
*/
--
2.9.0
^ permalink raw reply related
* [RFC PATCH 3/7] KVM: arm/arm64: Support calling vgic_update_irq_pending from irq context
From: Christoffer Dall @ 2016-12-10 20:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161210204712.21830-1-christoffer.dall@linaro.org>
We are about to optimize our timer handling logic which involves
injecting irqs to the vgic directly from the irq handler.
Unfortunately, the injection path can take any AP list lock and irq lock
and we must therefore make sure to use spin_lock_irqsave whereever
interrupts are enabled and we are taking any of those locks, to avoid
deadlocking between process context and the ISR.
This changes a lot of the VGIC code, but The good news are that the
changes are mostly mechanichal.
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
---
virt/kvm/arm/vgic/vgic-its.c | 17 +++++++-----
virt/kvm/arm/vgic/vgic-mmio-v2.c | 22 +++++++++-------
virt/kvm/arm/vgic/vgic-mmio-v3.c | 10 ++++---
virt/kvm/arm/vgic/vgic-mmio.c | 38 ++++++++++++++++-----------
virt/kvm/arm/vgic/vgic-v2.c | 5 ++--
virt/kvm/arm/vgic/vgic-v3.c | 5 ++--
virt/kvm/arm/vgic/vgic.c | 56 +++++++++++++++++++++++++---------------
virt/kvm/arm/vgic/vgic.h | 3 ++-
8 files changed, 95 insertions(+), 61 deletions(-)
diff --git a/virt/kvm/arm/vgic/vgic-its.c b/virt/kvm/arm/vgic/vgic-its.c
index 8c2b3cd..140ee78 100644
--- a/virt/kvm/arm/vgic/vgic-its.c
+++ b/virt/kvm/arm/vgic/vgic-its.c
@@ -207,6 +207,7 @@ static int update_lpi_config(struct kvm *kvm, struct vgic_irq *irq,
u64 propbase = PROPBASER_ADDRESS(kvm->arch.vgic.propbaser);
u8 prop;
int ret;
+ unsigned long flags;
ret = kvm_read_guest(kvm, propbase + irq->intid - GIC_LPI_OFFSET,
&prop, 1);
@@ -214,15 +215,15 @@ static int update_lpi_config(struct kvm *kvm, struct vgic_irq *irq,
if (ret)
return ret;
- spin_lock(&irq->irq_lock);
+ spin_lock_irqsave(&irq->irq_lock, flags);
if (!filter_vcpu || filter_vcpu == irq->target_vcpu) {
irq->priority = LPI_PROP_PRIORITY(prop);
irq->enabled = LPI_PROP_ENABLE_BIT(prop);
- vgic_queue_irq_unlock(kvm, irq);
+ vgic_queue_irq_unlock(kvm, irq, flags);
} else {
- spin_unlock(&irq->irq_lock);
+ spin_unlock_irqrestore(&irq->irq_lock, flags);
}
return 0;
@@ -322,6 +323,7 @@ static int its_sync_lpi_pending_table(struct kvm_vcpu *vcpu)
int ret = 0;
u32 *intids;
int nr_irqs, i;
+ unsigned long flags;
nr_irqs = vgic_copy_lpi_list(vcpu->kvm, &intids);
if (nr_irqs < 0)
@@ -349,9 +351,9 @@ static int its_sync_lpi_pending_table(struct kvm_vcpu *vcpu)
}
irq = vgic_get_irq(vcpu->kvm, NULL, intids[i]);
- spin_lock(&irq->irq_lock);
+ spin_lock_irqsave(&irq->irq_lock, flags);
irq->pending = pendmask & (1U << bit_nr);
- vgic_queue_irq_unlock(vcpu->kvm, irq);
+ vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
vgic_put_irq(vcpu->kvm, irq);
}
@@ -449,6 +451,7 @@ static int vgic_its_trigger_msi(struct kvm *kvm, struct vgic_its *its,
{
struct kvm_vcpu *vcpu;
struct its_itte *itte;
+ unsigned long flags;
if (!its->enabled)
return -EBUSY;
@@ -464,9 +467,9 @@ static int vgic_its_trigger_msi(struct kvm *kvm, struct vgic_its *its,
if (!vcpu->arch.vgic_cpu.lpis_enabled)
return -EBUSY;
- spin_lock(&itte->irq->irq_lock);
+ spin_lock_irqsave(&itte->irq->irq_lock, flags);
itte->irq->pending = true;
- vgic_queue_irq_unlock(kvm, itte->irq);
+ vgic_queue_irq_unlock(kvm, itte->irq, flags);
return 0;
}
diff --git a/virt/kvm/arm/vgic/vgic-mmio-v2.c b/virt/kvm/arm/vgic/vgic-mmio-v2.c
index b44b359..f08d3e6 100644
--- a/virt/kvm/arm/vgic/vgic-mmio-v2.c
+++ b/virt/kvm/arm/vgic/vgic-mmio-v2.c
@@ -74,6 +74,7 @@ static void vgic_mmio_write_sgir(struct kvm_vcpu *source_vcpu,
int mode = (val >> 24) & 0x03;
int c;
struct kvm_vcpu *vcpu;
+ unsigned long flags;
switch (mode) {
case 0x0: /* as specified by targets */
@@ -97,11 +98,11 @@ static void vgic_mmio_write_sgir(struct kvm_vcpu *source_vcpu,
irq = vgic_get_irq(source_vcpu->kvm, vcpu, intid);
- spin_lock(&irq->irq_lock);
+ spin_lock_irqsave(&irq->irq_lock, flags);
irq->pending = true;
irq->source |= 1U << source_vcpu->vcpu_id;
- vgic_queue_irq_unlock(source_vcpu->kvm, irq);
+ vgic_queue_irq_unlock(source_vcpu->kvm, irq, flags);
vgic_put_irq(source_vcpu->kvm, irq);
}
}
@@ -130,6 +131,7 @@ static void vgic_mmio_write_target(struct kvm_vcpu *vcpu,
{
u32 intid = VGIC_ADDR_TO_INTID(addr, 8);
int i;
+ unsigned long flags;
/* GICD_ITARGETSR[0-7] are read-only */
if (intid < VGIC_NR_PRIVATE_IRQS)
@@ -139,13 +141,13 @@ static void vgic_mmio_write_target(struct kvm_vcpu *vcpu,
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, NULL, intid + i);
int target;
- spin_lock(&irq->irq_lock);
+ spin_lock_irqsave(&irq->irq_lock, flags);
irq->targets = (val >> (i * 8)) & 0xff;
target = irq->targets ? __ffs(irq->targets) : 0;
irq->target_vcpu = kvm_get_vcpu(vcpu->kvm, target);
- spin_unlock(&irq->irq_lock);
+ spin_unlock_irqrestore(&irq->irq_lock, flags);
vgic_put_irq(vcpu->kvm, irq);
}
}
@@ -173,17 +175,18 @@ static void vgic_mmio_write_sgipendc(struct kvm_vcpu *vcpu,
{
u32 intid = addr & 0x0f;
int i;
+ unsigned long flags;
for (i = 0; i < len; i++) {
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
- spin_lock(&irq->irq_lock);
+ spin_lock_irqsave(&irq->irq_lock, flags);
irq->source &= ~((val >> (i * 8)) & 0xff);
if (!irq->source)
irq->pending = false;
- spin_unlock(&irq->irq_lock);
+ spin_unlock_irqrestore(&irq->irq_lock, flags);
vgic_put_irq(vcpu->kvm, irq);
}
}
@@ -194,19 +197,20 @@ static void vgic_mmio_write_sgipends(struct kvm_vcpu *vcpu,
{
u32 intid = addr & 0x0f;
int i;
+ unsigned long flags;
for (i = 0; i < len; i++) {
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
- spin_lock(&irq->irq_lock);
+ spin_lock_irqsave(&irq->irq_lock, flags);
irq->source |= (val >> (i * 8)) & 0xff;
if (irq->source) {
irq->pending = true;
- vgic_queue_irq_unlock(vcpu->kvm, irq);
+ vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
} else {
- spin_unlock(&irq->irq_lock);
+ spin_unlock_irqrestore(&irq->irq_lock, flags);
}
vgic_put_irq(vcpu->kvm, irq);
}
diff --git a/virt/kvm/arm/vgic/vgic-mmio-v3.c b/virt/kvm/arm/vgic/vgic-mmio-v3.c
index 50f42f0..b1c2676 100644
--- a/virt/kvm/arm/vgic/vgic-mmio-v3.c
+++ b/virt/kvm/arm/vgic/vgic-mmio-v3.c
@@ -127,6 +127,7 @@ static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
{
int intid = VGIC_ADDR_TO_INTID(addr, 64);
struct vgic_irq *irq;
+ unsigned long flags;
/* The upper word is WI for us since we don't implement Aff3. */
if (addr & 4)
@@ -137,13 +138,13 @@ static void vgic_mmio_write_irouter(struct kvm_vcpu *vcpu,
if (!irq)
return;
- spin_lock(&irq->irq_lock);
+ spin_lock_irqsave(&irq->irq_lock, flags);
/* We only care about and preserve Aff0, Aff1 and Aff2. */
irq->mpidr = val & GENMASK(23, 0);
irq->target_vcpu = kvm_mpidr_to_vcpu(vcpu->kvm, irq->mpidr);
- spin_unlock(&irq->irq_lock);
+ spin_unlock_irqrestore(&irq->irq_lock, flags);
vgic_put_irq(vcpu->kvm, irq);
}
@@ -607,6 +608,7 @@ void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg)
int sgi, c;
int vcpu_id = vcpu->vcpu_id;
bool broadcast;
+ unsigned long flags;
sgi = (reg & ICC_SGI1R_SGI_ID_MASK) >> ICC_SGI1R_SGI_ID_SHIFT;
broadcast = reg & BIT_ULL(ICC_SGI1R_IRQ_ROUTING_MODE_BIT);
@@ -645,10 +647,10 @@ void vgic_v3_dispatch_sgi(struct kvm_vcpu *vcpu, u64 reg)
irq = vgic_get_irq(vcpu->kvm, c_vcpu, sgi);
- spin_lock(&irq->irq_lock);
+ spin_lock_irqsave(&irq->irq_lock, flags);
irq->pending = true;
- vgic_queue_irq_unlock(vcpu->kvm, irq);
+ vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
vgic_put_irq(vcpu->kvm, irq);
}
}
diff --git a/virt/kvm/arm/vgic/vgic-mmio.c b/virt/kvm/arm/vgic/vgic-mmio.c
index ebe1b9f..a7b7dc5 100644
--- a/virt/kvm/arm/vgic/vgic-mmio.c
+++ b/virt/kvm/arm/vgic/vgic-mmio.c
@@ -69,13 +69,14 @@ void vgic_mmio_write_senable(struct kvm_vcpu *vcpu,
{
u32 intid = VGIC_ADDR_TO_INTID(addr, 1);
int i;
+ unsigned long flags;
for_each_set_bit(i, &val, len * 8) {
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
- spin_lock(&irq->irq_lock);
+ spin_lock_irqsave(&irq->irq_lock, flags);
irq->enabled = true;
- vgic_queue_irq_unlock(vcpu->kvm, irq);
+ vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
vgic_put_irq(vcpu->kvm, irq);
}
@@ -87,15 +88,16 @@ void vgic_mmio_write_cenable(struct kvm_vcpu *vcpu,
{
u32 intid = VGIC_ADDR_TO_INTID(addr, 1);
int i;
+ unsigned long flags;
for_each_set_bit(i, &val, len * 8) {
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
- spin_lock(&irq->irq_lock);
+ spin_lock_irqsave(&irq->irq_lock, flags);
irq->enabled = false;
- spin_unlock(&irq->irq_lock);
+ spin_unlock_irqrestore(&irq->irq_lock, flags);
vgic_put_irq(vcpu->kvm, irq);
}
}
@@ -126,16 +128,17 @@ void vgic_mmio_write_spending(struct kvm_vcpu *vcpu,
{
u32 intid = VGIC_ADDR_TO_INTID(addr, 1);
int i;
+ unsigned long flags;
for_each_set_bit(i, &val, len * 8) {
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
- spin_lock(&irq->irq_lock);
+ spin_lock_irqsave(&irq->irq_lock, flags);
irq->pending = true;
if (irq->config == VGIC_CONFIG_LEVEL)
irq->soft_pending = true;
- vgic_queue_irq_unlock(vcpu->kvm, irq);
+ vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
vgic_put_irq(vcpu->kvm, irq);
}
}
@@ -146,11 +149,12 @@ void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu,
{
u32 intid = VGIC_ADDR_TO_INTID(addr, 1);
int i;
+ unsigned long flags;
for_each_set_bit(i, &val, len * 8) {
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
- spin_lock(&irq->irq_lock);
+ spin_lock_irqsave(&irq->irq_lock, flags);
if (irq->config == VGIC_CONFIG_LEVEL) {
irq->soft_pending = false;
@@ -159,7 +163,7 @@ void vgic_mmio_write_cpending(struct kvm_vcpu *vcpu,
irq->pending = false;
}
- spin_unlock(&irq->irq_lock);
+ spin_unlock_irqrestore(&irq->irq_lock, flags);
vgic_put_irq(vcpu->kvm, irq);
}
}
@@ -187,7 +191,9 @@ unsigned long vgic_mmio_read_active(struct kvm_vcpu *vcpu,
static void vgic_mmio_change_active(struct kvm_vcpu *vcpu, struct vgic_irq *irq,
bool new_active_state)
{
- spin_lock(&irq->irq_lock);
+ unsigned long flags;
+
+ spin_lock_irqsave(&irq->irq_lock, flags);
/*
* If this virtual IRQ was written into a list register, we
* have to make sure the CPU that runs the VCPU thread has
@@ -207,9 +213,9 @@ static void vgic_mmio_change_active(struct kvm_vcpu *vcpu, struct vgic_irq *irq,
irq->active = new_active_state;
if (new_active_state)
- vgic_queue_irq_unlock(vcpu->kvm, irq);
+ vgic_queue_irq_unlock(vcpu->kvm, irq, flags);
else
- spin_unlock(&irq->irq_lock);
+ spin_unlock_irqrestore(&irq->irq_lock, flags);
}
/*
@@ -305,14 +311,15 @@ void vgic_mmio_write_priority(struct kvm_vcpu *vcpu,
{
u32 intid = VGIC_ADDR_TO_INTID(addr, 8);
int i;
+ unsigned long flags;
for (i = 0; i < len; i++) {
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
- spin_lock(&irq->irq_lock);
+ spin_lock_irqsave(&irq->irq_lock, flags);
/* Narrow the priority range to what we actually support */
irq->priority = (val >> (i * 8)) & GENMASK(7, 8 - VGIC_PRI_BITS);
- spin_unlock(&irq->irq_lock);
+ spin_unlock_irqrestore(&irq->irq_lock, flags);
vgic_put_irq(vcpu->kvm, irq);
}
@@ -343,6 +350,7 @@ void vgic_mmio_write_config(struct kvm_vcpu *vcpu,
{
u32 intid = VGIC_ADDR_TO_INTID(addr, 2);
int i;
+ unsigned long flags;
for (i = 0; i < len * 4; i++) {
struct vgic_irq *irq;
@@ -357,7 +365,7 @@ void vgic_mmio_write_config(struct kvm_vcpu *vcpu,
continue;
irq = vgic_get_irq(vcpu->kvm, vcpu, intid + i);
- spin_lock(&irq->irq_lock);
+ spin_lock_irqsave(&irq->irq_lock, flags);
if (test_bit(i * 2 + 1, &val)) {
irq->config = VGIC_CONFIG_EDGE;
@@ -366,7 +374,7 @@ void vgic_mmio_write_config(struct kvm_vcpu *vcpu,
irq->pending = irq->line_level | irq->soft_pending;
}
- spin_unlock(&irq->irq_lock);
+ spin_unlock_irqrestore(&irq->irq_lock, flags);
vgic_put_irq(vcpu->kvm, irq);
}
}
diff --git a/virt/kvm/arm/vgic/vgic-v2.c b/virt/kvm/arm/vgic/vgic-v2.c
index 0a063af..95cbc9f 100644
--- a/virt/kvm/arm/vgic/vgic-v2.c
+++ b/virt/kvm/arm/vgic/vgic-v2.c
@@ -86,6 +86,7 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
{
struct vgic_v2_cpu_if *cpuif = &vcpu->arch.vgic_cpu.vgic_v2;
int lr;
+ unsigned long flags;
for (lr = 0; lr < vcpu->arch.vgic_cpu.used_lrs; lr++) {
u32 val = cpuif->vgic_lr[lr];
@@ -94,7 +95,7 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
irq = vgic_get_irq(vcpu->kvm, vcpu, intid);
- spin_lock(&irq->irq_lock);
+ spin_lock_irqsave(&irq->irq_lock, flags);
/* Always preserve the active bit */
irq->active = !!(val & GICH_LR_ACTIVE_BIT);
@@ -123,7 +124,7 @@ void vgic_v2_fold_lr_state(struct kvm_vcpu *vcpu)
irq->pending = irq->line_level || irq->soft_pending;
}
- spin_unlock(&irq->irq_lock);
+ spin_unlock_irqrestore(&irq->irq_lock, flags);
vgic_put_irq(vcpu->kvm, irq);
}
}
diff --git a/virt/kvm/arm/vgic/vgic-v3.c b/virt/kvm/arm/vgic/vgic-v3.c
index 9f0dae3..9edeffd 100644
--- a/virt/kvm/arm/vgic/vgic-v3.c
+++ b/virt/kvm/arm/vgic/vgic-v3.c
@@ -70,6 +70,7 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
struct vgic_v3_cpu_if *cpuif = &vcpu->arch.vgic_cpu.vgic_v3;
u32 model = vcpu->kvm->arch.vgic.vgic_model;
int lr;
+ unsigned long flags;
for (lr = 0; lr < vcpu->arch.vgic_cpu.used_lrs; lr++) {
u64 val = cpuif->vgic_lr[lr];
@@ -84,7 +85,7 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
if (!irq) /* An LPI could have been unmapped. */
continue;
- spin_lock(&irq->irq_lock);
+ spin_lock_irqsave(&irq->irq_lock, flags);
/* Always preserve the active bit */
irq->active = !!(val & ICH_LR_ACTIVE_BIT);
@@ -114,7 +115,7 @@ void vgic_v3_fold_lr_state(struct kvm_vcpu *vcpu)
irq->pending = irq->line_level || irq->soft_pending;
}
- spin_unlock(&irq->irq_lock);
+ spin_unlock_irqrestore(&irq->irq_lock, flags);
vgic_put_irq(vcpu->kvm, irq);
}
}
diff --git a/virt/kvm/arm/vgic/vgic.c b/virt/kvm/arm/vgic/vgic.c
index 6440b56..67d231d 100644
--- a/virt/kvm/arm/vgic/vgic.c
+++ b/virt/kvm/arm/vgic/vgic.c
@@ -50,6 +50,10 @@ struct vgic_global __section(.hyp.text) kvm_vgic_global_state = {.gicv3_cpuif =
* vcpuX->vcpu_id < vcpuY->vcpu_id:
* spin_lock(vcpuX->arch.vgic_cpu.ap_list_lock);
* spin_lock(vcpuY->arch.vgic_cpu.ap_list_lock);
+ *
+ * Since the VGIC must support injecting virtual interrupts from ISRs, we have
+ * to use the spin_lock_irqsave/spin_unlock_irqrestore versions of outer
+ * spinlocks for any lock that may be taken while injecting an interrupt.
*/
/*
@@ -254,7 +258,8 @@ static bool vgic_validate_injection(struct vgic_irq *irq, bool level)
* Needs to be entered with the IRQ lock already held, but will return
* with all locks dropped.
*/
-bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq)
+bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq,
+ unsigned long flags)
{
struct kvm_vcpu *vcpu;
@@ -272,7 +277,7 @@ bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq)
* not need to be inserted into an ap_list and there is also
* no more work for us to do.
*/
- spin_unlock(&irq->irq_lock);
+ spin_unlock_irqrestore(&irq->irq_lock, flags);
/*
* We have to kick the VCPU here, because we could be
@@ -292,11 +297,11 @@ bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq)
* We must unlock the irq lock to take the ap_list_lock where
* we are going to insert this new pending interrupt.
*/
- spin_unlock(&irq->irq_lock);
+ spin_unlock_irqrestore(&irq->irq_lock, flags);
/* someone can do stuff here, which we re-check below */
- spin_lock(&vcpu->arch.vgic_cpu.ap_list_lock);
+ spin_lock_irqsave(&vcpu->arch.vgic_cpu.ap_list_lock, flags);
spin_lock(&irq->irq_lock);
/*
@@ -313,9 +318,9 @@ bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq)
if (unlikely(irq->vcpu || vcpu != vgic_target_oracle(irq))) {
spin_unlock(&irq->irq_lock);
- spin_unlock(&vcpu->arch.vgic_cpu.ap_list_lock);
+ spin_unlock_irqrestore(&vcpu->arch.vgic_cpu.ap_list_lock, flags);
- spin_lock(&irq->irq_lock);
+ spin_lock_irqsave(&irq->irq_lock, flags);
goto retry;
}
@@ -328,7 +333,7 @@ bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq)
irq->vcpu = vcpu;
spin_unlock(&irq->irq_lock);
- spin_unlock(&vcpu->arch.vgic_cpu.ap_list_lock);
+ spin_unlock_irqrestore(&vcpu->arch.vgic_cpu.ap_list_lock, flags);
kvm_vcpu_kick(vcpu);
@@ -341,6 +346,7 @@ static int vgic_update_irq_pending(struct kvm *kvm, int cpuid,
{
struct kvm_vcpu *vcpu;
struct vgic_irq *irq;
+ unsigned long flags;
int ret;
trace_vgic_update_irq_pending(cpuid, intid, level);
@@ -362,11 +368,11 @@ static int vgic_update_irq_pending(struct kvm *kvm, int cpuid,
return -EINVAL;
}
- spin_lock(&irq->irq_lock);
+ spin_lock_irqsave(&irq->irq_lock, flags);
if (!vgic_validate_injection(irq, level)) {
/* Nothing to see here, move along... */
- spin_unlock(&irq->irq_lock);
+ spin_unlock_irqrestore(&irq->irq_lock, flags);
vgic_put_irq(kvm, irq);
return 0;
}
@@ -378,7 +384,7 @@ static int vgic_update_irq_pending(struct kvm *kvm, int cpuid,
irq->pending = true;
}
- vgic_queue_irq_unlock(kvm, irq);
+ vgic_queue_irq_unlock(kvm, irq, flags);
vgic_put_irq(kvm, irq);
return 0;
@@ -413,15 +419,16 @@ int kvm_vgic_inject_mapped_irq(struct kvm *kvm, int cpuid, unsigned int intid,
int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, u32 virt_irq, u32 phys_irq)
{
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, virt_irq);
+ unsigned long flags;
BUG_ON(!irq);
- spin_lock(&irq->irq_lock);
+ spin_lock_irqsave(&irq->irq_lock, flags);
irq->hw = true;
irq->hwintid = phys_irq;
- spin_unlock(&irq->irq_lock);
+ spin_unlock_irqrestore(&irq->irq_lock, flags);
vgic_put_irq(vcpu->kvm, irq);
return 0;
@@ -430,6 +437,7 @@ int kvm_vgic_map_phys_irq(struct kvm_vcpu *vcpu, u32 virt_irq, u32 phys_irq)
int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, unsigned int virt_irq)
{
struct vgic_irq *irq;
+ unsigned long flags;
if (!vgic_initialized(vcpu->kvm))
return -EAGAIN;
@@ -437,12 +445,12 @@ int kvm_vgic_unmap_phys_irq(struct kvm_vcpu *vcpu, unsigned int virt_irq)
irq = vgic_get_irq(vcpu->kvm, vcpu, virt_irq);
BUG_ON(!irq);
- spin_lock(&irq->irq_lock);
+ spin_lock_irqsave(&irq->irq_lock, flags);
irq->hw = false;
irq->hwintid = 0;
- spin_unlock(&irq->irq_lock);
+ spin_unlock_irqrestore(&irq->irq_lock, flags);
vgic_put_irq(vcpu->kvm, irq);
return 0;
@@ -460,9 +468,10 @@ static void vgic_prune_ap_list(struct kvm_vcpu *vcpu)
{
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
struct vgic_irq *irq, *tmp;
+ unsigned long flags;
retry:
- spin_lock(&vgic_cpu->ap_list_lock);
+ spin_lock_irqsave(&vgic_cpu->ap_list_lock, flags);
list_for_each_entry_safe(irq, tmp, &vgic_cpu->ap_list_head, ap_list) {
struct kvm_vcpu *target_vcpu, *vcpuA, *vcpuB;
@@ -502,7 +511,7 @@ static void vgic_prune_ap_list(struct kvm_vcpu *vcpu)
/* This interrupt looks like it has to be migrated. */
spin_unlock(&irq->irq_lock);
- spin_unlock(&vgic_cpu->ap_list_lock);
+ spin_unlock_irqrestore(&vgic_cpu->ap_list_lock, flags);
/*
* Ensure locking order by always locking the smallest
@@ -516,7 +525,7 @@ static void vgic_prune_ap_list(struct kvm_vcpu *vcpu)
vcpuB = vcpu;
}
- spin_lock(&vcpuA->arch.vgic_cpu.ap_list_lock);
+ spin_lock_irqsave(&vcpuA->arch.vgic_cpu.ap_list_lock, flags);
spin_lock_nested(&vcpuB->arch.vgic_cpu.ap_list_lock,
SINGLE_DEPTH_NESTING);
spin_lock(&irq->irq_lock);
@@ -540,11 +549,11 @@ static void vgic_prune_ap_list(struct kvm_vcpu *vcpu)
spin_unlock(&irq->irq_lock);
spin_unlock(&vcpuB->arch.vgic_cpu.ap_list_lock);
- spin_unlock(&vcpuA->arch.vgic_cpu.ap_list_lock);
+ spin_unlock_irqrestore(&vcpuA->arch.vgic_cpu.ap_list_lock, flags);
goto retry;
}
- spin_unlock(&vgic_cpu->ap_list_lock);
+ spin_unlock_irqrestore(&vgic_cpu->ap_list_lock, flags);
}
static inline void vgic_process_maintenance_interrupt(struct kvm_vcpu *vcpu)
@@ -671,6 +680,8 @@ void kvm_vgic_flush_hwstate(struct kvm_vcpu *vcpu)
if (unlikely(!vgic_initialized(vcpu->kvm)))
return;
+ DEBUG_SPINLOCK_BUG_ON(!irqs_disabled());
+
spin_lock(&vcpu->arch.vgic_cpu.ap_list_lock);
vgic_flush_lr_state(vcpu);
spin_unlock(&vcpu->arch.vgic_cpu.ap_list_lock);
@@ -681,11 +692,12 @@ int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu)
struct vgic_cpu *vgic_cpu = &vcpu->arch.vgic_cpu;
struct vgic_irq *irq;
bool pending = false;
+ unsigned long flags;
if (!vcpu->kvm->arch.vgic.enabled)
return false;
- spin_lock(&vgic_cpu->ap_list_lock);
+ spin_lock_irqsave(&vgic_cpu->ap_list_lock, flags);
list_for_each_entry(irq, &vgic_cpu->ap_list_head, ap_list) {
spin_lock(&irq->irq_lock);
@@ -696,7 +708,7 @@ int kvm_vgic_vcpu_pending_irq(struct kvm_vcpu *vcpu)
break;
}
- spin_unlock(&vgic_cpu->ap_list_lock);
+ spin_unlock_irqrestore(&vgic_cpu->ap_list_lock, flags);
return pending;
}
@@ -721,6 +733,8 @@ bool kvm_vgic_map_is_active(struct kvm_vcpu *vcpu, unsigned int virt_irq)
struct vgic_irq *irq = vgic_get_irq(vcpu->kvm, vcpu, virt_irq);
bool map_is_active;
+ DEBUG_SPINLOCK_BUG_ON(!irqs_disabled());
+
spin_lock(&irq->irq_lock);
map_is_active = irq->hw && irq->active;
spin_unlock(&irq->irq_lock);
diff --git a/virt/kvm/arm/vgic/vgic.h b/virt/kvm/arm/vgic/vgic.h
index 859f65c..2132c66 100644
--- a/virt/kvm/arm/vgic/vgic.h
+++ b/virt/kvm/arm/vgic/vgic.h
@@ -40,7 +40,8 @@ struct vgic_vmcr {
struct vgic_irq *vgic_get_irq(struct kvm *kvm, struct kvm_vcpu *vcpu,
u32 intid);
void vgic_put_irq(struct kvm *kvm, struct vgic_irq *irq);
-bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq);
+bool vgic_queue_irq_unlock(struct kvm *kvm, struct vgic_irq *irq,
+ unsigned long flags);
void vgic_kick_vcpus(struct kvm *kvm);
int vgic_check_ioaddr(struct kvm *kvm, phys_addr_t *ioaddr,
--
2.9.0
^ permalink raw reply related
* [RFC PATCH 4/7] KVM: arm/arm64: Check that system supports split eoi/deactivate
From: Christoffer Dall @ 2016-12-10 20:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161210204712.21830-1-christoffer.dall@linaro.org>
Some systems without proper firmware and/or hardware description data
don't support the split EOI and deactivate operation and therefore
don't provide an irq_set_vcpu_affinity implementation. On such
systems, we cannot leave the physical interrupt active after the timer
handler on the host has run, so we cannot support KVM with the timer
changes we about to introduce.
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
---
virt/kvm/arm/arch_timer.c | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c
index c7c3bfd..f27a086 100644
--- a/virt/kvm/arm/arch_timer.c
+++ b/virt/kvm/arm/arch_timer.c
@@ -418,6 +418,31 @@ static int kvm_timer_dying_cpu(unsigned int cpu)
return 0;
}
+static bool has_split_eoi_deactivate_support(void)
+{
+ struct irq_desc *desc;
+ struct irq_data *data;
+ struct irq_chip *chip;
+
+ /*
+ * Check if split EOI and deactivate is supported on this machine.
+ */
+ desc = irq_to_desc(host_vtimer_irq);
+ if (!desc) {
+ kvm_err("kvm_arch_timer: no host_vtimer_irq descriptor\n");
+ return false;
+ }
+
+ data = irq_desc_get_irq_data(desc);
+ chip = irq_data_get_irq_chip(data);
+ if (!chip || !chip->irq_set_vcpu_affinity) {
+ kvm_err("kvm_arch_timer: no split EOI/deactivate; abort\n");
+ return false;
+ }
+
+ return true;
+}
+
int kvm_timer_hyp_init(void)
{
struct arch_timer_kvm_info *info;
@@ -449,6 +474,11 @@ int kvm_timer_hyp_init(void)
return err;
}
+ if (!has_split_eoi_deactivate_support()) {
+ disable_percpu_irq(host_vtimer_irq);
+ return -ENODEV;
+ }
+
kvm_info("virtual timer IRQ%d\n", host_vtimer_irq);
cpuhp_setup_state(CPUHP_AP_KVM_ARM_TIMER_STARTING,
--
2.9.0
^ permalink raw reply related
* [RFC PATCH 5/7] KVM: arm/arm64: Move timer save/restore out of hyp code where possible
From: Christoffer Dall @ 2016-12-10 20:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161210204712.21830-1-christoffer.dall@linaro.org>
We can access the virtual timer registers from EL1 so there's no need to
do this from the hyp path. This gives us some advantages in being able to
optimize our handling of timer registers.
We don't adjust the cntvoff during vcpu execution so we add a hyp hook
to be able to call into hyp mode for setting the cntvoff whenever we
load/put the timer, but we don't have to touch this register on every
entry/exit to the VM.
On VHE systems we also don't need to enable and disable physical counter
access traps on every trip to the VM, we can just do that when we load a
VCPU and put a VCPU.
Therefore we factor out the trap configuration from the save/restore of
the virtual timer state, which is moved to the main arch timer file and
use static keys to avoid touching the trap configuration registers on
VHE systems.
We can now leave the timer running on exiting the guest, and handle
interrupts from the vtimer directly and inject the interrupt as a
virtual interrupt to the GIC directly from the ISR. An added benefit is
that we no longer have to actively write the active state to the
physical distributor, because we set the affinity of the vtimer
interrupt when loading the timer state, so that the interrupt
automatically stays active after firing.
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
---
arch/arm/include/asm/kvm_asm.h | 2 +
arch/arm/include/asm/kvm_hyp.h | 4 +-
arch/arm/kvm/arm.c | 12 ++-
arch/arm/kvm/hyp/switch.c | 5 +-
arch/arm64/include/asm/kvm_asm.h | 2 +
arch/arm64/include/asm/kvm_hyp.h | 4 +-
arch/arm64/kvm/hyp/switch.c | 4 +-
include/kvm/arm_arch_timer.h | 7 +-
virt/kvm/arm/arch_timer.c | 190 +++++++++++++++++++++++++--------------
virt/kvm/arm/hyp/timer-sr.c | 32 ++-----
10 files changed, 156 insertions(+), 106 deletions(-)
diff --git a/arch/arm/include/asm/kvm_asm.h b/arch/arm/include/asm/kvm_asm.h
index 8ef0538..1eabfe2 100644
--- a/arch/arm/include/asm/kvm_asm.h
+++ b/arch/arm/include/asm/kvm_asm.h
@@ -68,6 +68,8 @@ extern void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa);
extern void __kvm_tlb_flush_vmid(struct kvm *kvm);
extern void __kvm_tlb_flush_local_vmid(struct kvm_vcpu *vcpu);
+extern void __kvm_timer_set_cntvoff(u32 cntvoff_low, u32 cntvoff_high);
+
extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
extern void __init_stage2_translation(void);
diff --git a/arch/arm/include/asm/kvm_hyp.h b/arch/arm/include/asm/kvm_hyp.h
index 5850890..92db213 100644
--- a/arch/arm/include/asm/kvm_hyp.h
+++ b/arch/arm/include/asm/kvm_hyp.h
@@ -98,8 +98,8 @@
#define cntvoff_el2 CNTVOFF
#define cnthctl_el2 CNTHCTL
-void __timer_save_state(struct kvm_vcpu *vcpu);
-void __timer_restore_state(struct kvm_vcpu *vcpu);
+void __timer_enable_traps(struct kvm_vcpu *vcpu);
+void __timer_disable_traps(struct kvm_vcpu *vcpu);
void __vgic_v2_save_state(struct kvm_vcpu *vcpu);
void __vgic_v2_restore_state(struct kvm_vcpu *vcpu);
diff --git a/arch/arm/kvm/arm.c b/arch/arm/kvm/arm.c
index 6591af7..582e2f7 100644
--- a/arch/arm/kvm/arm.c
+++ b/arch/arm/kvm/arm.c
@@ -347,10 +347,13 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu)
vcpu->arch.host_cpu_context = this_cpu_ptr(kvm_host_cpu_state);
kvm_arm_set_running_vcpu(vcpu);
+ kvm_timer_vcpu_load(vcpu);
}
void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
{
+ kvm_timer_vcpu_put(vcpu);
+
/*
* The arch-generic KVM code expects the cpu field of a vcpu to be -1
* if the vcpu is no longer assigned to a cpu. This is used for the
@@ -359,7 +362,6 @@ void kvm_arch_vcpu_put(struct kvm_vcpu *vcpu)
vcpu->cpu = -1;
kvm_arm_set_running_vcpu(NULL);
- kvm_timer_vcpu_put(vcpu);
}
int kvm_arch_vcpu_ioctl_get_mpstate(struct kvm_vcpu *vcpu,
@@ -645,7 +647,6 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
vcpu->arch.power_off || vcpu->arch.pause) {
local_irq_enable();
kvm_pmu_sync_hwstate(vcpu);
- kvm_timer_sync_hwstate(vcpu);
kvm_vgic_sync_hwstate(vcpu);
preempt_enable();
continue;
@@ -671,6 +672,12 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
kvm_arm_clear_debug(vcpu);
/*
+ * Sync timer state before enabling interrupts as the sync
+ * must not collide with a timer interrupt.
+ */
+ kvm_timer_sync_hwstate(vcpu);
+
+ /*
* We may have taken a host interrupt in HYP mode (ie
* while executing the guest). This interrupt is still
* pending, as we haven't serviced it yet!
@@ -699,7 +706,6 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *run)
* interrupt line.
*/
kvm_pmu_sync_hwstate(vcpu);
- kvm_timer_sync_hwstate(vcpu);
kvm_vgic_sync_hwstate(vcpu);
diff --git a/arch/arm/kvm/hyp/switch.c b/arch/arm/kvm/hyp/switch.c
index 92678b7..f4f3ebf 100644
--- a/arch/arm/kvm/hyp/switch.c
+++ b/arch/arm/kvm/hyp/switch.c
@@ -172,7 +172,7 @@ int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu)
__activate_vm(vcpu);
__vgic_restore_state(vcpu);
- __timer_restore_state(vcpu);
+ __timer_enable_traps(vcpu);
__sysreg_restore_state(guest_ctxt);
__banked_restore_state(guest_ctxt);
@@ -189,7 +189,8 @@ int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu)
__banked_save_state(guest_ctxt);
__sysreg_save_state(guest_ctxt);
- __timer_save_state(vcpu);
+ __timer_disable_traps(vcpu);
+
__vgic_save_state(vcpu);
__deactivate_traps(vcpu);
diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
index ec3553eb..5daf476 100644
--- a/arch/arm64/include/asm/kvm_asm.h
+++ b/arch/arm64/include/asm/kvm_asm.h
@@ -56,6 +56,8 @@ extern void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa);
extern void __kvm_tlb_flush_vmid(struct kvm *kvm);
extern void __kvm_tlb_flush_local_vmid(struct kvm_vcpu *vcpu);
+extern void __kvm_timer_set_cntvoff(u32 cntvoff_low, u32 cntvoff_high);
+
extern int __kvm_vcpu_run(struct kvm_vcpu *vcpu);
extern u64 __vgic_v3_get_ich_vtr_el2(void);
diff --git a/arch/arm64/include/asm/kvm_hyp.h b/arch/arm64/include/asm/kvm_hyp.h
index b18e852..7bbc717 100644
--- a/arch/arm64/include/asm/kvm_hyp.h
+++ b/arch/arm64/include/asm/kvm_hyp.h
@@ -128,8 +128,8 @@ int __vgic_v2_perform_cpuif_access(struct kvm_vcpu *vcpu);
void __vgic_v3_save_state(struct kvm_vcpu *vcpu);
void __vgic_v3_restore_state(struct kvm_vcpu *vcpu);
-void __timer_save_state(struct kvm_vcpu *vcpu);
-void __timer_restore_state(struct kvm_vcpu *vcpu);
+void __timer_enable_traps(struct kvm_vcpu *vcpu);
+void __timer_disable_traps(struct kvm_vcpu *vcpu);
void __sysreg_save_host_state(struct kvm_cpu_context *ctxt);
void __sysreg_restore_host_state(struct kvm_cpu_context *ctxt);
diff --git a/arch/arm64/kvm/hyp/switch.c b/arch/arm64/kvm/hyp/switch.c
index 8bcae7b..db920a34 100644
--- a/arch/arm64/kvm/hyp/switch.c
+++ b/arch/arm64/kvm/hyp/switch.c
@@ -281,7 +281,7 @@ int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu)
__activate_vm(vcpu);
__vgic_restore_state(vcpu);
- __timer_restore_state(vcpu);
+ __timer_enable_traps(vcpu);
/*
* We must restore the 32-bit state before the sysregs, thanks
@@ -337,7 +337,7 @@ int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu)
__sysreg_save_guest_state(guest_ctxt);
__sysreg32_save_state(vcpu);
- __timer_save_state(vcpu);
+ __timer_disable_traps(vcpu);
__vgic_save_state(vcpu);
__deactivate_traps(vcpu);
diff --git a/include/kvm/arm_arch_timer.h b/include/kvm/arm_arch_timer.h
index 2d54903..3e518a6 100644
--- a/include/kvm/arm_arch_timer.h
+++ b/include/kvm/arm_arch_timer.h
@@ -50,11 +50,12 @@ struct arch_timer_cpu {
/* Timer IRQ */
struct kvm_irq_level irq;
- /* Active IRQ state caching */
- bool active_cleared_last;
/* Is the timer enabled */
bool enabled;
+
+ /* Is the timer state loaded on the hardware timer */
+ bool loaded;
};
int kvm_timer_hyp_init(void);
@@ -74,7 +75,9 @@ bool kvm_timer_should_fire(struct kvm_vcpu *vcpu);
void kvm_timer_schedule(struct kvm_vcpu *vcpu);
void kvm_timer_unschedule(struct kvm_vcpu *vcpu);
+void kvm_timer_vcpu_load(struct kvm_vcpu *vcpu);
void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu);
void kvm_timer_init_vhe(void);
+
#endif
diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c
index f27a086..beede1b 100644
--- a/virt/kvm/arm/arch_timer.c
+++ b/virt/kvm/arm/arch_timer.c
@@ -35,10 +35,8 @@ static struct timecounter *timecounter;
static unsigned int host_vtimer_irq;
static u32 host_vtimer_irq_flags;
-void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu)
-{
- vcpu->arch.timer_cpu.active_cleared_last = false;
-}
+static bool kvm_timer_irq_can_fire(struct kvm_vcpu *vcpu);
+static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level);
static cycle_t kvm_phys_timer_read(void)
{
@@ -70,14 +68,14 @@ static void timer_disarm(struct arch_timer_cpu *timer)
static irqreturn_t kvm_arch_timer_handler(int irq, void *dev_id)
{
struct kvm_vcpu *vcpu = *(struct kvm_vcpu **)dev_id;
+ struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+
+ if (!timer->irq.level) {
+ timer->cntv_ctl = read_sysreg_el0(cntv_ctl);
+ if (kvm_timer_irq_can_fire(vcpu))
+ kvm_timer_update_irq(vcpu, true);
+ }
- /*
- * We disable the timer in the world switch and let it be
- * handled by kvm_timer_sync_hwstate(). Getting a timer
- * interrupt at this point is a sure sign of some major
- * breakage.
- */
- pr_warn("Unexpected interrupt %d on vcpu %p\n", irq, vcpu);
return IRQ_HANDLED;
}
@@ -158,6 +156,16 @@ bool kvm_timer_should_fire(struct kvm_vcpu *vcpu)
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
cycle_t cval, now;
+ /*
+ * If somebody is asking if the timer should fire, but the timer state
+ * is maintained in hardware, we need to take a snapshot of the
+ * current hardware state first.
+ */
+ if (timer->loaded) {
+ timer->cntv_ctl = read_sysreg_el0(cntv_ctl);
+ timer->cntv_cval = read_sysreg_el0(cntv_cval);
+ }
+
if (!kvm_timer_irq_can_fire(vcpu))
return false;
@@ -174,7 +182,6 @@ static void kvm_timer_update_irq(struct kvm_vcpu *vcpu, bool new_level)
BUG_ON(!vgic_initialized(vcpu->kvm));
- timer->active_cleared_last = false;
timer->irq.level = new_level;
trace_kvm_timer_update_irq(vcpu->vcpu_id, timer->irq.irq,
timer->irq.level);
@@ -207,6 +214,29 @@ static int kvm_timer_update_state(struct kvm_vcpu *vcpu)
return 0;
}
+static void timer_save_state(struct kvm_vcpu *vcpu)
+{
+ struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ if (!timer->loaded)
+ goto out;
+
+ if (timer->enabled) {
+ timer->cntv_ctl = read_sysreg_el0(cntv_ctl);
+ timer->cntv_cval = read_sysreg_el0(cntv_cval);
+ }
+
+ /* Disable the virtual timer */
+ write_sysreg_el0(0, cntv_ctl);
+
+ timer->loaded = false;
+out:
+ local_irq_restore(flags);
+}
+
/*
* Schedule the background timer before calling kvm_vcpu_block, so that this
* thread is removed from its waitqueue and made runnable when there's a timer
@@ -218,6 +248,8 @@ void kvm_timer_schedule(struct kvm_vcpu *vcpu)
BUG_ON(timer_is_armed(timer));
+ timer_save_state(vcpu);
+
/*
* No need to schedule a background timer if the guest timer has
* already expired, because kvm_vcpu_block will return before putting
@@ -237,77 +269,91 @@ void kvm_timer_schedule(struct kvm_vcpu *vcpu)
timer_arm(timer, kvm_timer_compute_delta(vcpu));
}
+static void timer_restore_state(struct kvm_vcpu *vcpu)
+{
+ struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ if (timer->loaded)
+ goto out;
+
+ if (timer->enabled) {
+ write_sysreg_el0(timer->cntv_cval, cntv_cval);
+ isb();
+ write_sysreg_el0(timer->cntv_ctl, cntv_ctl);
+ }
+
+ timer->loaded = true;
+out:
+ local_irq_restore(flags);
+}
+
void kvm_timer_unschedule(struct kvm_vcpu *vcpu)
{
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
timer_disarm(timer);
+
+ timer_restore_state(vcpu);
}
-/**
- * kvm_timer_flush_hwstate - prepare to move the virt timer to the cpu
- * @vcpu: The vcpu pointer
- *
- * Check if the virtual timer has expired while we were running in the host,
- * and inject an interrupt if that was the case.
- */
-void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
+static void set_cntvoff(u64 cntvoff)
+{
+ u32 low = cntvoff & GENMASK(31, 0);
+ u32 high = (cntvoff >> 32) & GENMASK(31, 0);
+ kvm_call_hyp(__kvm_timer_set_cntvoff, low, high);
+}
+
+void kvm_timer_vcpu_load(struct kvm_vcpu *vcpu)
{
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
bool phys_active;
int ret;
- if (kvm_timer_update_state(vcpu))
- return;
-
- /*
- * If we enter the guest with the virtual input level to the VGIC
- * asserted, then we have already told the VGIC what we need to, and
- * we don't need to exit from the guest until the guest deactivates
- * the already injected interrupt, so therefore we should set the
- * hardware active state to prevent unnecessary exits from the guest.
- *
- * Also, if we enter the guest with the virtual timer interrupt active,
- * then it must be active on the physical distributor, because we set
- * the HW bit and the guest must be able to deactivate the virtual and
- * physical interrupt at the same time.
- *
- * Conversely, if the virtual input level is deasserted and the virtual
- * interrupt is not active, then always clear the hardware active state
- * to ensure that hardware interrupts from the timer triggers a guest
- * exit.
- */
- phys_active = timer->irq.level ||
- kvm_vgic_map_is_active(vcpu, timer->irq.irq);
-
- /*
- * We want to avoid hitting the (re)distributor as much as
- * possible, as this is a potentially expensive MMIO access
- * (not to mention locks in the irq layer), and a solution for
- * this is to cache the "active" state in memory.
- *
- * Things to consider: we cannot cache an "active set" state,
- * because the HW can change this behind our back (it becomes
- * "clear" in the HW). We must then restrict the caching to
- * the "clear" state.
- *
- * The cache is invalidated on:
- * - vcpu put, indicating that the HW cannot be trusted to be
- * in a sane state on the next vcpu load,
- * - any change in the interrupt state
- *
- * Usage conditions:
- * - cached value is "active clear"
- * - value to be programmed is "active clear"
- */
- if (timer->active_cleared_last && !phys_active)
- return;
+ ret = irq_set_vcpu_affinity(host_vtimer_irq, vcpu);
+ WARN(ret, "irq_set_vcpu_affinity ret %d\n", ret);
+ if (timer->irq.level || kvm_vgic_map_is_active(vcpu, timer->irq.irq))
+ phys_active = true;
+ else
+ phys_active = false;
ret = irq_set_irqchip_state(host_vtimer_irq,
IRQCHIP_STATE_ACTIVE,
phys_active);
WARN_ON(ret);
- timer->active_cleared_last = !phys_active;
+ set_cntvoff(vcpu->kvm->arch.timer.cntvoff);
+ timer_restore_state(vcpu);
+
+ if (is_kernel_in_hyp_mode())
+ __timer_enable_traps(vcpu);
+}
+
+void kvm_timer_vcpu_put(struct kvm_vcpu *vcpu)
+{
+ int ret;
+
+ if (is_kernel_in_hyp_mode())
+ __timer_disable_traps(vcpu);
+
+ timer_save_state(vcpu);
+
+ set_cntvoff(0);
+
+ ret = irq_set_vcpu_affinity(host_vtimer_irq, NULL);
+ WARN(ret, "irq_set_vcpu_affinity ret %d\n", ret);
+}
+
+/**
+ * kvm_timer_flush_hwstate - prepare to move the virt timer to the cpu
+ * @vcpu: The vcpu pointer
+ *
+ * Check if the virtual timer has expired while we were running in the host,
+ * and inject an interrupt if that was the case.
+ */
+void kvm_timer_flush_hwstate(struct kvm_vcpu *vcpu)
+{
}
/**
@@ -324,10 +370,16 @@ void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu)
BUG_ON(timer_is_armed(timer));
/*
- * The guest could have modified the timer registers or the timer
- * could have expired, update the timer state.
+ * If we entered the guest with the timer output asserted we have to
+ * check if the guest has modified the timer so that we should lower
+ * the line at this point.
*/
- kvm_timer_update_state(vcpu);
+ if (timer->irq.level) {
+ timer->cntv_ctl = read_sysreg_el0(cntv_ctl);
+ timer->cntv_cval = read_sysreg_el0(cntv_cval);
+ if (!kvm_timer_should_fire(vcpu))
+ kvm_timer_update_irq(vcpu, false);
+ }
}
int kvm_timer_vcpu_reset(struct kvm_vcpu *vcpu,
diff --git a/virt/kvm/arm/hyp/timer-sr.c b/virt/kvm/arm/hyp/timer-sr.c
index 63e28dd..3786ac65 100644
--- a/virt/kvm/arm/hyp/timer-sr.c
+++ b/virt/kvm/arm/hyp/timer-sr.c
@@ -21,19 +21,15 @@
#include <asm/kvm_hyp.h>
-/* vcpu is already in the HYP VA space */
-void __hyp_text __timer_save_state(struct kvm_vcpu *vcpu)
+void __hyp_text __kvm_timer_set_cntvoff(u32 cntvoff_low, u32 cntvoff_high)
{
- struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
- u64 val;
-
- if (timer->enabled) {
- timer->cntv_ctl = read_sysreg_el0(cntv_ctl);
- timer->cntv_cval = read_sysreg_el0(cntv_cval);
- }
+ u64 cntvoff = (u64)cntvoff_high << 32 | cntvoff_low;
+ write_sysreg(cntvoff, cntvoff_el2);
+}
- /* Disable the virtual timer */
- write_sysreg_el0(0, cntv_ctl);
+void __hyp_text __timer_disable_traps(struct kvm_vcpu *vcpu)
+{
+ u64 val;
/*
* We don't need to do this for VHE since the host kernel runs in EL2
@@ -45,15 +41,10 @@ void __hyp_text __timer_save_state(struct kvm_vcpu *vcpu)
val |= CNTHCTL_EL1PCTEN | CNTHCTL_EL1PCEN;
write_sysreg(val, cnthctl_el2);
}
-
- /* Clear cntvoff for the host */
- write_sysreg(0, cntvoff_el2);
}
-void __hyp_text __timer_restore_state(struct kvm_vcpu *vcpu)
+void __hyp_text __timer_enable_traps(struct kvm_vcpu *vcpu)
{
- struct kvm *kvm = kern_hyp_va(vcpu->kvm);
- struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
u64 val;
/* Those bits are already configured at boot on VHE-system */
@@ -67,11 +58,4 @@ void __hyp_text __timer_restore_state(struct kvm_vcpu *vcpu)
val |= CNTHCTL_EL1PCTEN;
write_sysreg(val, cnthctl_el2);
}
-
- if (timer->enabled) {
- write_sysreg(kvm->arch.timer.cntvoff, cntvoff_el2);
- write_sysreg_el0(timer->cntv_cval, cntv_cval);
- isb();
- write_sysreg_el0(timer->cntv_ctl, cntv_ctl);
- }
}
--
2.9.0
^ permalink raw reply related
* [RFC PATCH 6/7] KVM: arm/arm64: Remove unnecessary timer BUG_ON operations
From: Christoffer Dall @ 2016-12-10 20:47 UTC (permalink / raw)
To: linux-arm-kernel
In-Reply-To: <20161210204712.21830-1-christoffer.dall@linaro.org>
We don't need BUG_ON operations for the timer code here anymore as this
is not a likely case to get wrong and they are in the critical path so
may potentially add overhead.
Signed-off-by: Christoffer Dall <christoffer.dall@linaro.org>
---
virt/kvm/arm/arch_timer.c | 2 --
1 file changed, 2 deletions(-)
diff --git a/virt/kvm/arm/arch_timer.c b/virt/kvm/arm/arch_timer.c
index beede1b..242728a 100644
--- a/virt/kvm/arm/arch_timer.c
+++ b/virt/kvm/arm/arch_timer.c
@@ -367,8 +367,6 @@ void kvm_timer_sync_hwstate(struct kvm_vcpu *vcpu)
{
struct arch_timer_cpu *timer = &vcpu->arch.timer_cpu;
- BUG_ON(timer_is_armed(timer));
-
/*
* If we entered the guest with the timer output asserted we have to
* check if the guest has modified the timer so that we should lower
--
2.9.0
^ 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