2012-11-12 22:33:44 +08:00
|
|
|
/*
|
|
|
|
* linux/drivers/clocksource/arm_arch_timer.c
|
|
|
|
*
|
|
|
|
* Copyright (C) 2011 ARM Ltd.
|
|
|
|
* All Rights Reserved
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
clocksource/arm_arch_timer: Force per-CPU interrupt to be level-triggered
The ARM architected timer produces level-triggered interrupts (this
is mandated by the architecture). Unfortunately, a number of
device-trees get this wrong, and expose an edge-triggered interrupt.
Until now, this wasn't too much an issue, as the programming of the
trigger would fail (the corresponding PPI cannot be reconfigured),
and the kernel would be happy with this. But we're about to change
this, and trust DT a lot if the driver doesn't provide its own
trigger information. In that context, the timer breaks badly.
While we do need to fix the DTs, there is also some userspace out
there (kvmtool) that generates the same kind of broken DT on the
fly, and that will completely break with newer kernels.
As a safety measure, and to keep buggy software alive as well as
buying us some time to fix DTs all over the place, let's check
what trigger configuration has been given us by the firmware.
If this is not a level configuration, then we know that the
DT/ACPI configuration is bust, and we pick some defaults which
won't be worse than the existing setup.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Liu Gang <Gang.Liu@nxp.com>
Cc: Mark Rutland <marc.rutland@arm.com>
Cc: Masahiro Yamada <yamada.masahiro@socionext.com>
Cc: Wenbin Song <Wenbin.Song@freescale.com>
Cc: Mingkai Hu <Mingkai.Hu@freescale.com>
Cc: Florian Fainelli <f.fainelli@gmail.com>
Cc: Kevin Hilman <khilman@baylibre.com>
Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
Cc: Michal Simek <michal.simek@xilinx.com>
Cc: Jon Hunter <jonathanh@nvidia.com>
Cc: arm@kernel.org
Cc: bcm-kernel-feedback-list@broadcom.com
Cc: linux-arm-kernel@lists.infradead.org
Cc: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Ray Jui <rjui@broadcom.com>
Cc: "Hou Zhiqiang" <B48286@freescale.com>
Cc: Tirumalesh Chalamarla <tchalamarla@cavium.com>
Cc: linux-samsung-soc@vger.kernel.org
Cc: Yuan Yao <yao.yuan@nxp.com>
Cc: Jan Glauber <jglauber@cavium.com>
Cc: Gregory Clement <gregory.clement@free-electrons.com>
Cc: linux-amlogic@lists.infradead.org
Cc: soren.brinkmann@xilinx.com
Cc: Rajesh Bhagat <rajesh.bhagat@freescale.com>
Cc: Scott Branden <sbranden@broadcom.com>
Cc: Duc Dang <dhdang@apm.com>
Cc: Kukjin Kim <kgene@kernel.org>
Cc: Carlo Caione <carlo@caione.org>
Cc: Dinh Nguyen <dinguyen@opensource.altera.com>
Link: http://lkml.kernel.org/r/1470045256-9032-2-git-send-email-marc.zyngier@arm.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2016-08-01 17:54:15 +08:00
|
|
|
|
|
|
|
#define pr_fmt(fmt) "arm_arch_timer: " fmt
|
|
|
|
|
2012-11-12 22:33:44 +08:00
|
|
|
#include <linux/init.h>
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/device.h>
|
|
|
|
#include <linux/smp.h>
|
|
|
|
#include <linux/cpu.h>
|
2013-08-23 22:53:15 +08:00
|
|
|
#include <linux/cpu_pm.h>
|
2012-11-12 22:33:44 +08:00
|
|
|
#include <linux/clockchips.h>
|
2015-01-06 21:26:13 +08:00
|
|
|
#include <linux/clocksource.h>
|
2012-11-12 22:33:44 +08:00
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/of_irq.h>
|
2013-07-19 07:59:32 +08:00
|
|
|
#include <linux/of_address.h>
|
2012-11-12 22:33:44 +08:00
|
|
|
#include <linux/io.h>
|
2013-07-19 07:59:32 +08:00
|
|
|
#include <linux/slab.h>
|
2017-02-01 23:36:40 +08:00
|
|
|
#include <linux/sched/clock.h>
|
2013-07-19 07:21:18 +08:00
|
|
|
#include <linux/sched_clock.h>
|
2015-03-24 22:02:50 +08:00
|
|
|
#include <linux/acpi.h>
|
2012-11-12 22:33:44 +08:00
|
|
|
|
|
|
|
#include <asm/arch_timer.h>
|
2013-01-10 19:13:07 +08:00
|
|
|
#include <asm/virt.h>
|
2012-11-12 22:33:44 +08:00
|
|
|
|
|
|
|
#include <clocksource/arm_arch_timer.h>
|
|
|
|
|
2017-01-18 21:25:25 +08:00
|
|
|
#undef pr_fmt
|
|
|
|
#define pr_fmt(fmt) "arch_timer: " fmt
|
|
|
|
|
2013-07-19 07:59:32 +08:00
|
|
|
#define CNTTIDR 0x08
|
|
|
|
#define CNTTIDR_VIRT(n) (BIT(1) << ((n) * 4))
|
|
|
|
|
2016-02-01 20:00:48 +08:00
|
|
|
#define CNTACR(n) (0x40 + ((n) * 4))
|
|
|
|
#define CNTACR_RPCT BIT(0)
|
|
|
|
#define CNTACR_RVCT BIT(1)
|
|
|
|
#define CNTACR_RFRQ BIT(2)
|
|
|
|
#define CNTACR_RVOFF BIT(3)
|
|
|
|
#define CNTACR_RWVT BIT(4)
|
|
|
|
#define CNTACR_RWPT BIT(5)
|
|
|
|
|
2013-07-19 07:59:32 +08:00
|
|
|
#define CNTVCT_LO 0x08
|
|
|
|
#define CNTVCT_HI 0x0c
|
|
|
|
#define CNTFRQ 0x10
|
|
|
|
#define CNTP_TVAL 0x28
|
|
|
|
#define CNTP_CTL 0x2c
|
|
|
|
#define CNTV_TVAL 0x38
|
|
|
|
#define CNTV_CTL 0x3c
|
|
|
|
|
|
|
|
static unsigned arch_timers_present __initdata;
|
|
|
|
|
|
|
|
static void __iomem *arch_counter_base;
|
|
|
|
|
|
|
|
struct arch_timer {
|
|
|
|
void __iomem *base;
|
|
|
|
struct clock_event_device evt;
|
|
|
|
};
|
|
|
|
|
|
|
|
#define to_arch_timer(e) container_of(e, struct arch_timer, evt)
|
|
|
|
|
2012-11-12 22:33:44 +08:00
|
|
|
static u32 arch_timer_rate;
|
2017-01-18 21:25:27 +08:00
|
|
|
static int arch_timer_ppi[ARCH_TIMER_MAX_TIMER_PPI];
|
2012-11-12 22:33:44 +08:00
|
|
|
|
|
|
|
static struct clock_event_device __percpu *arch_timer_evt;
|
|
|
|
|
2017-01-18 21:25:27 +08:00
|
|
|
static enum arch_timer_ppi_nr arch_timer_uses_ppi = ARCH_TIMER_VIRT_PPI;
|
clocksource: arch_arm_timer: Fix age-old arch timer C3STOP detection issue
ARM arch timers are tightly coupled with the CPU logic and lose context
on platform implementing HW power management when cores are powered
down at run-time. Marking the arch timers as C3STOP regardless of power
management capabilities causes issues on platforms with no power management,
since in that case the arch timers cannot possibly enter states where the
timer loses context at runtime and therefore can always be used as a high
resolution clockevent device.
In order to fix the C3STOP issue in a way compliant with how real HW
works, this patch adds a boolean property to the arch timer bindings
to define if the arch timer is managed by an always-on power domain.
This power domain is present on all ARM platforms to date, and manages
HW that must not be turned off, whatever the state of other HW
components (eg power controller). On platforms with no power management
capabilities, it is the only power domain present, which encompasses
and manages power supply for all HW components in the system.
If the timer is powered by the always-on power domain, the always-on
property must be present in the bindings which means that the timer cannot
be shutdown at runtime, so it is not a C3STOP clockevent device.
If the timer binding does not contain the always-on property, the timer is
assumed to be power-gateable, hence it must be defined as a C3STOP
clockevent device.
Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
Cc: Magnus Damm <damm@opensource.se>
Cc: Marc Carino <marc.ceeeee@gmail.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Acked-by: Marc Zyngier <marc.zyngier@arm.com>
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
2014-04-08 17:04:32 +08:00
|
|
|
static bool arch_timer_c3stop;
|
2013-07-19 07:59:32 +08:00
|
|
|
static bool arch_timer_mem_use_virtual;
|
2016-10-05 02:12:09 +08:00
|
|
|
static bool arch_counter_suspend_stop;
|
2017-02-01 20:07:15 +08:00
|
|
|
static bool vdso_default = true;
|
2012-11-12 22:33:44 +08:00
|
|
|
|
2017-10-13 21:32:55 +08:00
|
|
|
static cpumask_t evtstrm_available = CPU_MASK_NONE;
|
2016-06-28 00:30:13 +08:00
|
|
|
static bool evtstrm_enable = IS_ENABLED(CONFIG_ARM_ARCH_TIMER_EVTSTREAM);
|
|
|
|
|
|
|
|
static int __init early_evtstrm_cfg(char *buf)
|
|
|
|
{
|
|
|
|
return strtobool(buf, &evtstrm_enable);
|
|
|
|
}
|
|
|
|
early_param("clocksource.arm_arch_timer.evtstrm", early_evtstrm_cfg);
|
|
|
|
|
2012-11-12 22:33:44 +08:00
|
|
|
/*
|
|
|
|
* Architected system timer support.
|
|
|
|
*/
|
|
|
|
|
2017-01-21 02:28:32 +08:00
|
|
|
static __always_inline
|
|
|
|
void arch_timer_reg_write(int access, enum arch_timer_reg reg, u32 val,
|
|
|
|
struct clock_event_device *clk)
|
|
|
|
{
|
|
|
|
if (access == ARCH_TIMER_MEM_PHYS_ACCESS) {
|
|
|
|
struct arch_timer *timer = to_arch_timer(clk);
|
|
|
|
switch (reg) {
|
|
|
|
case ARCH_TIMER_REG_CTRL:
|
|
|
|
writel_relaxed(val, timer->base + CNTP_CTL);
|
|
|
|
break;
|
|
|
|
case ARCH_TIMER_REG_TVAL:
|
|
|
|
writel_relaxed(val, timer->base + CNTP_TVAL);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else if (access == ARCH_TIMER_MEM_VIRT_ACCESS) {
|
|
|
|
struct arch_timer *timer = to_arch_timer(clk);
|
|
|
|
switch (reg) {
|
|
|
|
case ARCH_TIMER_REG_CTRL:
|
|
|
|
writel_relaxed(val, timer->base + CNTV_CTL);
|
|
|
|
break;
|
|
|
|
case ARCH_TIMER_REG_TVAL:
|
|
|
|
writel_relaxed(val, timer->base + CNTV_TVAL);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
arch_timer_reg_write_cp15(access, reg, val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static __always_inline
|
|
|
|
u32 arch_timer_reg_read(int access, enum arch_timer_reg reg,
|
|
|
|
struct clock_event_device *clk)
|
|
|
|
{
|
|
|
|
u32 val;
|
|
|
|
|
|
|
|
if (access == ARCH_TIMER_MEM_PHYS_ACCESS) {
|
|
|
|
struct arch_timer *timer = to_arch_timer(clk);
|
|
|
|
switch (reg) {
|
|
|
|
case ARCH_TIMER_REG_CTRL:
|
|
|
|
val = readl_relaxed(timer->base + CNTP_CTL);
|
|
|
|
break;
|
|
|
|
case ARCH_TIMER_REG_TVAL:
|
|
|
|
val = readl_relaxed(timer->base + CNTP_TVAL);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else if (access == ARCH_TIMER_MEM_VIRT_ACCESS) {
|
|
|
|
struct arch_timer *timer = to_arch_timer(clk);
|
|
|
|
switch (reg) {
|
|
|
|
case ARCH_TIMER_REG_CTRL:
|
|
|
|
val = readl_relaxed(timer->base + CNTV_CTL);
|
|
|
|
break;
|
|
|
|
case ARCH_TIMER_REG_TVAL:
|
|
|
|
val = readl_relaxed(timer->base + CNTV_TVAL);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
val = arch_timer_reg_read_cp15(access, reg);
|
|
|
|
}
|
|
|
|
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2017-02-01 19:53:46 +08:00
|
|
|
/*
|
|
|
|
* Default to cp15 based access because arm64 uses this function for
|
|
|
|
* sched_clock() before DT is probed and the cp15 method is guaranteed
|
|
|
|
* to exist on arm64. arm doesn't use this before DT is probed so even
|
|
|
|
* if we don't have the cp15 accessors we won't have a problem.
|
|
|
|
*/
|
|
|
|
u64 (*arch_timer_read_counter)(void) = arch_counter_get_cntvct;
|
2017-07-05 17:04:28 +08:00
|
|
|
EXPORT_SYMBOL_GPL(arch_timer_read_counter);
|
2017-02-01 19:53:46 +08:00
|
|
|
|
|
|
|
static u64 arch_counter_read(struct clocksource *cs)
|
|
|
|
{
|
|
|
|
return arch_timer_read_counter();
|
|
|
|
}
|
|
|
|
|
|
|
|
static u64 arch_counter_read_cc(const struct cyclecounter *cc)
|
|
|
|
{
|
|
|
|
return arch_timer_read_counter();
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct clocksource clocksource_counter = {
|
|
|
|
.name = "arch_sys_counter",
|
|
|
|
.rating = 400,
|
|
|
|
.read = arch_counter_read,
|
|
|
|
.mask = CLOCKSOURCE_MASK(56),
|
|
|
|
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct cyclecounter cyclecounter __ro_after_init = {
|
|
|
|
.read = arch_counter_read_cc,
|
|
|
|
.mask = CLOCKSOURCE_MASK(56),
|
|
|
|
};
|
|
|
|
|
2017-02-21 22:37:30 +08:00
|
|
|
struct ate_acpi_oem_info {
|
|
|
|
char oem_id[ACPI_OEM_ID_SIZE + 1];
|
|
|
|
char oem_table_id[ACPI_OEM_TABLE_ID_SIZE + 1];
|
|
|
|
u32 oem_revision;
|
|
|
|
};
|
|
|
|
|
2016-09-22 16:35:17 +08:00
|
|
|
#ifdef CONFIG_FSL_ERRATUM_A008585
|
2017-02-07 00:47:41 +08:00
|
|
|
/*
|
|
|
|
* The number of retries is an arbitrary value well beyond the highest number
|
|
|
|
* of iterations the loop has been observed to take.
|
|
|
|
*/
|
|
|
|
#define __fsl_a008585_read_reg(reg) ({ \
|
|
|
|
u64 _old, _new; \
|
|
|
|
int _retries = 200; \
|
|
|
|
\
|
|
|
|
do { \
|
|
|
|
_old = read_sysreg(reg); \
|
|
|
|
_new = read_sysreg(reg); \
|
|
|
|
_retries--; \
|
|
|
|
} while (unlikely(_old != _new) && _retries); \
|
|
|
|
\
|
|
|
|
WARN_ON_ONCE(!_retries); \
|
|
|
|
_new; \
|
|
|
|
})
|
|
|
|
|
|
|
|
static u32 notrace fsl_a008585_read_cntp_tval_el0(void)
|
2016-09-22 16:35:17 +08:00
|
|
|
{
|
|
|
|
return __fsl_a008585_read_reg(cntp_tval_el0);
|
|
|
|
}
|
|
|
|
|
2017-02-07 00:47:41 +08:00
|
|
|
static u32 notrace fsl_a008585_read_cntv_tval_el0(void)
|
2016-09-22 16:35:17 +08:00
|
|
|
{
|
|
|
|
return __fsl_a008585_read_reg(cntv_tval_el0);
|
|
|
|
}
|
|
|
|
|
2017-10-18 19:06:25 +08:00
|
|
|
static u64 notrace fsl_a008585_read_cntpct_el0(void)
|
|
|
|
{
|
|
|
|
return __fsl_a008585_read_reg(cntpct_el0);
|
|
|
|
}
|
|
|
|
|
2017-02-07 00:47:41 +08:00
|
|
|
static u64 notrace fsl_a008585_read_cntvct_el0(void)
|
2016-09-22 16:35:17 +08:00
|
|
|
{
|
|
|
|
return __fsl_a008585_read_reg(cntvct_el0);
|
|
|
|
}
|
2017-02-07 00:47:41 +08:00
|
|
|
#endif
|
|
|
|
|
2017-02-07 00:47:42 +08:00
|
|
|
#ifdef CONFIG_HISILICON_ERRATUM_161010101
|
|
|
|
/*
|
|
|
|
* Verify whether the value of the second read is larger than the first by
|
|
|
|
* less than 32 is the only way to confirm the value is correct, so clear the
|
|
|
|
* lower 5 bits to check whether the difference is greater than 32 or not.
|
|
|
|
* Theoretically the erratum should not occur more than twice in succession
|
|
|
|
* when reading the system counter, but it is possible that some interrupts
|
|
|
|
* may lead to more than twice read errors, triggering the warning, so setting
|
|
|
|
* the number of retries far beyond the number of iterations the loop has been
|
|
|
|
* observed to take.
|
|
|
|
*/
|
|
|
|
#define __hisi_161010101_read_reg(reg) ({ \
|
|
|
|
u64 _old, _new; \
|
|
|
|
int _retries = 50; \
|
|
|
|
\
|
|
|
|
do { \
|
|
|
|
_old = read_sysreg(reg); \
|
|
|
|
_new = read_sysreg(reg); \
|
|
|
|
_retries--; \
|
|
|
|
} while (unlikely((_new - _old) >> 5) && _retries); \
|
|
|
|
\
|
|
|
|
WARN_ON_ONCE(!_retries); \
|
|
|
|
_new; \
|
|
|
|
})
|
|
|
|
|
|
|
|
static u32 notrace hisi_161010101_read_cntp_tval_el0(void)
|
|
|
|
{
|
|
|
|
return __hisi_161010101_read_reg(cntp_tval_el0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static u32 notrace hisi_161010101_read_cntv_tval_el0(void)
|
|
|
|
{
|
|
|
|
return __hisi_161010101_read_reg(cntv_tval_el0);
|
|
|
|
}
|
|
|
|
|
2017-10-18 19:06:25 +08:00
|
|
|
static u64 notrace hisi_161010101_read_cntpct_el0(void)
|
|
|
|
{
|
|
|
|
return __hisi_161010101_read_reg(cntpct_el0);
|
|
|
|
}
|
|
|
|
|
2017-02-07 00:47:42 +08:00
|
|
|
static u64 notrace hisi_161010101_read_cntvct_el0(void)
|
|
|
|
{
|
|
|
|
return __hisi_161010101_read_reg(cntvct_el0);
|
|
|
|
}
|
2017-02-21 23:04:27 +08:00
|
|
|
|
|
|
|
static struct ate_acpi_oem_info hisi_161010101_oem_info[] = {
|
|
|
|
/*
|
|
|
|
* Note that trailing spaces are required to properly match
|
|
|
|
* the OEM table information.
|
|
|
|
*/
|
|
|
|
{
|
|
|
|
.oem_id = "HISI ",
|
|
|
|
.oem_table_id = "HIP05 ",
|
|
|
|
.oem_revision = 0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.oem_id = "HISI ",
|
|
|
|
.oem_table_id = "HIP06 ",
|
|
|
|
.oem_revision = 0,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
.oem_id = "HISI ",
|
|
|
|
.oem_table_id = "HIP07 ",
|
|
|
|
.oem_revision = 0,
|
|
|
|
},
|
|
|
|
{ /* Sentinel indicating the end of the OEM array */ },
|
|
|
|
};
|
2017-02-07 00:47:42 +08:00
|
|
|
#endif
|
|
|
|
|
2017-01-27 20:52:31 +08:00
|
|
|
#ifdef CONFIG_ARM64_ERRATUM_858921
|
2017-10-18 19:06:25 +08:00
|
|
|
static u64 notrace arm64_858921_read_cntpct_el0(void)
|
|
|
|
{
|
|
|
|
u64 old, new;
|
|
|
|
|
|
|
|
old = read_sysreg(cntpct_el0);
|
|
|
|
new = read_sysreg(cntpct_el0);
|
|
|
|
return (((old ^ new) >> 32) & 1) ? old : new;
|
|
|
|
}
|
|
|
|
|
2017-01-27 20:52:31 +08:00
|
|
|
static u64 notrace arm64_858921_read_cntvct_el0(void)
|
|
|
|
{
|
|
|
|
u64 old, new;
|
|
|
|
|
|
|
|
old = read_sysreg(cntvct_el0);
|
|
|
|
new = read_sysreg(cntvct_el0);
|
|
|
|
return (((old ^ new) >> 32) & 1) ? old : new;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-02-07 00:47:41 +08:00
|
|
|
#ifdef CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND
|
2017-10-16 23:28:39 +08:00
|
|
|
DEFINE_PER_CPU(const struct arch_timer_erratum_workaround *, timer_unstable_counter_workaround);
|
2017-02-07 00:47:41 +08:00
|
|
|
EXPORT_SYMBOL_GPL(timer_unstable_counter_workaround);
|
|
|
|
|
|
|
|
DEFINE_STATIC_KEY_FALSE(arch_timer_read_ool_enabled);
|
|
|
|
EXPORT_SYMBOL_GPL(arch_timer_read_ool_enabled);
|
|
|
|
|
2017-01-27 18:27:09 +08:00
|
|
|
static void erratum_set_next_event_tval_generic(const int access, unsigned long evt,
|
|
|
|
struct clock_event_device *clk)
|
|
|
|
{
|
|
|
|
unsigned long ctrl;
|
2017-07-05 17:04:28 +08:00
|
|
|
u64 cval;
|
2017-01-27 18:27:09 +08:00
|
|
|
|
|
|
|
ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL, clk);
|
|
|
|
ctrl |= ARCH_TIMER_CTRL_ENABLE;
|
|
|
|
ctrl &= ~ARCH_TIMER_CTRL_IT_MASK;
|
|
|
|
|
2017-07-05 17:04:28 +08:00
|
|
|
if (access == ARCH_TIMER_PHYS_ACCESS) {
|
|
|
|
cval = evt + arch_counter_get_cntpct();
|
2017-01-27 18:27:09 +08:00
|
|
|
write_sysreg(cval, cntp_cval_el0);
|
2017-07-05 17:04:28 +08:00
|
|
|
} else {
|
|
|
|
cval = evt + arch_counter_get_cntvct();
|
2017-01-27 18:27:09 +08:00
|
|
|
write_sysreg(cval, cntv_cval_el0);
|
2017-07-05 17:04:28 +08:00
|
|
|
}
|
2017-01-27 18:27:09 +08:00
|
|
|
|
|
|
|
arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk);
|
|
|
|
}
|
|
|
|
|
2017-04-20 01:37:09 +08:00
|
|
|
static __maybe_unused int erratum_set_next_event_tval_virt(unsigned long evt,
|
2017-01-27 18:27:09 +08:00
|
|
|
struct clock_event_device *clk)
|
|
|
|
{
|
|
|
|
erratum_set_next_event_tval_generic(ARCH_TIMER_VIRT_ACCESS, evt, clk);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-04-20 01:37:09 +08:00
|
|
|
static __maybe_unused int erratum_set_next_event_tval_phys(unsigned long evt,
|
2017-01-27 18:27:09 +08:00
|
|
|
struct clock_event_device *clk)
|
|
|
|
{
|
|
|
|
erratum_set_next_event_tval_generic(ARCH_TIMER_PHYS_ACCESS, evt, clk);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-02-07 00:47:41 +08:00
|
|
|
static const struct arch_timer_erratum_workaround ool_workarounds[] = {
|
|
|
|
#ifdef CONFIG_FSL_ERRATUM_A008585
|
|
|
|
{
|
2017-01-20 01:20:59 +08:00
|
|
|
.match_type = ate_match_dt,
|
2017-02-07 00:47:41 +08:00
|
|
|
.id = "fsl,erratum-a008585",
|
2017-01-20 01:20:59 +08:00
|
|
|
.desc = "Freescale erratum a005858",
|
2017-02-07 00:47:41 +08:00
|
|
|
.read_cntp_tval_el0 = fsl_a008585_read_cntp_tval_el0,
|
|
|
|
.read_cntv_tval_el0 = fsl_a008585_read_cntv_tval_el0,
|
2017-10-18 19:06:25 +08:00
|
|
|
.read_cntpct_el0 = fsl_a008585_read_cntpct_el0,
|
2017-02-07 00:47:41 +08:00
|
|
|
.read_cntvct_el0 = fsl_a008585_read_cntvct_el0,
|
2017-01-27 18:27:09 +08:00
|
|
|
.set_next_event_phys = erratum_set_next_event_tval_phys,
|
|
|
|
.set_next_event_virt = erratum_set_next_event_tval_virt,
|
2017-02-07 00:47:41 +08:00
|
|
|
},
|
|
|
|
#endif
|
2017-02-07 00:47:42 +08:00
|
|
|
#ifdef CONFIG_HISILICON_ERRATUM_161010101
|
|
|
|
{
|
2017-01-20 01:20:59 +08:00
|
|
|
.match_type = ate_match_dt,
|
2017-02-07 00:47:42 +08:00
|
|
|
.id = "hisilicon,erratum-161010101",
|
2017-01-20 01:20:59 +08:00
|
|
|
.desc = "HiSilicon erratum 161010101",
|
2017-02-07 00:47:42 +08:00
|
|
|
.read_cntp_tval_el0 = hisi_161010101_read_cntp_tval_el0,
|
|
|
|
.read_cntv_tval_el0 = hisi_161010101_read_cntv_tval_el0,
|
2017-10-18 19:06:25 +08:00
|
|
|
.read_cntpct_el0 = hisi_161010101_read_cntpct_el0,
|
2017-02-07 00:47:42 +08:00
|
|
|
.read_cntvct_el0 = hisi_161010101_read_cntvct_el0,
|
2017-01-27 18:27:09 +08:00
|
|
|
.set_next_event_phys = erratum_set_next_event_tval_phys,
|
|
|
|
.set_next_event_virt = erratum_set_next_event_tval_virt,
|
2017-02-21 23:04:27 +08:00
|
|
|
},
|
|
|
|
{
|
|
|
|
.match_type = ate_match_acpi_oem_info,
|
|
|
|
.id = hisi_161010101_oem_info,
|
|
|
|
.desc = "HiSilicon erratum 161010101",
|
|
|
|
.read_cntp_tval_el0 = hisi_161010101_read_cntp_tval_el0,
|
|
|
|
.read_cntv_tval_el0 = hisi_161010101_read_cntv_tval_el0,
|
2017-10-18 19:06:25 +08:00
|
|
|
.read_cntpct_el0 = hisi_161010101_read_cntpct_el0,
|
2017-02-21 23:04:27 +08:00
|
|
|
.read_cntvct_el0 = hisi_161010101_read_cntvct_el0,
|
|
|
|
.set_next_event_phys = erratum_set_next_event_tval_phys,
|
|
|
|
.set_next_event_virt = erratum_set_next_event_tval_virt,
|
2017-02-07 00:47:42 +08:00
|
|
|
},
|
|
|
|
#endif
|
2017-01-27 20:52:31 +08:00
|
|
|
#ifdef CONFIG_ARM64_ERRATUM_858921
|
|
|
|
{
|
|
|
|
.match_type = ate_match_local_cap_id,
|
|
|
|
.id = (void *)ARM64_WORKAROUND_858921,
|
|
|
|
.desc = "ARM erratum 858921",
|
2017-10-18 19:06:25 +08:00
|
|
|
.read_cntpct_el0 = arm64_858921_read_cntpct_el0,
|
2017-01-27 20:52:31 +08:00
|
|
|
.read_cntvct_el0 = arm64_858921_read_cntvct_el0,
|
|
|
|
},
|
|
|
|
#endif
|
2017-02-07 00:47:41 +08:00
|
|
|
};
|
2017-01-20 01:20:59 +08:00
|
|
|
|
|
|
|
typedef bool (*ate_match_fn_t)(const struct arch_timer_erratum_workaround *,
|
|
|
|
const void *);
|
|
|
|
|
|
|
|
static
|
|
|
|
bool arch_timer_check_dt_erratum(const struct arch_timer_erratum_workaround *wa,
|
|
|
|
const void *arg)
|
|
|
|
{
|
|
|
|
const struct device_node *np = arg;
|
|
|
|
|
|
|
|
return of_property_read_bool(np, wa->id);
|
|
|
|
}
|
|
|
|
|
2017-03-21 00:47:59 +08:00
|
|
|
static
|
|
|
|
bool arch_timer_check_local_cap_erratum(const struct arch_timer_erratum_workaround *wa,
|
|
|
|
const void *arg)
|
|
|
|
{
|
|
|
|
return this_cpu_has_cap((uintptr_t)wa->id);
|
|
|
|
}
|
|
|
|
|
2017-02-21 22:37:30 +08:00
|
|
|
|
|
|
|
static
|
|
|
|
bool arch_timer_check_acpi_oem_erratum(const struct arch_timer_erratum_workaround *wa,
|
|
|
|
const void *arg)
|
|
|
|
{
|
|
|
|
static const struct ate_acpi_oem_info empty_oem_info = {};
|
|
|
|
const struct ate_acpi_oem_info *info = wa->id;
|
|
|
|
const struct acpi_table_header *table = arg;
|
|
|
|
|
|
|
|
/* Iterate over the ACPI OEM info array, looking for a match */
|
|
|
|
while (memcmp(info, &empty_oem_info, sizeof(*info))) {
|
|
|
|
if (!memcmp(info->oem_id, table->oem_id, ACPI_OEM_ID_SIZE) &&
|
|
|
|
!memcmp(info->oem_table_id, table->oem_table_id, ACPI_OEM_TABLE_ID_SIZE) &&
|
|
|
|
info->oem_revision == table->oem_revision)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
info++;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2017-01-20 01:20:59 +08:00
|
|
|
static const struct arch_timer_erratum_workaround *
|
|
|
|
arch_timer_iterate_errata(enum arch_timer_erratum_match_type type,
|
|
|
|
ate_match_fn_t match_fn,
|
|
|
|
void *arg)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ool_workarounds); i++) {
|
|
|
|
if (ool_workarounds[i].match_type != type)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (match_fn(&ool_workarounds[i], arg))
|
|
|
|
return &ool_workarounds[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static
|
2017-02-21 02:34:48 +08:00
|
|
|
void arch_timer_enable_workaround(const struct arch_timer_erratum_workaround *wa,
|
|
|
|
bool local)
|
2017-01-20 01:20:59 +08:00
|
|
|
{
|
2017-02-21 02:34:48 +08:00
|
|
|
int i;
|
|
|
|
|
|
|
|
if (local) {
|
|
|
|
__this_cpu_write(timer_unstable_counter_workaround, wa);
|
|
|
|
} else {
|
|
|
|
for_each_possible_cpu(i)
|
|
|
|
per_cpu(timer_unstable_counter_workaround, i) = wa;
|
|
|
|
}
|
|
|
|
|
2017-08-01 16:02:57 +08:00
|
|
|
/*
|
|
|
|
* Use the locked version, as we're called from the CPU
|
|
|
|
* hotplug framework. Otherwise, we end-up in deadlock-land.
|
|
|
|
*/
|
|
|
|
static_branch_enable_cpuslocked(&arch_timer_read_ool_enabled);
|
2017-02-01 20:07:15 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Don't use the vdso fastpath if errata require using the
|
|
|
|
* out-of-line counter accessor. We may change our mind pretty
|
|
|
|
* late in the game (with a per-CPU erratum, for example), so
|
|
|
|
* change both the default value and the vdso itself.
|
|
|
|
*/
|
|
|
|
if (wa->read_cntvct_el0) {
|
|
|
|
clocksource_counter.archdata.vdso_direct = false;
|
|
|
|
vdso_default = false;
|
|
|
|
}
|
2017-01-20 01:20:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void arch_timer_check_ool_workaround(enum arch_timer_erratum_match_type type,
|
|
|
|
void *arg)
|
|
|
|
{
|
|
|
|
const struct arch_timer_erratum_workaround *wa;
|
|
|
|
ate_match_fn_t match_fn = NULL;
|
2017-03-21 00:47:59 +08:00
|
|
|
bool local = false;
|
2017-01-20 01:20:59 +08:00
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
case ate_match_dt:
|
|
|
|
match_fn = arch_timer_check_dt_erratum;
|
|
|
|
break;
|
2017-03-21 00:47:59 +08:00
|
|
|
case ate_match_local_cap_id:
|
|
|
|
match_fn = arch_timer_check_local_cap_erratum;
|
|
|
|
local = true;
|
|
|
|
break;
|
2017-02-21 22:37:30 +08:00
|
|
|
case ate_match_acpi_oem_info:
|
|
|
|
match_fn = arch_timer_check_acpi_oem_erratum;
|
|
|
|
break;
|
2017-01-20 01:20:59 +08:00
|
|
|
default:
|
|
|
|
WARN_ON(1);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
wa = arch_timer_iterate_errata(type, match_fn, arg);
|
|
|
|
if (!wa)
|
|
|
|
return;
|
|
|
|
|
2017-03-21 00:47:59 +08:00
|
|
|
if (needs_unstable_timer_counter_workaround()) {
|
2017-02-21 02:34:48 +08:00
|
|
|
const struct arch_timer_erratum_workaround *__wa;
|
|
|
|
__wa = __this_cpu_read(timer_unstable_counter_workaround);
|
|
|
|
if (__wa && wa != __wa)
|
2017-03-21 00:47:59 +08:00
|
|
|
pr_warn("Can't enable workaround for %s (clashes with %s\n)",
|
2017-02-21 02:34:48 +08:00
|
|
|
wa->desc, __wa->desc);
|
|
|
|
|
|
|
|
if (__wa)
|
|
|
|
return;
|
2017-03-21 00:47:59 +08:00
|
|
|
}
|
|
|
|
|
2017-02-21 02:34:48 +08:00
|
|
|
arch_timer_enable_workaround(wa, local);
|
2017-03-21 00:47:59 +08:00
|
|
|
pr_info("Enabling %s workaround for %s\n",
|
|
|
|
local ? "local" : "global", wa->desc);
|
2017-01-20 01:20:59 +08:00
|
|
|
}
|
|
|
|
|
2017-01-27 18:27:09 +08:00
|
|
|
#define erratum_handler(fn, r, ...) \
|
|
|
|
({ \
|
|
|
|
bool __val; \
|
2017-02-21 02:34:48 +08:00
|
|
|
if (needs_unstable_timer_counter_workaround()) { \
|
|
|
|
const struct arch_timer_erratum_workaround *__wa; \
|
|
|
|
__wa = __this_cpu_read(timer_unstable_counter_workaround); \
|
|
|
|
if (__wa && __wa->fn) { \
|
|
|
|
r = __wa->fn(__VA_ARGS__); \
|
|
|
|
__val = true; \
|
|
|
|
} else { \
|
|
|
|
__val = false; \
|
|
|
|
} \
|
2017-01-27 18:27:09 +08:00
|
|
|
} else { \
|
|
|
|
__val = false; \
|
|
|
|
} \
|
|
|
|
__val; \
|
|
|
|
})
|
|
|
|
|
2017-02-01 20:07:15 +08:00
|
|
|
static bool arch_timer_this_cpu_has_cntvct_wa(void)
|
|
|
|
{
|
|
|
|
const struct arch_timer_erratum_workaround *wa;
|
|
|
|
|
|
|
|
wa = __this_cpu_read(timer_unstable_counter_workaround);
|
|
|
|
return wa && wa->read_cntvct_el0;
|
|
|
|
}
|
2017-01-20 01:20:59 +08:00
|
|
|
#else
|
|
|
|
#define arch_timer_check_ool_workaround(t,a) do { } while(0)
|
2017-01-27 18:27:09 +08:00
|
|
|
#define erratum_set_next_event_tval_virt(...) ({BUG(); 0;})
|
|
|
|
#define erratum_set_next_event_tval_phys(...) ({BUG(); 0;})
|
2017-01-27 18:27:09 +08:00
|
|
|
#define erratum_handler(fn, r, ...) ({false;})
|
2017-02-01 20:07:15 +08:00
|
|
|
#define arch_timer_this_cpu_has_cntvct_wa() ({false;})
|
2017-02-07 00:47:41 +08:00
|
|
|
#endif /* CONFIG_ARM_ARCH_TIMER_OOL_WORKAROUND */
|
2016-09-22 16:35:17 +08:00
|
|
|
|
2013-07-19 07:59:28 +08:00
|
|
|
static __always_inline irqreturn_t timer_handler(const int access,
|
2012-11-12 22:33:44 +08:00
|
|
|
struct clock_event_device *evt)
|
|
|
|
{
|
|
|
|
unsigned long ctrl;
|
2013-08-21 20:59:23 +08:00
|
|
|
|
2013-07-19 07:59:31 +08:00
|
|
|
ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL, evt);
|
2012-11-12 22:33:44 +08:00
|
|
|
if (ctrl & ARCH_TIMER_CTRL_IT_STAT) {
|
|
|
|
ctrl |= ARCH_TIMER_CTRL_IT_MASK;
|
2013-07-19 07:59:31 +08:00
|
|
|
arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, evt);
|
2012-11-12 22:33:44 +08:00
|
|
|
evt->event_handler(evt);
|
|
|
|
return IRQ_HANDLED;
|
|
|
|
}
|
|
|
|
|
|
|
|
return IRQ_NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
static irqreturn_t arch_timer_handler_virt(int irq, void *dev_id)
|
|
|
|
{
|
|
|
|
struct clock_event_device *evt = dev_id;
|
|
|
|
|
|
|
|
return timer_handler(ARCH_TIMER_VIRT_ACCESS, evt);
|
|
|
|
}
|
|
|
|
|
|
|
|
static irqreturn_t arch_timer_handler_phys(int irq, void *dev_id)
|
|
|
|
{
|
|
|
|
struct clock_event_device *evt = dev_id;
|
|
|
|
|
|
|
|
return timer_handler(ARCH_TIMER_PHYS_ACCESS, evt);
|
|
|
|
}
|
|
|
|
|
2013-07-19 07:59:32 +08:00
|
|
|
static irqreturn_t arch_timer_handler_phys_mem(int irq, void *dev_id)
|
|
|
|
{
|
|
|
|
struct clock_event_device *evt = dev_id;
|
|
|
|
|
|
|
|
return timer_handler(ARCH_TIMER_MEM_PHYS_ACCESS, evt);
|
|
|
|
}
|
|
|
|
|
|
|
|
static irqreturn_t arch_timer_handler_virt_mem(int irq, void *dev_id)
|
|
|
|
{
|
|
|
|
struct clock_event_device *evt = dev_id;
|
|
|
|
|
|
|
|
return timer_handler(ARCH_TIMER_MEM_VIRT_ACCESS, evt);
|
|
|
|
}
|
|
|
|
|
2015-06-12 16:00:12 +08:00
|
|
|
static __always_inline int timer_shutdown(const int access,
|
|
|
|
struct clock_event_device *clk)
|
2012-11-12 22:33:44 +08:00
|
|
|
{
|
|
|
|
unsigned long ctrl;
|
2015-06-12 16:00:12 +08:00
|
|
|
|
|
|
|
ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL, clk);
|
|
|
|
ctrl &= ~ARCH_TIMER_CTRL_ENABLE;
|
|
|
|
arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk);
|
|
|
|
|
|
|
|
return 0;
|
2012-11-12 22:33:44 +08:00
|
|
|
}
|
|
|
|
|
2015-06-12 16:00:12 +08:00
|
|
|
static int arch_timer_shutdown_virt(struct clock_event_device *clk)
|
2012-11-12 22:33:44 +08:00
|
|
|
{
|
2015-06-12 16:00:12 +08:00
|
|
|
return timer_shutdown(ARCH_TIMER_VIRT_ACCESS, clk);
|
2012-11-12 22:33:44 +08:00
|
|
|
}
|
|
|
|
|
2015-06-12 16:00:12 +08:00
|
|
|
static int arch_timer_shutdown_phys(struct clock_event_device *clk)
|
2012-11-12 22:33:44 +08:00
|
|
|
{
|
2015-06-12 16:00:12 +08:00
|
|
|
return timer_shutdown(ARCH_TIMER_PHYS_ACCESS, clk);
|
2012-11-12 22:33:44 +08:00
|
|
|
}
|
|
|
|
|
2015-06-12 16:00:12 +08:00
|
|
|
static int arch_timer_shutdown_virt_mem(struct clock_event_device *clk)
|
2013-07-19 07:59:32 +08:00
|
|
|
{
|
2015-06-12 16:00:12 +08:00
|
|
|
return timer_shutdown(ARCH_TIMER_MEM_VIRT_ACCESS, clk);
|
2012-11-12 22:33:44 +08:00
|
|
|
}
|
|
|
|
|
2015-06-12 16:00:12 +08:00
|
|
|
static int arch_timer_shutdown_phys_mem(struct clock_event_device *clk)
|
2013-07-19 07:59:32 +08:00
|
|
|
{
|
2015-06-12 16:00:12 +08:00
|
|
|
return timer_shutdown(ARCH_TIMER_MEM_PHYS_ACCESS, clk);
|
2013-07-19 07:59:32 +08:00
|
|
|
}
|
|
|
|
|
2013-07-19 07:59:31 +08:00
|
|
|
static __always_inline void set_next_event(const int access, unsigned long evt,
|
2013-08-21 20:59:23 +08:00
|
|
|
struct clock_event_device *clk)
|
2012-11-12 22:33:44 +08:00
|
|
|
{
|
|
|
|
unsigned long ctrl;
|
2013-07-19 07:59:31 +08:00
|
|
|
ctrl = arch_timer_reg_read(access, ARCH_TIMER_REG_CTRL, clk);
|
2012-11-12 22:33:44 +08:00
|
|
|
ctrl |= ARCH_TIMER_CTRL_ENABLE;
|
|
|
|
ctrl &= ~ARCH_TIMER_CTRL_IT_MASK;
|
2013-07-19 07:59:31 +08:00
|
|
|
arch_timer_reg_write(access, ARCH_TIMER_REG_TVAL, evt, clk);
|
|
|
|
arch_timer_reg_write(access, ARCH_TIMER_REG_CTRL, ctrl, clk);
|
2012-11-12 22:33:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int arch_timer_set_next_event_virt(unsigned long evt,
|
2013-07-19 07:59:31 +08:00
|
|
|
struct clock_event_device *clk)
|
2012-11-12 22:33:44 +08:00
|
|
|
{
|
2017-01-27 18:27:09 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (erratum_handler(set_next_event_virt, ret, evt, clk))
|
|
|
|
return ret;
|
2017-01-27 18:27:09 +08:00
|
|
|
|
2013-07-19 07:59:31 +08:00
|
|
|
set_next_event(ARCH_TIMER_VIRT_ACCESS, evt, clk);
|
2012-11-12 22:33:44 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int arch_timer_set_next_event_phys(unsigned long evt,
|
2013-07-19 07:59:31 +08:00
|
|
|
struct clock_event_device *clk)
|
2012-11-12 22:33:44 +08:00
|
|
|
{
|
2017-01-27 18:27:09 +08:00
|
|
|
int ret;
|
|
|
|
|
|
|
|
if (erratum_handler(set_next_event_phys, ret, evt, clk))
|
|
|
|
return ret;
|
2017-01-27 18:27:09 +08:00
|
|
|
|
2013-07-19 07:59:31 +08:00
|
|
|
set_next_event(ARCH_TIMER_PHYS_ACCESS, evt, clk);
|
2012-11-12 22:33:44 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-07-19 07:59:32 +08:00
|
|
|
static int arch_timer_set_next_event_virt_mem(unsigned long evt,
|
|
|
|
struct clock_event_device *clk)
|
2012-11-12 22:33:44 +08:00
|
|
|
{
|
2013-07-19 07:59:32 +08:00
|
|
|
set_next_event(ARCH_TIMER_MEM_VIRT_ACCESS, evt, clk);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int arch_timer_set_next_event_phys_mem(unsigned long evt,
|
|
|
|
struct clock_event_device *clk)
|
|
|
|
{
|
|
|
|
set_next_event(ARCH_TIMER_MEM_PHYS_ACCESS, evt, clk);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2013-08-21 20:59:23 +08:00
|
|
|
static void __arch_timer_setup(unsigned type,
|
|
|
|
struct clock_event_device *clk)
|
2013-07-19 07:59:32 +08:00
|
|
|
{
|
|
|
|
clk->features = CLOCK_EVT_FEAT_ONESHOT;
|
|
|
|
|
2017-01-18 21:25:26 +08:00
|
|
|
if (type == ARCH_TIMER_TYPE_CP15) {
|
clocksource: arch_arm_timer: Fix age-old arch timer C3STOP detection issue
ARM arch timers are tightly coupled with the CPU logic and lose context
on platform implementing HW power management when cores are powered
down at run-time. Marking the arch timers as C3STOP regardless of power
management capabilities causes issues on platforms with no power management,
since in that case the arch timers cannot possibly enter states where the
timer loses context at runtime and therefore can always be used as a high
resolution clockevent device.
In order to fix the C3STOP issue in a way compliant with how real HW
works, this patch adds a boolean property to the arch timer bindings
to define if the arch timer is managed by an always-on power domain.
This power domain is present on all ARM platforms to date, and manages
HW that must not be turned off, whatever the state of other HW
components (eg power controller). On platforms with no power management
capabilities, it is the only power domain present, which encompasses
and manages power supply for all HW components in the system.
If the timer is powered by the always-on power domain, the always-on
property must be present in the bindings which means that the timer cannot
be shutdown at runtime, so it is not a C3STOP clockevent device.
If the timer binding does not contain the always-on property, the timer is
assumed to be power-gateable, hence it must be defined as a C3STOP
clockevent device.
Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
Cc: Magnus Damm <damm@opensource.se>
Cc: Marc Carino <marc.ceeeee@gmail.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Acked-by: Marc Zyngier <marc.zyngier@arm.com>
Acked-by: Rob Herring <robh@kernel.org>
Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
2014-04-08 17:04:32 +08:00
|
|
|
if (arch_timer_c3stop)
|
|
|
|
clk->features |= CLOCK_EVT_FEAT_C3STOP;
|
2013-07-19 07:59:32 +08:00
|
|
|
clk->name = "arch_sys_timer";
|
|
|
|
clk->rating = 450;
|
|
|
|
clk->cpumask = cpumask_of(smp_processor_id());
|
2014-02-20 23:21:23 +08:00
|
|
|
clk->irq = arch_timer_ppi[arch_timer_uses_ppi];
|
|
|
|
switch (arch_timer_uses_ppi) {
|
2017-01-18 21:25:27 +08:00
|
|
|
case ARCH_TIMER_VIRT_PPI:
|
2015-06-12 16:00:12 +08:00
|
|
|
clk->set_state_shutdown = arch_timer_shutdown_virt;
|
2015-12-23 19:29:12 +08:00
|
|
|
clk->set_state_oneshot_stopped = arch_timer_shutdown_virt;
|
2013-07-19 07:59:32 +08:00
|
|
|
clk->set_next_event = arch_timer_set_next_event_virt;
|
2014-02-20 23:21:23 +08:00
|
|
|
break;
|
2017-01-18 21:25:27 +08:00
|
|
|
case ARCH_TIMER_PHYS_SECURE_PPI:
|
|
|
|
case ARCH_TIMER_PHYS_NONSECURE_PPI:
|
|
|
|
case ARCH_TIMER_HYP_PPI:
|
2015-06-12 16:00:12 +08:00
|
|
|
clk->set_state_shutdown = arch_timer_shutdown_phys;
|
2015-12-23 19:29:12 +08:00
|
|
|
clk->set_state_oneshot_stopped = arch_timer_shutdown_phys;
|
2013-07-19 07:59:32 +08:00
|
|
|
clk->set_next_event = arch_timer_set_next_event_phys;
|
2014-02-20 23:21:23 +08:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
BUG();
|
2013-07-19 07:59:32 +08:00
|
|
|
}
|
2016-09-22 16:35:17 +08:00
|
|
|
|
2017-03-21 00:47:59 +08:00
|
|
|
arch_timer_check_ool_workaround(ate_match_local_cap_id, NULL);
|
2012-11-12 22:33:44 +08:00
|
|
|
} else {
|
2014-01-07 06:56:17 +08:00
|
|
|
clk->features |= CLOCK_EVT_FEAT_DYNIRQ;
|
2013-07-19 07:59:32 +08:00
|
|
|
clk->name = "arch_mem_timer";
|
|
|
|
clk->rating = 400;
|
|
|
|
clk->cpumask = cpu_all_mask;
|
|
|
|
if (arch_timer_mem_use_virtual) {
|
2015-06-12 16:00:12 +08:00
|
|
|
clk->set_state_shutdown = arch_timer_shutdown_virt_mem;
|
2015-12-23 19:29:12 +08:00
|
|
|
clk->set_state_oneshot_stopped = arch_timer_shutdown_virt_mem;
|
2013-07-19 07:59:32 +08:00
|
|
|
clk->set_next_event =
|
|
|
|
arch_timer_set_next_event_virt_mem;
|
|
|
|
} else {
|
2015-06-12 16:00:12 +08:00
|
|
|
clk->set_state_shutdown = arch_timer_shutdown_phys_mem;
|
2015-12-23 19:29:12 +08:00
|
|
|
clk->set_state_oneshot_stopped = arch_timer_shutdown_phys_mem;
|
2013-07-19 07:59:32 +08:00
|
|
|
clk->set_next_event =
|
|
|
|
arch_timer_set_next_event_phys_mem;
|
|
|
|
}
|
2012-11-12 22:33:44 +08:00
|
|
|
}
|
|
|
|
|
2015-06-12 16:00:12 +08:00
|
|
|
clk->set_state_shutdown(clk);
|
2012-11-12 22:33:44 +08:00
|
|
|
|
2013-07-19 07:59:32 +08:00
|
|
|
clockevents_config_and_register(clk, arch_timer_rate, 0xf, 0x7fffffff);
|
|
|
|
}
|
2012-11-12 22:33:44 +08:00
|
|
|
|
2014-09-29 07:50:06 +08:00
|
|
|
static void arch_timer_evtstrm_enable(int divider)
|
|
|
|
{
|
|
|
|
u32 cntkctl = arch_timer_get_cntkctl();
|
|
|
|
|
|
|
|
cntkctl &= ~ARCH_TIMER_EVT_TRIGGER_MASK;
|
|
|
|
/* Set the divider and enable virtual event stream */
|
|
|
|
cntkctl |= (divider << ARCH_TIMER_EVT_TRIGGER_SHIFT)
|
|
|
|
| ARCH_TIMER_VIRT_EVT_EN;
|
|
|
|
arch_timer_set_cntkctl(cntkctl);
|
|
|
|
elf_hwcap |= HWCAP_EVTSTRM;
|
|
|
|
#ifdef CONFIG_COMPAT
|
|
|
|
compat_elf_hwcap |= COMPAT_HWCAP_EVTSTRM;
|
|
|
|
#endif
|
2017-10-13 21:32:55 +08:00
|
|
|
cpumask_set_cpu(smp_processor_id(), &evtstrm_available);
|
2014-09-29 07:50:06 +08:00
|
|
|
}
|
|
|
|
|
2013-08-23 22:32:29 +08:00
|
|
|
static void arch_timer_configure_evtstream(void)
|
|
|
|
{
|
|
|
|
int evt_stream_div, pos;
|
|
|
|
|
|
|
|
/* Find the closest power of two to the divisor */
|
|
|
|
evt_stream_div = arch_timer_rate / ARCH_TIMER_EVT_STREAM_FREQ;
|
|
|
|
pos = fls(evt_stream_div);
|
|
|
|
if (pos > 1 && !(evt_stream_div & (1 << (pos - 2))))
|
|
|
|
pos--;
|
|
|
|
/* enable event stream */
|
|
|
|
arch_timer_evtstrm_enable(min(pos, 15));
|
|
|
|
}
|
|
|
|
|
2014-09-29 07:50:06 +08:00
|
|
|
static void arch_counter_set_user_access(void)
|
|
|
|
{
|
|
|
|
u32 cntkctl = arch_timer_get_cntkctl();
|
|
|
|
|
2017-02-01 20:07:15 +08:00
|
|
|
/* Disable user access to the timers and both counters */
|
2014-09-29 07:50:06 +08:00
|
|
|
/* Also disable virtual event stream */
|
|
|
|
cntkctl &= ~(ARCH_TIMER_USR_PT_ACCESS_EN
|
|
|
|
| ARCH_TIMER_USR_VT_ACCESS_EN
|
2017-02-01 20:07:15 +08:00
|
|
|
| ARCH_TIMER_USR_VCT_ACCESS_EN
|
2014-09-29 07:50:06 +08:00
|
|
|
| ARCH_TIMER_VIRT_EVT_EN
|
|
|
|
| ARCH_TIMER_USR_PCT_ACCESS_EN);
|
|
|
|
|
2017-02-01 20:07:15 +08:00
|
|
|
/*
|
|
|
|
* Enable user access to the virtual counter if it doesn't
|
|
|
|
* need to be workaround. The vdso may have been already
|
|
|
|
* disabled though.
|
|
|
|
*/
|
|
|
|
if (arch_timer_this_cpu_has_cntvct_wa())
|
|
|
|
pr_info("CPU%d: Trapping CNTVCT access\n", smp_processor_id());
|
|
|
|
else
|
|
|
|
cntkctl |= ARCH_TIMER_USR_VCT_ACCESS_EN;
|
2014-09-29 07:50:06 +08:00
|
|
|
|
|
|
|
arch_timer_set_cntkctl(cntkctl);
|
|
|
|
}
|
|
|
|
|
2014-02-20 23:21:23 +08:00
|
|
|
static bool arch_timer_has_nonsecure_ppi(void)
|
|
|
|
{
|
2017-01-18 21:25:27 +08:00
|
|
|
return (arch_timer_uses_ppi == ARCH_TIMER_PHYS_SECURE_PPI &&
|
|
|
|
arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI]);
|
2014-02-20 23:21:23 +08:00
|
|
|
}
|
|
|
|
|
clocksource/arm_arch_timer: Force per-CPU interrupt to be level-triggered
The ARM architected timer produces level-triggered interrupts (this
is mandated by the architecture). Unfortunately, a number of
device-trees get this wrong, and expose an edge-triggered interrupt.
Until now, this wasn't too much an issue, as the programming of the
trigger would fail (the corresponding PPI cannot be reconfigured),
and the kernel would be happy with this. But we're about to change
this, and trust DT a lot if the driver doesn't provide its own
trigger information. In that context, the timer breaks badly.
While we do need to fix the DTs, there is also some userspace out
there (kvmtool) that generates the same kind of broken DT on the
fly, and that will completely break with newer kernels.
As a safety measure, and to keep buggy software alive as well as
buying us some time to fix DTs all over the place, let's check
what trigger configuration has been given us by the firmware.
If this is not a level configuration, then we know that the
DT/ACPI configuration is bust, and we pick some defaults which
won't be worse than the existing setup.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Liu Gang <Gang.Liu@nxp.com>
Cc: Mark Rutland <marc.rutland@arm.com>
Cc: Masahiro Yamada <yamada.masahiro@socionext.com>
Cc: Wenbin Song <Wenbin.Song@freescale.com>
Cc: Mingkai Hu <Mingkai.Hu@freescale.com>
Cc: Florian Fainelli <f.fainelli@gmail.com>
Cc: Kevin Hilman <khilman@baylibre.com>
Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
Cc: Michal Simek <michal.simek@xilinx.com>
Cc: Jon Hunter <jonathanh@nvidia.com>
Cc: arm@kernel.org
Cc: bcm-kernel-feedback-list@broadcom.com
Cc: linux-arm-kernel@lists.infradead.org
Cc: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Ray Jui <rjui@broadcom.com>
Cc: "Hou Zhiqiang" <B48286@freescale.com>
Cc: Tirumalesh Chalamarla <tchalamarla@cavium.com>
Cc: linux-samsung-soc@vger.kernel.org
Cc: Yuan Yao <yao.yuan@nxp.com>
Cc: Jan Glauber <jglauber@cavium.com>
Cc: Gregory Clement <gregory.clement@free-electrons.com>
Cc: linux-amlogic@lists.infradead.org
Cc: soren.brinkmann@xilinx.com
Cc: Rajesh Bhagat <rajesh.bhagat@freescale.com>
Cc: Scott Branden <sbranden@broadcom.com>
Cc: Duc Dang <dhdang@apm.com>
Cc: Kukjin Kim <kgene@kernel.org>
Cc: Carlo Caione <carlo@caione.org>
Cc: Dinh Nguyen <dinguyen@opensource.altera.com>
Link: http://lkml.kernel.org/r/1470045256-9032-2-git-send-email-marc.zyngier@arm.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2016-08-01 17:54:15 +08:00
|
|
|
static u32 check_ppi_trigger(int irq)
|
|
|
|
{
|
|
|
|
u32 flags = irq_get_trigger_type(irq);
|
|
|
|
|
|
|
|
if (flags != IRQF_TRIGGER_HIGH && flags != IRQF_TRIGGER_LOW) {
|
|
|
|
pr_warn("WARNING: Invalid trigger for IRQ%d, assuming level low\n", irq);
|
|
|
|
pr_warn("WARNING: Please fix your firmware\n");
|
|
|
|
flags = IRQF_TRIGGER_LOW;
|
|
|
|
}
|
|
|
|
|
|
|
|
return flags;
|
|
|
|
}
|
|
|
|
|
2016-07-14 01:16:39 +08:00
|
|
|
static int arch_timer_starting_cpu(unsigned int cpu)
|
2013-07-19 07:59:32 +08:00
|
|
|
{
|
2016-07-14 01:16:39 +08:00
|
|
|
struct clock_event_device *clk = this_cpu_ptr(arch_timer_evt);
|
clocksource/arm_arch_timer: Force per-CPU interrupt to be level-triggered
The ARM architected timer produces level-triggered interrupts (this
is mandated by the architecture). Unfortunately, a number of
device-trees get this wrong, and expose an edge-triggered interrupt.
Until now, this wasn't too much an issue, as the programming of the
trigger would fail (the corresponding PPI cannot be reconfigured),
and the kernel would be happy with this. But we're about to change
this, and trust DT a lot if the driver doesn't provide its own
trigger information. In that context, the timer breaks badly.
While we do need to fix the DTs, there is also some userspace out
there (kvmtool) that generates the same kind of broken DT on the
fly, and that will completely break with newer kernels.
As a safety measure, and to keep buggy software alive as well as
buying us some time to fix DTs all over the place, let's check
what trigger configuration has been given us by the firmware.
If this is not a level configuration, then we know that the
DT/ACPI configuration is bust, and we pick some defaults which
won't be worse than the existing setup.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Liu Gang <Gang.Liu@nxp.com>
Cc: Mark Rutland <marc.rutland@arm.com>
Cc: Masahiro Yamada <yamada.masahiro@socionext.com>
Cc: Wenbin Song <Wenbin.Song@freescale.com>
Cc: Mingkai Hu <Mingkai.Hu@freescale.com>
Cc: Florian Fainelli <f.fainelli@gmail.com>
Cc: Kevin Hilman <khilman@baylibre.com>
Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
Cc: Michal Simek <michal.simek@xilinx.com>
Cc: Jon Hunter <jonathanh@nvidia.com>
Cc: arm@kernel.org
Cc: bcm-kernel-feedback-list@broadcom.com
Cc: linux-arm-kernel@lists.infradead.org
Cc: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Ray Jui <rjui@broadcom.com>
Cc: "Hou Zhiqiang" <B48286@freescale.com>
Cc: Tirumalesh Chalamarla <tchalamarla@cavium.com>
Cc: linux-samsung-soc@vger.kernel.org
Cc: Yuan Yao <yao.yuan@nxp.com>
Cc: Jan Glauber <jglauber@cavium.com>
Cc: Gregory Clement <gregory.clement@free-electrons.com>
Cc: linux-amlogic@lists.infradead.org
Cc: soren.brinkmann@xilinx.com
Cc: Rajesh Bhagat <rajesh.bhagat@freescale.com>
Cc: Scott Branden <sbranden@broadcom.com>
Cc: Duc Dang <dhdang@apm.com>
Cc: Kukjin Kim <kgene@kernel.org>
Cc: Carlo Caione <carlo@caione.org>
Cc: Dinh Nguyen <dinguyen@opensource.altera.com>
Link: http://lkml.kernel.org/r/1470045256-9032-2-git-send-email-marc.zyngier@arm.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2016-08-01 17:54:15 +08:00
|
|
|
u32 flags;
|
2016-07-14 01:16:39 +08:00
|
|
|
|
2017-01-18 21:25:26 +08:00
|
|
|
__arch_timer_setup(ARCH_TIMER_TYPE_CP15, clk);
|
2012-11-12 22:33:44 +08:00
|
|
|
|
clocksource/arm_arch_timer: Force per-CPU interrupt to be level-triggered
The ARM architected timer produces level-triggered interrupts (this
is mandated by the architecture). Unfortunately, a number of
device-trees get this wrong, and expose an edge-triggered interrupt.
Until now, this wasn't too much an issue, as the programming of the
trigger would fail (the corresponding PPI cannot be reconfigured),
and the kernel would be happy with this. But we're about to change
this, and trust DT a lot if the driver doesn't provide its own
trigger information. In that context, the timer breaks badly.
While we do need to fix the DTs, there is also some userspace out
there (kvmtool) that generates the same kind of broken DT on the
fly, and that will completely break with newer kernels.
As a safety measure, and to keep buggy software alive as well as
buying us some time to fix DTs all over the place, let's check
what trigger configuration has been given us by the firmware.
If this is not a level configuration, then we know that the
DT/ACPI configuration is bust, and we pick some defaults which
won't be worse than the existing setup.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Liu Gang <Gang.Liu@nxp.com>
Cc: Mark Rutland <marc.rutland@arm.com>
Cc: Masahiro Yamada <yamada.masahiro@socionext.com>
Cc: Wenbin Song <Wenbin.Song@freescale.com>
Cc: Mingkai Hu <Mingkai.Hu@freescale.com>
Cc: Florian Fainelli <f.fainelli@gmail.com>
Cc: Kevin Hilman <khilman@baylibre.com>
Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
Cc: Michal Simek <michal.simek@xilinx.com>
Cc: Jon Hunter <jonathanh@nvidia.com>
Cc: arm@kernel.org
Cc: bcm-kernel-feedback-list@broadcom.com
Cc: linux-arm-kernel@lists.infradead.org
Cc: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Ray Jui <rjui@broadcom.com>
Cc: "Hou Zhiqiang" <B48286@freescale.com>
Cc: Tirumalesh Chalamarla <tchalamarla@cavium.com>
Cc: linux-samsung-soc@vger.kernel.org
Cc: Yuan Yao <yao.yuan@nxp.com>
Cc: Jan Glauber <jglauber@cavium.com>
Cc: Gregory Clement <gregory.clement@free-electrons.com>
Cc: linux-amlogic@lists.infradead.org
Cc: soren.brinkmann@xilinx.com
Cc: Rajesh Bhagat <rajesh.bhagat@freescale.com>
Cc: Scott Branden <sbranden@broadcom.com>
Cc: Duc Dang <dhdang@apm.com>
Cc: Kukjin Kim <kgene@kernel.org>
Cc: Carlo Caione <carlo@caione.org>
Cc: Dinh Nguyen <dinguyen@opensource.altera.com>
Link: http://lkml.kernel.org/r/1470045256-9032-2-git-send-email-marc.zyngier@arm.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2016-08-01 17:54:15 +08:00
|
|
|
flags = check_ppi_trigger(arch_timer_ppi[arch_timer_uses_ppi]);
|
|
|
|
enable_percpu_irq(arch_timer_ppi[arch_timer_uses_ppi], flags);
|
2014-02-20 23:21:23 +08:00
|
|
|
|
clocksource/arm_arch_timer: Force per-CPU interrupt to be level-triggered
The ARM architected timer produces level-triggered interrupts (this
is mandated by the architecture). Unfortunately, a number of
device-trees get this wrong, and expose an edge-triggered interrupt.
Until now, this wasn't too much an issue, as the programming of the
trigger would fail (the corresponding PPI cannot be reconfigured),
and the kernel would be happy with this. But we're about to change
this, and trust DT a lot if the driver doesn't provide its own
trigger information. In that context, the timer breaks badly.
While we do need to fix the DTs, there is also some userspace out
there (kvmtool) that generates the same kind of broken DT on the
fly, and that will completely break with newer kernels.
As a safety measure, and to keep buggy software alive as well as
buying us some time to fix DTs all over the place, let's check
what trigger configuration has been given us by the firmware.
If this is not a level configuration, then we know that the
DT/ACPI configuration is bust, and we pick some defaults which
won't be worse than the existing setup.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Liu Gang <Gang.Liu@nxp.com>
Cc: Mark Rutland <marc.rutland@arm.com>
Cc: Masahiro Yamada <yamada.masahiro@socionext.com>
Cc: Wenbin Song <Wenbin.Song@freescale.com>
Cc: Mingkai Hu <Mingkai.Hu@freescale.com>
Cc: Florian Fainelli <f.fainelli@gmail.com>
Cc: Kevin Hilman <khilman@baylibre.com>
Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
Cc: Michal Simek <michal.simek@xilinx.com>
Cc: Jon Hunter <jonathanh@nvidia.com>
Cc: arm@kernel.org
Cc: bcm-kernel-feedback-list@broadcom.com
Cc: linux-arm-kernel@lists.infradead.org
Cc: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Ray Jui <rjui@broadcom.com>
Cc: "Hou Zhiqiang" <B48286@freescale.com>
Cc: Tirumalesh Chalamarla <tchalamarla@cavium.com>
Cc: linux-samsung-soc@vger.kernel.org
Cc: Yuan Yao <yao.yuan@nxp.com>
Cc: Jan Glauber <jglauber@cavium.com>
Cc: Gregory Clement <gregory.clement@free-electrons.com>
Cc: linux-amlogic@lists.infradead.org
Cc: soren.brinkmann@xilinx.com
Cc: Rajesh Bhagat <rajesh.bhagat@freescale.com>
Cc: Scott Branden <sbranden@broadcom.com>
Cc: Duc Dang <dhdang@apm.com>
Cc: Kukjin Kim <kgene@kernel.org>
Cc: Carlo Caione <carlo@caione.org>
Cc: Dinh Nguyen <dinguyen@opensource.altera.com>
Link: http://lkml.kernel.org/r/1470045256-9032-2-git-send-email-marc.zyngier@arm.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2016-08-01 17:54:15 +08:00
|
|
|
if (arch_timer_has_nonsecure_ppi()) {
|
2017-01-18 21:25:27 +08:00
|
|
|
flags = check_ppi_trigger(arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI]);
|
|
|
|
enable_percpu_irq(arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI],
|
|
|
|
flags);
|
clocksource/arm_arch_timer: Force per-CPU interrupt to be level-triggered
The ARM architected timer produces level-triggered interrupts (this
is mandated by the architecture). Unfortunately, a number of
device-trees get this wrong, and expose an edge-triggered interrupt.
Until now, this wasn't too much an issue, as the programming of the
trigger would fail (the corresponding PPI cannot be reconfigured),
and the kernel would be happy with this. But we're about to change
this, and trust DT a lot if the driver doesn't provide its own
trigger information. In that context, the timer breaks badly.
While we do need to fix the DTs, there is also some userspace out
there (kvmtool) that generates the same kind of broken DT on the
fly, and that will completely break with newer kernels.
As a safety measure, and to keep buggy software alive as well as
buying us some time to fix DTs all over the place, let's check
what trigger configuration has been given us by the firmware.
If this is not a level configuration, then we know that the
DT/ACPI configuration is bust, and we pick some defaults which
won't be worse than the existing setup.
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
Cc: Andrew Lunn <andrew@lunn.ch>
Cc: Liu Gang <Gang.Liu@nxp.com>
Cc: Mark Rutland <marc.rutland@arm.com>
Cc: Masahiro Yamada <yamada.masahiro@socionext.com>
Cc: Wenbin Song <Wenbin.Song@freescale.com>
Cc: Mingkai Hu <Mingkai.Hu@freescale.com>
Cc: Florian Fainelli <f.fainelli@gmail.com>
Cc: Kevin Hilman <khilman@baylibre.com>
Cc: Daniel Lezcano <daniel.lezcano@linaro.org>
Cc: Michal Simek <michal.simek@xilinx.com>
Cc: Jon Hunter <jonathanh@nvidia.com>
Cc: arm@kernel.org
Cc: bcm-kernel-feedback-list@broadcom.com
Cc: linux-arm-kernel@lists.infradead.org
Cc: Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Ray Jui <rjui@broadcom.com>
Cc: "Hou Zhiqiang" <B48286@freescale.com>
Cc: Tirumalesh Chalamarla <tchalamarla@cavium.com>
Cc: linux-samsung-soc@vger.kernel.org
Cc: Yuan Yao <yao.yuan@nxp.com>
Cc: Jan Glauber <jglauber@cavium.com>
Cc: Gregory Clement <gregory.clement@free-electrons.com>
Cc: linux-amlogic@lists.infradead.org
Cc: soren.brinkmann@xilinx.com
Cc: Rajesh Bhagat <rajesh.bhagat@freescale.com>
Cc: Scott Branden <sbranden@broadcom.com>
Cc: Duc Dang <dhdang@apm.com>
Cc: Kukjin Kim <kgene@kernel.org>
Cc: Carlo Caione <carlo@caione.org>
Cc: Dinh Nguyen <dinguyen@opensource.altera.com>
Link: http://lkml.kernel.org/r/1470045256-9032-2-git-send-email-marc.zyngier@arm.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2016-08-01 17:54:15 +08:00
|
|
|
}
|
2012-11-12 22:33:44 +08:00
|
|
|
|
|
|
|
arch_counter_set_user_access();
|
2016-06-28 00:30:13 +08:00
|
|
|
if (evtstrm_enable)
|
2013-08-23 22:32:29 +08:00
|
|
|
arch_timer_configure_evtstream();
|
2012-11-12 22:33:44 +08:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-03-22 00:31:13 +08:00
|
|
|
/*
|
|
|
|
* For historical reasons, when probing with DT we use whichever (non-zero)
|
|
|
|
* rate was probed first, and don't verify that others match. If the first node
|
|
|
|
* probed has a clock-frequency property, this overrides the HW register.
|
|
|
|
*/
|
|
|
|
static void arch_timer_of_configure_rate(u32 rate, struct device_node *np)
|
2012-11-12 22:33:44 +08:00
|
|
|
{
|
2013-07-19 07:59:32 +08:00
|
|
|
/* Who has more than one independent system counter? */
|
|
|
|
if (arch_timer_rate)
|
|
|
|
return;
|
2012-11-12 22:33:44 +08:00
|
|
|
|
2017-03-22 00:31:13 +08:00
|
|
|
if (of_property_read_u32(np, "clock-frequency", &arch_timer_rate))
|
|
|
|
arch_timer_rate = rate;
|
2012-11-12 22:33:44 +08:00
|
|
|
|
2013-07-19 07:59:32 +08:00
|
|
|
/* Check the timer frequency. */
|
|
|
|
if (arch_timer_rate == 0)
|
2017-01-18 21:25:25 +08:00
|
|
|
pr_warn("frequency not available\n");
|
2013-07-19 07:59:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void arch_timer_banner(unsigned type)
|
|
|
|
{
|
2017-01-18 21:25:25 +08:00
|
|
|
pr_info("%s%s%s timer(s) running at %lu.%02luMHz (%s%s%s).\n",
|
2017-01-18 21:25:26 +08:00
|
|
|
type & ARCH_TIMER_TYPE_CP15 ? "cp15" : "",
|
|
|
|
type == (ARCH_TIMER_TYPE_CP15 | ARCH_TIMER_TYPE_MEM) ?
|
|
|
|
" and " : "",
|
|
|
|
type & ARCH_TIMER_TYPE_MEM ? "mmio" : "",
|
2017-01-18 21:25:25 +08:00
|
|
|
(unsigned long)arch_timer_rate / 1000000,
|
|
|
|
(unsigned long)(arch_timer_rate / 10000) % 100,
|
2017-01-18 21:25:26 +08:00
|
|
|
type & ARCH_TIMER_TYPE_CP15 ?
|
2017-01-18 21:25:27 +08:00
|
|
|
(arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI) ? "virt" : "phys" :
|
2013-07-19 07:59:32 +08:00
|
|
|
"",
|
2017-01-18 21:25:26 +08:00
|
|
|
type == (ARCH_TIMER_TYPE_CP15 | ARCH_TIMER_TYPE_MEM) ? "/" : "",
|
|
|
|
type & ARCH_TIMER_TYPE_MEM ?
|
2013-07-19 07:59:32 +08:00
|
|
|
arch_timer_mem_use_virtual ? "virt" : "phys" :
|
|
|
|
"");
|
2012-11-12 22:33:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
u32 arch_timer_get_rate(void)
|
|
|
|
{
|
|
|
|
return arch_timer_rate;
|
|
|
|
}
|
|
|
|
|
2017-10-13 21:32:55 +08:00
|
|
|
bool arch_timer_evtstrm_available(void)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* We might get called from a preemptible context. This is fine
|
|
|
|
* because availability of the event stream should be always the same
|
|
|
|
* for a preemptible context and context where we might resume a task.
|
|
|
|
*/
|
|
|
|
return cpumask_test_cpu(raw_smp_processor_id(), &evtstrm_available);
|
|
|
|
}
|
|
|
|
|
2013-07-19 07:59:32 +08:00
|
|
|
static u64 arch_counter_get_cntvct_mem(void)
|
2012-11-12 22:33:44 +08:00
|
|
|
{
|
2013-07-19 07:59:32 +08:00
|
|
|
u32 vct_lo, vct_hi, tmp_hi;
|
|
|
|
|
|
|
|
do {
|
|
|
|
vct_hi = readl_relaxed(arch_counter_base + CNTVCT_HI);
|
|
|
|
vct_lo = readl_relaxed(arch_counter_base + CNTVCT_LO);
|
|
|
|
tmp_hi = readl_relaxed(arch_counter_base + CNTVCT_HI);
|
|
|
|
} while (vct_hi != tmp_hi);
|
|
|
|
|
|
|
|
return ((u64) vct_hi << 32) | vct_lo;
|
2012-11-12 22:33:44 +08:00
|
|
|
}
|
|
|
|
|
2016-04-11 23:32:51 +08:00
|
|
|
static struct arch_timer_kvm_info arch_timer_kvm_info;
|
|
|
|
|
|
|
|
struct arch_timer_kvm_info *arch_timer_get_kvm_info(void)
|
|
|
|
{
|
|
|
|
return &arch_timer_kvm_info;
|
|
|
|
}
|
2012-11-12 22:33:44 +08:00
|
|
|
|
2013-07-19 07:59:32 +08:00
|
|
|
static void __init arch_counter_register(unsigned type)
|
|
|
|
{
|
|
|
|
u64 start_count;
|
|
|
|
|
|
|
|
/* Register the CP15 based counter if we have one */
|
2017-01-18 21:25:26 +08:00
|
|
|
if (type & ARCH_TIMER_TYPE_CP15) {
|
2017-07-05 17:04:28 +08:00
|
|
|
if ((IS_ENABLED(CONFIG_ARM64) && !is_hyp_mode_available()) ||
|
2017-01-18 21:25:27 +08:00
|
|
|
arch_timer_uses_ppi == ARCH_TIMER_VIRT_PPI)
|
2014-11-24 15:02:44 +08:00
|
|
|
arch_timer_read_counter = arch_counter_get_cntvct;
|
|
|
|
else
|
|
|
|
arch_timer_read_counter = arch_counter_get_cntpct;
|
2016-09-22 16:35:17 +08:00
|
|
|
|
2017-02-01 20:07:15 +08:00
|
|
|
clocksource_counter.archdata.vdso_direct = vdso_default;
|
2014-09-29 07:50:06 +08:00
|
|
|
} else {
|
2013-07-19 07:59:32 +08:00
|
|
|
arch_timer_read_counter = arch_counter_get_cntvct_mem;
|
2014-09-29 07:50:06 +08:00
|
|
|
}
|
|
|
|
|
2016-10-05 02:12:09 +08:00
|
|
|
if (!arch_counter_suspend_stop)
|
|
|
|
clocksource_counter.flags |= CLOCK_SOURCE_SUSPEND_NONSTOP;
|
2013-07-19 07:59:32 +08:00
|
|
|
start_count = arch_timer_read_counter();
|
|
|
|
clocksource_register_hz(&clocksource_counter, arch_timer_rate);
|
|
|
|
cyclecounter.mult = clocksource_counter.mult;
|
|
|
|
cyclecounter.shift = clocksource_counter.shift;
|
2016-04-11 23:32:51 +08:00
|
|
|
timecounter_init(&arch_timer_kvm_info.timecounter,
|
|
|
|
&cyclecounter, start_count);
|
2013-10-15 21:31:51 +08:00
|
|
|
|
|
|
|
/* 56 bits minimum, so we assume worst case rollover */
|
|
|
|
sched_clock_register(arch_timer_read_counter, 56, arch_timer_rate);
|
2013-07-19 07:59:32 +08:00
|
|
|
}
|
|
|
|
|
2013-06-19 23:32:08 +08:00
|
|
|
static void arch_timer_stop(struct clock_event_device *clk)
|
2012-11-12 22:33:44 +08:00
|
|
|
{
|
2017-01-18 21:25:25 +08:00
|
|
|
pr_debug("disable IRQ%d cpu #%d\n", clk->irq, smp_processor_id());
|
2012-11-12 22:33:44 +08:00
|
|
|
|
2014-02-20 23:21:23 +08:00
|
|
|
disable_percpu_irq(arch_timer_ppi[arch_timer_uses_ppi]);
|
|
|
|
if (arch_timer_has_nonsecure_ppi())
|
2017-01-18 21:25:27 +08:00
|
|
|
disable_percpu_irq(arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI]);
|
2012-11-12 22:33:44 +08:00
|
|
|
|
2015-06-12 16:00:12 +08:00
|
|
|
clk->set_state_shutdown(clk);
|
2012-11-12 22:33:44 +08:00
|
|
|
}
|
|
|
|
|
2016-07-14 01:16:39 +08:00
|
|
|
static int arch_timer_dying_cpu(unsigned int cpu)
|
2012-11-12 22:33:44 +08:00
|
|
|
{
|
2016-07-14 01:16:39 +08:00
|
|
|
struct clock_event_device *clk = this_cpu_ptr(arch_timer_evt);
|
2012-11-12 22:33:44 +08:00
|
|
|
|
2017-10-13 21:32:55 +08:00
|
|
|
cpumask_clear_cpu(smp_processor_id(), &evtstrm_available);
|
|
|
|
|
2016-07-14 01:16:39 +08:00
|
|
|
arch_timer_stop(clk);
|
|
|
|
return 0;
|
2012-11-12 22:33:44 +08:00
|
|
|
}
|
|
|
|
|
2013-08-23 22:53:15 +08:00
|
|
|
#ifdef CONFIG_CPU_PM
|
2017-04-05 00:05:16 +08:00
|
|
|
static DEFINE_PER_CPU(unsigned long, saved_cntkctl);
|
2013-08-23 22:53:15 +08:00
|
|
|
static int arch_timer_cpu_pm_notify(struct notifier_block *self,
|
|
|
|
unsigned long action, void *hcpu)
|
|
|
|
{
|
2017-10-13 21:32:55 +08:00
|
|
|
if (action == CPU_PM_ENTER) {
|
2017-04-05 00:05:16 +08:00
|
|
|
__this_cpu_write(saved_cntkctl, arch_timer_get_cntkctl());
|
2017-10-13 21:32:55 +08:00
|
|
|
|
|
|
|
cpumask_clear_cpu(smp_processor_id(), &evtstrm_available);
|
|
|
|
} else if (action == CPU_PM_ENTER_FAILED || action == CPU_PM_EXIT) {
|
2017-04-05 00:05:16 +08:00
|
|
|
arch_timer_set_cntkctl(__this_cpu_read(saved_cntkctl));
|
2017-10-13 21:32:55 +08:00
|
|
|
|
|
|
|
if (elf_hwcap & HWCAP_EVTSTRM)
|
|
|
|
cpumask_set_cpu(smp_processor_id(), &evtstrm_available);
|
|
|
|
}
|
2013-08-23 22:53:15 +08:00
|
|
|
return NOTIFY_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct notifier_block arch_timer_cpu_pm_notifier = {
|
|
|
|
.notifier_call = arch_timer_cpu_pm_notify,
|
|
|
|
};
|
|
|
|
|
|
|
|
static int __init arch_timer_cpu_pm_init(void)
|
|
|
|
{
|
|
|
|
return cpu_pm_register_notifier(&arch_timer_cpu_pm_notifier);
|
|
|
|
}
|
2016-07-14 01:16:39 +08:00
|
|
|
|
|
|
|
static void __init arch_timer_cpu_pm_deinit(void)
|
|
|
|
{
|
|
|
|
WARN_ON(cpu_pm_unregister_notifier(&arch_timer_cpu_pm_notifier));
|
|
|
|
}
|
|
|
|
|
2013-08-23 22:53:15 +08:00
|
|
|
#else
|
|
|
|
static int __init arch_timer_cpu_pm_init(void)
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2016-07-14 01:16:39 +08:00
|
|
|
|
|
|
|
static void __init arch_timer_cpu_pm_deinit(void)
|
|
|
|
{
|
|
|
|
}
|
2013-08-23 22:53:15 +08:00
|
|
|
#endif
|
|
|
|
|
2012-11-12 22:33:44 +08:00
|
|
|
static int __init arch_timer_register(void)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
int ppi;
|
|
|
|
|
|
|
|
arch_timer_evt = alloc_percpu(struct clock_event_device);
|
|
|
|
if (!arch_timer_evt) {
|
|
|
|
err = -ENOMEM;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2014-02-20 23:21:23 +08:00
|
|
|
ppi = arch_timer_ppi[arch_timer_uses_ppi];
|
|
|
|
switch (arch_timer_uses_ppi) {
|
2017-01-18 21:25:27 +08:00
|
|
|
case ARCH_TIMER_VIRT_PPI:
|
2012-11-12 22:33:44 +08:00
|
|
|
err = request_percpu_irq(ppi, arch_timer_handler_virt,
|
|
|
|
"arch_timer", arch_timer_evt);
|
2014-02-20 23:21:23 +08:00
|
|
|
break;
|
2017-01-18 21:25:27 +08:00
|
|
|
case ARCH_TIMER_PHYS_SECURE_PPI:
|
|
|
|
case ARCH_TIMER_PHYS_NONSECURE_PPI:
|
2012-11-12 22:33:44 +08:00
|
|
|
err = request_percpu_irq(ppi, arch_timer_handler_phys,
|
|
|
|
"arch_timer", arch_timer_evt);
|
2017-01-18 21:25:30 +08:00
|
|
|
if (!err && arch_timer_has_nonsecure_ppi()) {
|
2017-01-18 21:25:27 +08:00
|
|
|
ppi = arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI];
|
2012-11-12 22:33:44 +08:00
|
|
|
err = request_percpu_irq(ppi, arch_timer_handler_phys,
|
|
|
|
"arch_timer", arch_timer_evt);
|
|
|
|
if (err)
|
2017-01-18 21:25:27 +08:00
|
|
|
free_percpu_irq(arch_timer_ppi[ARCH_TIMER_PHYS_SECURE_PPI],
|
2012-11-12 22:33:44 +08:00
|
|
|
arch_timer_evt);
|
|
|
|
}
|
2014-02-20 23:21:23 +08:00
|
|
|
break;
|
2017-01-18 21:25:27 +08:00
|
|
|
case ARCH_TIMER_HYP_PPI:
|
2014-02-20 23:21:23 +08:00
|
|
|
err = request_percpu_irq(ppi, arch_timer_handler_phys,
|
|
|
|
"arch_timer", arch_timer_evt);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
BUG();
|
2012-11-12 22:33:44 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (err) {
|
2017-01-18 21:25:25 +08:00
|
|
|
pr_err("can't register interrupt %d (%d)\n", ppi, err);
|
2012-11-12 22:33:44 +08:00
|
|
|
goto out_free;
|
|
|
|
}
|
|
|
|
|
2013-08-23 22:53:15 +08:00
|
|
|
err = arch_timer_cpu_pm_init();
|
|
|
|
if (err)
|
|
|
|
goto out_unreg_notify;
|
|
|
|
|
2016-07-14 01:16:39 +08:00
|
|
|
/* Register and immediately configure the timer on the boot CPU */
|
|
|
|
err = cpuhp_setup_state(CPUHP_AP_ARM_ARCH_TIMER_STARTING,
|
2016-12-22 03:19:54 +08:00
|
|
|
"clockevents/arm/arch_timer:starting",
|
2016-07-14 01:16:39 +08:00
|
|
|
arch_timer_starting_cpu, arch_timer_dying_cpu);
|
|
|
|
if (err)
|
|
|
|
goto out_unreg_cpupm;
|
2012-11-12 22:33:44 +08:00
|
|
|
return 0;
|
|
|
|
|
2016-07-14 01:16:39 +08:00
|
|
|
out_unreg_cpupm:
|
|
|
|
arch_timer_cpu_pm_deinit();
|
|
|
|
|
2013-08-23 22:53:15 +08:00
|
|
|
out_unreg_notify:
|
2014-02-20 23:21:23 +08:00
|
|
|
free_percpu_irq(arch_timer_ppi[arch_timer_uses_ppi], arch_timer_evt);
|
|
|
|
if (arch_timer_has_nonsecure_ppi())
|
2017-01-18 21:25:27 +08:00
|
|
|
free_percpu_irq(arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI],
|
2012-11-12 22:33:44 +08:00
|
|
|
arch_timer_evt);
|
|
|
|
|
|
|
|
out_free:
|
|
|
|
free_percpu(arch_timer_evt);
|
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2013-07-19 07:59:32 +08:00
|
|
|
static int __init arch_timer_mem_register(void __iomem *base, unsigned int irq)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
irq_handler_t func;
|
|
|
|
struct arch_timer *t;
|
|
|
|
|
|
|
|
t = kzalloc(sizeof(*t), GFP_KERNEL);
|
|
|
|
if (!t)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
t->base = base;
|
|
|
|
t->evt.irq = irq;
|
2017-01-18 21:25:26 +08:00
|
|
|
__arch_timer_setup(ARCH_TIMER_TYPE_MEM, &t->evt);
|
2013-07-19 07:59:32 +08:00
|
|
|
|
|
|
|
if (arch_timer_mem_use_virtual)
|
|
|
|
func = arch_timer_handler_virt_mem;
|
|
|
|
else
|
|
|
|
func = arch_timer_handler_phys_mem;
|
|
|
|
|
|
|
|
ret = request_irq(irq, func, IRQF_TIMER, "arch_mem_timer", &t->evt);
|
|
|
|
if (ret) {
|
2017-01-18 21:25:25 +08:00
|
|
|
pr_err("Failed to request mem timer irq\n");
|
2013-07-19 07:59:32 +08:00
|
|
|
kfree(t);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct of_device_id arch_timer_of_match[] __initconst = {
|
|
|
|
{ .compatible = "arm,armv7-timer", },
|
|
|
|
{ .compatible = "arm,armv8-timer", },
|
|
|
|
{},
|
|
|
|
};
|
|
|
|
|
|
|
|
static const struct of_device_id arch_timer_mem_of_match[] __initconst = {
|
|
|
|
{ .compatible = "arm,armv7-timer-mem", },
|
|
|
|
{},
|
|
|
|
};
|
|
|
|
|
2017-03-22 00:31:14 +08:00
|
|
|
static bool __init arch_timer_needs_of_probing(void)
|
2014-09-29 07:50:05 +08:00
|
|
|
{
|
|
|
|
struct device_node *dn;
|
2015-03-31 18:12:22 +08:00
|
|
|
bool needs_probing = false;
|
2017-03-22 00:31:14 +08:00
|
|
|
unsigned int mask = ARCH_TIMER_TYPE_CP15 | ARCH_TIMER_TYPE_MEM;
|
2014-09-29 07:50:05 +08:00
|
|
|
|
2017-03-22 00:31:14 +08:00
|
|
|
/* We have two timers, and both device-tree nodes are probed. */
|
|
|
|
if ((arch_timers_present & mask) == mask)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Only one type of timer is probed,
|
|
|
|
* check if we have another type of timer node in device-tree.
|
|
|
|
*/
|
|
|
|
if (arch_timers_present & ARCH_TIMER_TYPE_CP15)
|
|
|
|
dn = of_find_matching_node(NULL, arch_timer_mem_of_match);
|
|
|
|
else
|
|
|
|
dn = of_find_matching_node(NULL, arch_timer_of_match);
|
|
|
|
|
|
|
|
if (dn && of_device_is_available(dn))
|
2015-03-31 18:12:22 +08:00
|
|
|
needs_probing = true;
|
2017-03-22 00:31:14 +08:00
|
|
|
|
2014-09-29 07:50:05 +08:00
|
|
|
of_node_put(dn);
|
|
|
|
|
2015-03-31 18:12:22 +08:00
|
|
|
return needs_probing;
|
2014-09-29 07:50:05 +08:00
|
|
|
}
|
|
|
|
|
2016-06-06 23:55:40 +08:00
|
|
|
static int __init arch_timer_common_init(void)
|
2013-07-19 07:59:32 +08:00
|
|
|
{
|
|
|
|
arch_timer_banner(arch_timers_present);
|
|
|
|
arch_counter_register(arch_timers_present);
|
2016-06-06 23:55:40 +08:00
|
|
|
return arch_timer_arch_init();
|
2013-07-19 07:59:32 +08:00
|
|
|
}
|
|
|
|
|
2017-01-18 21:25:30 +08:00
|
|
|
/**
|
|
|
|
* arch_timer_select_ppi() - Select suitable PPI for the current system.
|
|
|
|
*
|
|
|
|
* If HYP mode is available, we know that the physical timer
|
|
|
|
* has been configured to be accessible from PL1. Use it, so
|
|
|
|
* that a guest can use the virtual timer instead.
|
|
|
|
*
|
|
|
|
* On ARMv8.1 with VH extensions, the kernel runs in HYP. VHE
|
|
|
|
* accesses to CNTP_*_EL1 registers are silently redirected to
|
|
|
|
* their CNTHP_*_EL2 counterparts, and use a different PPI
|
|
|
|
* number.
|
|
|
|
*
|
|
|
|
* If no interrupt provided for virtual timer, we'll have to
|
|
|
|
* stick to the physical timer. It'd better be accessible...
|
|
|
|
* For arm64 we never use the secure interrupt.
|
|
|
|
*
|
|
|
|
* Return: a suitable PPI type for the current system.
|
|
|
|
*/
|
|
|
|
static enum arch_timer_ppi_nr __init arch_timer_select_ppi(void)
|
2012-11-12 22:33:44 +08:00
|
|
|
{
|
2017-01-18 21:25:30 +08:00
|
|
|
if (is_kernel_in_hyp_mode())
|
|
|
|
return ARCH_TIMER_HYP_PPI;
|
2014-02-20 23:21:23 +08:00
|
|
|
|
2017-01-18 21:25:30 +08:00
|
|
|
if (!is_hyp_mode_available() && arch_timer_ppi[ARCH_TIMER_VIRT_PPI])
|
|
|
|
return ARCH_TIMER_VIRT_PPI;
|
2012-11-12 22:33:44 +08:00
|
|
|
|
2017-01-18 21:25:30 +08:00
|
|
|
if (IS_ENABLED(CONFIG_ARM64))
|
|
|
|
return ARCH_TIMER_PHYS_NONSECURE_PPI;
|
|
|
|
|
|
|
|
return ARCH_TIMER_PHYS_SECURE_PPI;
|
|
|
|
}
|
|
|
|
|
2016-06-06 23:55:40 +08:00
|
|
|
static int __init arch_timer_of_init(struct device_node *np)
|
2015-03-24 22:02:50 +08:00
|
|
|
{
|
2017-03-22 00:31:15 +08:00
|
|
|
int i, ret;
|
2017-03-22 00:31:13 +08:00
|
|
|
u32 rate;
|
2015-03-24 22:02:50 +08:00
|
|
|
|
2017-01-18 21:25:26 +08:00
|
|
|
if (arch_timers_present & ARCH_TIMER_TYPE_CP15) {
|
2017-01-18 21:25:25 +08:00
|
|
|
pr_warn("multiple nodes in dt, skipping\n");
|
2016-06-06 23:55:40 +08:00
|
|
|
return 0;
|
2015-03-24 22:02:50 +08:00
|
|
|
}
|
|
|
|
|
2017-01-18 21:25:26 +08:00
|
|
|
arch_timers_present |= ARCH_TIMER_TYPE_CP15;
|
2017-01-18 21:25:27 +08:00
|
|
|
for (i = ARCH_TIMER_PHYS_SECURE_PPI; i < ARCH_TIMER_MAX_TIMER_PPI; i++)
|
2015-03-24 22:02:50 +08:00
|
|
|
arch_timer_ppi[i] = irq_of_parse_and_map(np, i);
|
|
|
|
|
2017-03-22 00:31:15 +08:00
|
|
|
arch_timer_kvm_info.virtual_irq = arch_timer_ppi[ARCH_TIMER_VIRT_PPI];
|
|
|
|
|
2017-04-01 01:51:00 +08:00
|
|
|
rate = arch_timer_get_cntfrq();
|
2017-03-22 00:31:13 +08:00
|
|
|
arch_timer_of_configure_rate(rate, np);
|
2015-03-24 22:02:50 +08:00
|
|
|
|
|
|
|
arch_timer_c3stop = !of_property_read_bool(np, "always-on");
|
|
|
|
|
2017-01-20 01:20:59 +08:00
|
|
|
/* Check for globally applicable workarounds */
|
|
|
|
arch_timer_check_ool_workaround(ate_match_dt, np);
|
2016-09-22 16:35:17 +08:00
|
|
|
|
2015-03-24 22:02:50 +08:00
|
|
|
/*
|
|
|
|
* If we cannot rely on firmware initializing the timer registers then
|
|
|
|
* we should use the physical timers instead.
|
|
|
|
*/
|
|
|
|
if (IS_ENABLED(CONFIG_ARM) &&
|
|
|
|
of_property_read_bool(np, "arm,cpu-registers-not-fw-configured"))
|
2017-01-18 21:25:27 +08:00
|
|
|
arch_timer_uses_ppi = ARCH_TIMER_PHYS_SECURE_PPI;
|
2017-01-18 21:25:30 +08:00
|
|
|
else
|
|
|
|
arch_timer_uses_ppi = arch_timer_select_ppi();
|
|
|
|
|
|
|
|
if (!arch_timer_ppi[arch_timer_uses_ppi]) {
|
|
|
|
pr_err("No interrupt available, giving up\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2015-03-24 22:02:50 +08:00
|
|
|
|
2016-10-05 02:12:09 +08:00
|
|
|
/* On some systems, the counter stops ticking when in suspend. */
|
|
|
|
arch_counter_suspend_stop = of_property_read_bool(np,
|
|
|
|
"arm,no-tick-in-suspend");
|
|
|
|
|
2017-03-22 00:31:15 +08:00
|
|
|
ret = arch_timer_register();
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
|
|
|
if (arch_timer_needs_of_probing())
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return arch_timer_common_init();
|
2015-03-24 22:02:50 +08:00
|
|
|
}
|
2017-05-26 22:56:11 +08:00
|
|
|
TIMER_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_of_init);
|
|
|
|
TIMER_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_of_init);
|
2013-07-19 07:59:32 +08:00
|
|
|
|
2017-04-01 01:51:00 +08:00
|
|
|
static u32 __init
|
|
|
|
arch_timer_mem_frame_get_cntfrq(struct arch_timer_mem_frame *frame)
|
2013-07-19 07:59:32 +08:00
|
|
|
{
|
2017-04-01 01:51:00 +08:00
|
|
|
void __iomem *base;
|
|
|
|
u32 rate;
|
2013-07-19 07:59:32 +08:00
|
|
|
|
2017-04-01 01:51:00 +08:00
|
|
|
base = ioremap(frame->cntbase, frame->size);
|
|
|
|
if (!base) {
|
|
|
|
pr_err("Unable to map frame @ %pa\n", &frame->cntbase);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-06-10 08:26:32 +08:00
|
|
|
rate = readl_relaxed(base + CNTFRQ);
|
2017-04-01 01:51:00 +08:00
|
|
|
|
2017-06-10 08:26:32 +08:00
|
|
|
iounmap(base);
|
2017-04-01 01:51:00 +08:00
|
|
|
|
|
|
|
return rate;
|
|
|
|
}
|
|
|
|
|
|
|
|
static struct arch_timer_mem_frame * __init
|
|
|
|
arch_timer_mem_find_best_frame(struct arch_timer_mem *timer_mem)
|
|
|
|
{
|
|
|
|
struct arch_timer_mem_frame *frame, *best_frame = NULL;
|
|
|
|
void __iomem *cntctlbase;
|
|
|
|
u32 cnttidr;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
cntctlbase = ioremap(timer_mem->cntctlbase, timer_mem->size);
|
2013-07-19 07:59:32 +08:00
|
|
|
if (!cntctlbase) {
|
2017-04-01 01:51:00 +08:00
|
|
|
pr_err("Can't map CNTCTLBase @ %pa\n",
|
|
|
|
&timer_mem->cntctlbase);
|
|
|
|
return NULL;
|
2013-07-19 07:59:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
cnttidr = readl_relaxed(cntctlbase + CNTTIDR);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to find a virtual capable frame. Otherwise fall back to a
|
|
|
|
* physical capable frame.
|
|
|
|
*/
|
2017-04-01 01:51:00 +08:00
|
|
|
for (i = 0; i < ARCH_TIMER_MEM_MAX_FRAMES; i++) {
|
|
|
|
u32 cntacr = CNTACR_RFRQ | CNTACR_RWPT | CNTACR_RPCT |
|
|
|
|
CNTACR_RWVT | CNTACR_RVOFF | CNTACR_RVCT;
|
2013-07-19 07:59:32 +08:00
|
|
|
|
2017-04-01 01:51:00 +08:00
|
|
|
frame = &timer_mem->frame[i];
|
|
|
|
if (!frame->valid)
|
|
|
|
continue;
|
2013-07-19 07:59:32 +08:00
|
|
|
|
2016-02-01 20:00:48 +08:00
|
|
|
/* Try enabling everything, and see what sticks */
|
2017-04-01 01:51:00 +08:00
|
|
|
writel_relaxed(cntacr, cntctlbase + CNTACR(i));
|
|
|
|
cntacr = readl_relaxed(cntctlbase + CNTACR(i));
|
2016-02-01 20:00:48 +08:00
|
|
|
|
2017-04-01 01:51:00 +08:00
|
|
|
if ((cnttidr & CNTTIDR_VIRT(i)) &&
|
2016-02-01 20:00:48 +08:00
|
|
|
!(~cntacr & (CNTACR_RWVT | CNTACR_RVCT))) {
|
2013-07-19 07:59:32 +08:00
|
|
|
best_frame = frame;
|
|
|
|
arch_timer_mem_use_virtual = true;
|
|
|
|
break;
|
|
|
|
}
|
2016-02-01 20:00:48 +08:00
|
|
|
|
|
|
|
if (~cntacr & (CNTACR_RWPT | CNTACR_RPCT))
|
|
|
|
continue;
|
|
|
|
|
2017-04-01 01:51:00 +08:00
|
|
|
best_frame = frame;
|
2013-07-19 07:59:32 +08:00
|
|
|
}
|
|
|
|
|
2017-04-01 01:51:00 +08:00
|
|
|
iounmap(cntctlbase);
|
|
|
|
|
2017-05-08 20:32:27 +08:00
|
|
|
return best_frame;
|
2017-04-01 01:51:00 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static int __init
|
|
|
|
arch_timer_mem_frame_register(struct arch_timer_mem_frame *frame)
|
|
|
|
{
|
|
|
|
void __iomem *base;
|
|
|
|
int ret, irq = 0;
|
2013-07-19 07:59:32 +08:00
|
|
|
|
|
|
|
if (arch_timer_mem_use_virtual)
|
2017-04-01 01:51:00 +08:00
|
|
|
irq = frame->virt_irq;
|
2013-07-19 07:59:32 +08:00
|
|
|
else
|
2017-04-01 01:51:00 +08:00
|
|
|
irq = frame->phys_irq;
|
2016-02-01 20:00:48 +08:00
|
|
|
|
2013-07-19 07:59:32 +08:00
|
|
|
if (!irq) {
|
2017-01-18 21:25:25 +08:00
|
|
|
pr_err("Frame missing %s irq.\n",
|
2013-08-21 20:59:23 +08:00
|
|
|
arch_timer_mem_use_virtual ? "virt" : "phys");
|
2017-04-01 01:51:00 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!request_mem_region(frame->cntbase, frame->size,
|
|
|
|
"arch_mem_timer"))
|
|
|
|
return -EBUSY;
|
|
|
|
|
|
|
|
base = ioremap(frame->cntbase, frame->size);
|
|
|
|
if (!base) {
|
|
|
|
pr_err("Can't map frame's registers\n");
|
|
|
|
return -ENXIO;
|
2013-07-19 07:59:32 +08:00
|
|
|
}
|
|
|
|
|
2016-06-06 23:55:40 +08:00
|
|
|
ret = arch_timer_mem_register(base, irq);
|
2017-04-01 01:51:00 +08:00
|
|
|
if (ret) {
|
|
|
|
iounmap(base);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
arch_counter_base = base;
|
|
|
|
arch_timers_present |= ARCH_TIMER_TYPE_MEM;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __init arch_timer_mem_of_init(struct device_node *np)
|
|
|
|
{
|
|
|
|
struct arch_timer_mem *timer_mem;
|
|
|
|
struct arch_timer_mem_frame *frame;
|
|
|
|
struct device_node *frame_node;
|
|
|
|
struct resource res;
|
|
|
|
int ret = -EINVAL;
|
|
|
|
u32 rate;
|
|
|
|
|
|
|
|
timer_mem = kzalloc(sizeof(*timer_mem), GFP_KERNEL);
|
|
|
|
if (!timer_mem)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
if (of_address_to_resource(np, 0, &res))
|
2016-06-06 23:55:40 +08:00
|
|
|
goto out;
|
2017-04-01 01:51:00 +08:00
|
|
|
timer_mem->cntctlbase = res.start;
|
|
|
|
timer_mem->size = resource_size(&res);
|
2016-06-06 23:55:40 +08:00
|
|
|
|
2017-04-01 01:51:00 +08:00
|
|
|
for_each_available_child_of_node(np, frame_node) {
|
|
|
|
u32 n;
|
|
|
|
struct arch_timer_mem_frame *frame;
|
|
|
|
|
|
|
|
if (of_property_read_u32(frame_node, "frame-number", &n)) {
|
|
|
|
pr_err(FW_BUG "Missing frame-number.\n");
|
|
|
|
of_node_put(frame_node);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
if (n >= ARCH_TIMER_MEM_MAX_FRAMES) {
|
|
|
|
pr_err(FW_BUG "Wrong frame-number, only 0-%u are permitted.\n",
|
|
|
|
ARCH_TIMER_MEM_MAX_FRAMES - 1);
|
|
|
|
of_node_put(frame_node);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
frame = &timer_mem->frame[n];
|
|
|
|
|
|
|
|
if (frame->valid) {
|
|
|
|
pr_err(FW_BUG "Duplicated frame-number.\n");
|
|
|
|
of_node_put(frame_node);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (of_address_to_resource(frame_node, 0, &res)) {
|
|
|
|
of_node_put(frame_node);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
frame->cntbase = res.start;
|
|
|
|
frame->size = resource_size(&res);
|
|
|
|
|
|
|
|
frame->virt_irq = irq_of_parse_and_map(frame_node,
|
|
|
|
ARCH_TIMER_VIRT_SPI);
|
|
|
|
frame->phys_irq = irq_of_parse_and_map(frame_node,
|
|
|
|
ARCH_TIMER_PHYS_SPI);
|
|
|
|
|
|
|
|
frame->valid = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
frame = arch_timer_mem_find_best_frame(timer_mem);
|
|
|
|
if (!frame) {
|
2017-10-16 23:28:38 +08:00
|
|
|
pr_err("Unable to find a suitable frame in timer @ %pa\n",
|
|
|
|
&timer_mem->cntctlbase);
|
2017-04-01 01:51:00 +08:00
|
|
|
ret = -EINVAL;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
rate = arch_timer_mem_frame_get_cntfrq(frame);
|
|
|
|
arch_timer_of_configure_rate(rate, np);
|
|
|
|
|
|
|
|
ret = arch_timer_mem_frame_register(frame);
|
|
|
|
if (!ret && !arch_timer_needs_of_probing())
|
2017-03-22 00:31:15 +08:00
|
|
|
ret = arch_timer_common_init();
|
2016-02-01 20:00:48 +08:00
|
|
|
out:
|
2017-04-01 01:51:00 +08:00
|
|
|
kfree(timer_mem);
|
2016-06-06 23:55:40 +08:00
|
|
|
return ret;
|
2013-07-19 07:59:32 +08:00
|
|
|
}
|
2017-05-26 22:56:11 +08:00
|
|
|
TIMER_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem",
|
2017-04-01 01:51:00 +08:00
|
|
|
arch_timer_mem_of_init);
|
2015-03-24 22:02:50 +08:00
|
|
|
|
2017-04-01 01:51:02 +08:00
|
|
|
#ifdef CONFIG_ACPI_GTDT
|
2017-04-01 01:51:04 +08:00
|
|
|
static int __init
|
|
|
|
arch_timer_mem_verify_cntfrq(struct arch_timer_mem *timer_mem)
|
|
|
|
{
|
|
|
|
struct arch_timer_mem_frame *frame;
|
|
|
|
u32 rate;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < ARCH_TIMER_MEM_MAX_FRAMES; i++) {
|
|
|
|
frame = &timer_mem->frame[i];
|
|
|
|
|
|
|
|
if (!frame->valid)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
rate = arch_timer_mem_frame_get_cntfrq(frame);
|
|
|
|
if (rate == arch_timer_rate)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
pr_err(FW_BUG "CNTFRQ mismatch: frame @ %pa: (0x%08lx), CPU: (0x%08lx)\n",
|
|
|
|
&frame->cntbase,
|
|
|
|
(unsigned long)rate, (unsigned long)arch_timer_rate);
|
|
|
|
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int __init arch_timer_mem_acpi_init(int platform_timer_count)
|
|
|
|
{
|
|
|
|
struct arch_timer_mem *timers, *timer;
|
2017-10-16 23:28:38 +08:00
|
|
|
struct arch_timer_mem_frame *frame, *best_frame = NULL;
|
2017-04-01 01:51:04 +08:00
|
|
|
int timer_count, i, ret = 0;
|
|
|
|
|
|
|
|
timers = kcalloc(platform_timer_count, sizeof(*timers),
|
|
|
|
GFP_KERNEL);
|
|
|
|
if (!timers)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ret = acpi_arch_timer_mem_init(timers, &timer_count);
|
|
|
|
if (ret || !timer_count)
|
|
|
|
goto out;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* While unlikely, it's theoretically possible that none of the frames
|
|
|
|
* in a timer expose the combination of feature we want.
|
|
|
|
*/
|
2017-08-01 02:37:28 +08:00
|
|
|
for (i = 0; i < timer_count; i++) {
|
2017-04-01 01:51:04 +08:00
|
|
|
timer = &timers[i];
|
|
|
|
|
|
|
|
frame = arch_timer_mem_find_best_frame(timer);
|
2017-10-16 23:28:38 +08:00
|
|
|
if (!best_frame)
|
|
|
|
best_frame = frame;
|
|
|
|
|
|
|
|
ret = arch_timer_mem_verify_cntfrq(timer);
|
|
|
|
if (ret) {
|
|
|
|
pr_err("Disabling MMIO timers due to CNTFRQ mismatch\n");
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!best_frame) /* implies !frame */
|
|
|
|
/*
|
|
|
|
* Only complain about missing suitable frames if we
|
|
|
|
* haven't already found one in a previous iteration.
|
|
|
|
*/
|
|
|
|
pr_err("Unable to find a suitable frame in timer @ %pa\n",
|
|
|
|
&timer->cntctlbase);
|
2017-04-01 01:51:04 +08:00
|
|
|
}
|
|
|
|
|
2017-10-16 23:28:38 +08:00
|
|
|
if (best_frame)
|
|
|
|
ret = arch_timer_mem_frame_register(best_frame);
|
2017-04-01 01:51:04 +08:00
|
|
|
out:
|
|
|
|
kfree(timers);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Initialize per-processor generic timer and memory-mapped timer(if present) */
|
2015-03-24 22:02:50 +08:00
|
|
|
static int __init arch_timer_acpi_init(struct acpi_table_header *table)
|
|
|
|
{
|
2017-04-01 01:51:04 +08:00
|
|
|
int ret, platform_timer_count;
|
2015-03-24 22:02:50 +08:00
|
|
|
|
2017-01-18 21:25:26 +08:00
|
|
|
if (arch_timers_present & ARCH_TIMER_TYPE_CP15) {
|
2017-01-18 21:25:25 +08:00
|
|
|
pr_warn("already initialized, skipping\n");
|
2015-03-24 22:02:50 +08:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2017-01-18 21:25:26 +08:00
|
|
|
arch_timers_present |= ARCH_TIMER_TYPE_CP15;
|
2015-03-24 22:02:50 +08:00
|
|
|
|
2017-04-01 01:51:04 +08:00
|
|
|
ret = acpi_gtdt_init(table, &platform_timer_count);
|
2017-04-01 01:51:02 +08:00
|
|
|
if (ret) {
|
|
|
|
pr_err("Failed to init GTDT table.\n");
|
|
|
|
return ret;
|
|
|
|
}
|
2015-03-24 22:02:50 +08:00
|
|
|
|
2017-01-18 21:25:27 +08:00
|
|
|
arch_timer_ppi[ARCH_TIMER_PHYS_NONSECURE_PPI] =
|
2017-04-01 01:51:02 +08:00
|
|
|
acpi_gtdt_map_ppi(ARCH_TIMER_PHYS_NONSECURE_PPI);
|
2015-03-24 22:02:50 +08:00
|
|
|
|
2017-01-18 21:25:27 +08:00
|
|
|
arch_timer_ppi[ARCH_TIMER_VIRT_PPI] =
|
2017-04-01 01:51:02 +08:00
|
|
|
acpi_gtdt_map_ppi(ARCH_TIMER_VIRT_PPI);
|
2015-03-24 22:02:50 +08:00
|
|
|
|
2017-01-18 21:25:27 +08:00
|
|
|
arch_timer_ppi[ARCH_TIMER_HYP_PPI] =
|
2017-04-01 01:51:02 +08:00
|
|
|
acpi_gtdt_map_ppi(ARCH_TIMER_HYP_PPI);
|
2015-03-24 22:02:50 +08:00
|
|
|
|
2017-03-22 00:31:15 +08:00
|
|
|
arch_timer_kvm_info.virtual_irq = arch_timer_ppi[ARCH_TIMER_VIRT_PPI];
|
|
|
|
|
2017-03-22 00:31:13 +08:00
|
|
|
/*
|
|
|
|
* When probing via ACPI, we have no mechanism to override the sysreg
|
|
|
|
* CNTFRQ value. This *must* be correct.
|
|
|
|
*/
|
|
|
|
arch_timer_rate = arch_timer_get_cntfrq();
|
|
|
|
if (!arch_timer_rate) {
|
|
|
|
pr_err(FW_BUG "frequency not available.\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2015-03-24 22:02:50 +08:00
|
|
|
|
2017-01-18 21:25:30 +08:00
|
|
|
arch_timer_uses_ppi = arch_timer_select_ppi();
|
|
|
|
if (!arch_timer_ppi[arch_timer_uses_ppi]) {
|
|
|
|
pr_err("No interrupt available, giving up\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2015-03-24 22:02:50 +08:00
|
|
|
/* Always-on capability */
|
2017-04-01 01:51:02 +08:00
|
|
|
arch_timer_c3stop = acpi_gtdt_c3stop(arch_timer_uses_ppi);
|
2015-03-24 22:02:50 +08:00
|
|
|
|
2017-02-21 22:37:30 +08:00
|
|
|
/* Check for globally applicable workarounds */
|
|
|
|
arch_timer_check_ool_workaround(ate_match_acpi_oem_info, table);
|
|
|
|
|
2017-03-22 00:31:15 +08:00
|
|
|
ret = arch_timer_register();
|
|
|
|
if (ret)
|
|
|
|
return ret;
|
|
|
|
|
2017-04-01 01:51:04 +08:00
|
|
|
if (platform_timer_count &&
|
|
|
|
arch_timer_mem_acpi_init(platform_timer_count))
|
|
|
|
pr_err("Failed to initialize memory-mapped timer.\n");
|
|
|
|
|
2017-03-22 00:31:15 +08:00
|
|
|
return arch_timer_common_init();
|
2015-03-24 22:02:50 +08:00
|
|
|
}
|
2017-05-26 23:42:25 +08:00
|
|
|
TIMER_ACPI_DECLARE(arch_timer, ACPI_SIG_GTDT, arch_timer_acpi_init);
|
2015-03-24 22:02:50 +08:00
|
|
|
#endif
|