Commit b09ca1ec authored by Hanjun Guo's avatar Hanjun Guo Committed by Will Deacon

clocksource / arch_timer: Parse GTDT to initialize arch timer

Using the information presented by GTDT (Generic Timer Description Table)
to initialize the arch timer (not memory-mapped).

CC: Daniel Lezcano <daniel.lezcano@linaro.org>
CC: Thomas Gleixner <tglx@linutronix.de>
Originally-by: default avatarAmit Daniel Kachhap <amit.daniel@samsung.com>
Tested-by: default avatarSuravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>
Tested-by: default avatarYijing Wang <wangyijing@huawei.com>
Tested-by: default avatarMark Langsdorf <mlangsdo@redhat.com>
Tested-by: default avatarJon Masters <jcm@redhat.com>
Tested-by: default avatarTimur Tabi <timur@codeaurora.org>
Tested-by: default avatarRobert Richter <rrichter@cavium.com>
Acked-by: default avatarRobert Richter <rrichter@cavium.com>
Acked-by: default avatarDaniel Lezcano <daniel.lezcano@linaro.org>
Reviewed-by: default avatarGrant Likely <grant.likely@linaro.org>
Signed-off-by: default avatarHanjun Guo <hanjun.guo@linaro.org>
Signed-off-by: default avatarWill Deacon <will.deacon@arm.com>
parent d60fc389
...@@ -35,6 +35,7 @@ ...@@ -35,6 +35,7 @@
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/clocksource.h> #include <linux/clocksource.h>
#include <linux/clk-provider.h> #include <linux/clk-provider.h>
#include <linux/acpi.h>
#include <clocksource/arm_arch_timer.h> #include <clocksource/arm_arch_timer.h>
...@@ -72,6 +73,12 @@ void __init time_init(void) ...@@ -72,6 +73,12 @@ void __init time_init(void)
tick_setup_hrtimer_broadcast(); tick_setup_hrtimer_broadcast();
/*
* Since ACPI or FDT will only one be available in the system,
* we can use acpi_generic_timer_init() here safely
*/
acpi_generic_timer_init();
arch_timer_rate = arch_timer_get_rate(); arch_timer_rate = arch_timer_get_rate();
if (!arch_timer_rate) if (!arch_timer_rate)
panic("Unable to initialise architected timer.\n"); panic("Unable to initialise architected timer.\n");
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/sched_clock.h> #include <linux/sched_clock.h>
#include <linux/acpi.h>
#include <asm/arch_timer.h> #include <asm/arch_timer.h>
#include <asm/virt.h> #include <asm/virt.h>
...@@ -371,8 +372,12 @@ arch_timer_detect_rate(void __iomem *cntbase, struct device_node *np) ...@@ -371,8 +372,12 @@ arch_timer_detect_rate(void __iomem *cntbase, struct device_node *np)
if (arch_timer_rate) if (arch_timer_rate)
return; return;
/* Try to determine the frequency from the device tree or CNTFRQ */ /*
if (of_property_read_u32(np, "clock-frequency", &arch_timer_rate)) { * Try to determine the frequency from the device tree or CNTFRQ,
* if ACPI is enabled, get the frequency from CNTFRQ ONLY.
*/
if (!acpi_disabled ||
of_property_read_u32(np, "clock-frequency", &arch_timer_rate)) {
if (cntbase) if (cntbase)
arch_timer_rate = readl_relaxed(cntbase + CNTFRQ); arch_timer_rate = readl_relaxed(cntbase + CNTFRQ);
else else
...@@ -691,28 +696,8 @@ static void __init arch_timer_common_init(void) ...@@ -691,28 +696,8 @@ static void __init arch_timer_common_init(void)
arch_timer_arch_init(); arch_timer_arch_init();
} }
static void __init arch_timer_init(struct device_node *np) static void __init arch_timer_init(void)
{ {
int i;
if (arch_timers_present & ARCH_CP15_TIMER) {
pr_warn("arch_timer: multiple nodes in dt, skipping\n");
return;
}
arch_timers_present |= ARCH_CP15_TIMER;
for (i = PHYS_SECURE_PPI; i < MAX_TIMER_PPI; i++)
arch_timer_ppi[i] = irq_of_parse_and_map(np, i);
arch_timer_detect_rate(NULL, np);
/*
* 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"))
arch_timer_use_virtual = false;
/* /*
* If HYP mode is available, we know that the physical timer * If HYP mode is available, we know that the physical timer
* has been configured to be accessible from PL1. Use it, so * has been configured to be accessible from PL1. Use it, so
...@@ -731,13 +716,39 @@ static void __init arch_timer_init(struct device_node *np) ...@@ -731,13 +716,39 @@ static void __init arch_timer_init(struct device_node *np)
} }
} }
arch_timer_c3stop = !of_property_read_bool(np, "always-on");
arch_timer_register(); arch_timer_register();
arch_timer_common_init(); arch_timer_common_init();
} }
CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_init);
CLOCKSOURCE_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_init); static void __init arch_timer_of_init(struct device_node *np)
{
int i;
if (arch_timers_present & ARCH_CP15_TIMER) {
pr_warn("arch_timer: multiple nodes in dt, skipping\n");
return;
}
arch_timers_present |= ARCH_CP15_TIMER;
for (i = PHYS_SECURE_PPI; i < MAX_TIMER_PPI; i++)
arch_timer_ppi[i] = irq_of_parse_and_map(np, i);
arch_timer_detect_rate(NULL, np);
arch_timer_c3stop = !of_property_read_bool(np, "always-on");
/*
* 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"))
arch_timer_use_virtual = false;
arch_timer_init();
}
CLOCKSOURCE_OF_DECLARE(armv7_arch_timer, "arm,armv7-timer", arch_timer_of_init);
CLOCKSOURCE_OF_DECLARE(armv8_arch_timer, "arm,armv8-timer", arch_timer_of_init);
static void __init arch_timer_mem_init(struct device_node *np) static void __init arch_timer_mem_init(struct device_node *np)
{ {
...@@ -804,3 +815,70 @@ static void __init arch_timer_mem_init(struct device_node *np) ...@@ -804,3 +815,70 @@ static void __init arch_timer_mem_init(struct device_node *np)
} }
CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem", CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem",
arch_timer_mem_init); arch_timer_mem_init);
#ifdef CONFIG_ACPI
static int __init map_generic_timer_interrupt(u32 interrupt, u32 flags)
{
int trigger, polarity;
if (!interrupt)
return 0;
trigger = (flags & ACPI_GTDT_INTERRUPT_MODE) ? ACPI_EDGE_SENSITIVE
: ACPI_LEVEL_SENSITIVE;
polarity = (flags & ACPI_GTDT_INTERRUPT_POLARITY) ? ACPI_ACTIVE_LOW
: ACPI_ACTIVE_HIGH;
return acpi_register_gsi(NULL, interrupt, trigger, polarity);
}
/* Initialize per-processor generic timer */
static int __init arch_timer_acpi_init(struct acpi_table_header *table)
{
struct acpi_table_gtdt *gtdt;
if (arch_timers_present & ARCH_CP15_TIMER) {
pr_warn("arch_timer: already initialized, skipping\n");
return -EINVAL;
}
gtdt = container_of(table, struct acpi_table_gtdt, header);
arch_timers_present |= ARCH_CP15_TIMER;
arch_timer_ppi[PHYS_SECURE_PPI] =
map_generic_timer_interrupt(gtdt->secure_el1_interrupt,
gtdt->secure_el1_flags);
arch_timer_ppi[PHYS_NONSECURE_PPI] =
map_generic_timer_interrupt(gtdt->non_secure_el1_interrupt,
gtdt->non_secure_el1_flags);
arch_timer_ppi[VIRT_PPI] =
map_generic_timer_interrupt(gtdt->virtual_timer_interrupt,
gtdt->virtual_timer_flags);
arch_timer_ppi[HYP_PPI] =
map_generic_timer_interrupt(gtdt->non_secure_el2_interrupt,
gtdt->non_secure_el2_flags);
/* Get the frequency from CNTFRQ */
arch_timer_detect_rate(NULL, NULL);
/* Always-on capability */
arch_timer_c3stop = !(gtdt->non_secure_el1_flags & ACPI_GTDT_ALWAYS_ON);
arch_timer_init();
return 0;
}
/* Initialize all the generic timers presented in GTDT */
void __init acpi_generic_timer_init(void)
{
if (acpi_disabled)
return;
acpi_table_parse(ACPI_SIG_GTDT, arch_timer_acpi_init);
}
#endif
...@@ -244,4 +244,10 @@ extern void clocksource_of_init(void); ...@@ -244,4 +244,10 @@ extern void clocksource_of_init(void);
static inline void clocksource_of_init(void) {} static inline void clocksource_of_init(void) {}
#endif #endif
#ifdef CONFIG_ACPI
void acpi_generic_timer_init(void);
#else
static inline void acpi_generic_timer_init(void) { }
#endif
#endif /* _LINUX_CLOCKSOURCE_H */ #endif /* _LINUX_CLOCKSOURCE_H */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment