diff mbox

[v8,17/21] clocksource / arch_timer: Parse GTDT to initialize arch timer

Message ID 1422881149-8177-18-git-send-email-hanjun.guo@linaro.org (mailing list archive)
State New, archived
Headers show

Commit Message

Hanjun Guo Feb. 2, 2015, 12:45 p.m. UTC
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>
Originally-by: Amit Daniel Kachhap <amit.daniel@samsung.com>
Tested-by: Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>
Tested-by: Yijing Wang <wangyijing@huawei.com>
Tested-by: Mark Langsdorf <mlangsdo@redhat.com>
Tested-by: Jon Masters <jcm@redhat.com>
Tested-by: Timur Tabi <timur@codeaurora.org>
Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
---
 arch/arm64/kernel/time.c             |   7 ++
 drivers/clocksource/arm_arch_timer.c | 132 ++++++++++++++++++++++++++++-------
 include/linux/clocksource.h          |   6 ++
 3 files changed, 118 insertions(+), 27 deletions(-)

Comments

Rafael J. Wysocki Feb. 2, 2015, 10:23 p.m. UTC | #1
On Monday, February 02, 2015 08:45:45 PM Hanjun Guo wrote:
> 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>
> Originally-by: Amit Daniel Kachhap <amit.daniel@samsung.com>
> Tested-by: Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>
> Tested-by: Yijing Wang <wangyijing@huawei.com>
> Tested-by: Mark Langsdorf <mlangsdo@redhat.com>
> Tested-by: Jon Masters <jcm@redhat.com>
> Tested-by: Timur Tabi <timur@codeaurora.org>
> Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
> ---
>  arch/arm64/kernel/time.c             |   7 ++
>  drivers/clocksource/arm_arch_timer.c | 132 ++++++++++++++++++++++++++++-------

An ACK from Thomas Gleixner is necessary for that.

>  include/linux/clocksource.h          |   6 ++
>  3 files changed, 118 insertions(+), 27 deletions(-)
> 
> diff --git a/arch/arm64/kernel/time.c b/arch/arm64/kernel/time.c
> index 1a7125c..42f9195 100644
> --- a/arch/arm64/kernel/time.c
> +++ b/arch/arm64/kernel/time.c
> @@ -35,6 +35,7 @@
>  #include <linux/delay.h>
>  #include <linux/clocksource.h>
>  #include <linux/clk-provider.h>
> +#include <linux/acpi.h>
>  
>  #include <clocksource/arm_arch_timer.h>
>  
> @@ -72,6 +73,12 @@ void __init time_init(void)
>  
>  	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();
>  	if (!arch_timer_rate)
>  		panic("Unable to initialise architected timer.\n");
> diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
> index 095c177..407aa63 100644
> --- a/drivers/clocksource/arm_arch_timer.c
> +++ b/drivers/clocksource/arm_arch_timer.c
> @@ -21,6 +21,7 @@
>  #include <linux/io.h>
>  #include <linux/slab.h>
>  #include <linux/sched_clock.h>
> +#include <linux/acpi.h>
>  
>  #include <asm/arch_timer.h>
>  #include <asm/virt.h>
> @@ -370,8 +371,12 @@ arch_timer_detect_rate(void __iomem *cntbase, struct device_node *np)
>  	if (arch_timer_rate)
>  		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)
>  			arch_timer_rate = readl_relaxed(cntbase + CNTFRQ);
>  		else
> @@ -690,28 +695,8 @@ static void __init arch_timer_common_init(void)
>  	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
>  	 * has been configured to be accessible from PL1. Use it, so
> @@ -730,13 +715,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_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)
>  {
> @@ -803,3 +814,70 @@ static void __init arch_timer_mem_init(struct device_node *np)
>  }
>  CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem",
>  		       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
> diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h
> index abcafaa..af6155a 100644
> --- a/include/linux/clocksource.h
> +++ b/include/linux/clocksource.h
> @@ -346,4 +346,10 @@ extern void clocksource_of_init(void);
>  static inline void clocksource_of_init(void) {}
>  #endif
>  
> +#ifdef CONFIG_ACPI
> +void acpi_generic_timer_init(void);
> +#else
> +static inline void acpi_generic_timer_init(void) { }
> +#endif
> +
>  #endif /* _LINUX_CLOCKSOURCE_H */
>
Hanjun Guo Feb. 3, 2015, 1:28 p.m. UTC | #2
On 2015?02?03? 06:23, Rafael J. Wysocki wrote:
> On Monday, February 02, 2015 08:45:45 PM Hanjun Guo wrote:
>> 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>
>> Originally-by: Amit Daniel Kachhap <amit.daniel@samsung.com>
>> Tested-by: Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>
>> Tested-by: Yijing Wang <wangyijing@huawei.com>
>> Tested-by: Mark Langsdorf <mlangsdo@redhat.com>
>> Tested-by: Jon Masters <jcm@redhat.com>
>> Tested-by: Timur Tabi <timur@codeaurora.org>
>> Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
>> ---
>>   arch/arm64/kernel/time.c             |   7 ++
>>   drivers/clocksource/arm_arch_timer.c | 132 ++++++++++++++++++++++++++++-------
>
> An ACK from Thomas Gleixner is necessary for that.

I will CC Thomas in the change log in next version
with both patch 16/17.

Thanks
Hanjun
Lorenzo Pieralisi Feb. 4, 2015, 6:59 p.m. UTC | #3
On Mon, Feb 02, 2015 at 12:45:45PM +0000, Hanjun Guo wrote:
> Using the information presented by GTDT (Generic Timer Description Table)
> to initialize the arch timer (not memory-mapped).

Why are you not initializing the memory mapped timer ?

> CC: Daniel Lezcano <daniel.lezcano@linaro.org>
> Originally-by: Amit Daniel Kachhap <amit.daniel@samsung.com>
> Tested-by: Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>
> Tested-by: Yijing Wang <wangyijing@huawei.com>
> Tested-by: Mark Langsdorf <mlangsdo@redhat.com>
> Tested-by: Jon Masters <jcm@redhat.com>
> Tested-by: Timur Tabi <timur@codeaurora.org>
> Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
> ---
>  arch/arm64/kernel/time.c             |   7 ++
>  drivers/clocksource/arm_arch_timer.c | 132 ++++++++++++++++++++++++++++-------
>  include/linux/clocksource.h          |   6 ++
>  3 files changed, 118 insertions(+), 27 deletions(-)
> 
> diff --git a/arch/arm64/kernel/time.c b/arch/arm64/kernel/time.c
> index 1a7125c..42f9195 100644
> --- a/arch/arm64/kernel/time.c
> +++ b/arch/arm64/kernel/time.c
> @@ -35,6 +35,7 @@
>  #include <linux/delay.h>
>  #include <linux/clocksource.h>
>  #include <linux/clk-provider.h>
> +#include <linux/acpi.h>
>  
>  #include <clocksource/arm_arch_timer.h>
>  
> @@ -72,6 +73,12 @@ void __init time_init(void)
>  
>  	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();
>  	if (!arch_timer_rate)
>  		panic("Unable to initialise architected timer.\n");
> diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
> index 095c177..407aa63 100644
> --- a/drivers/clocksource/arm_arch_timer.c
> +++ b/drivers/clocksource/arm_arch_timer.c
> @@ -21,6 +21,7 @@
>  #include <linux/io.h>
>  #include <linux/slab.h>
>  #include <linux/sched_clock.h>
> +#include <linux/acpi.h>
>  
>  #include <asm/arch_timer.h>
>  #include <asm/virt.h>
> @@ -370,8 +371,12 @@ arch_timer_detect_rate(void __iomem *cntbase, struct device_node *np)
>  	if (arch_timer_rate)
>  		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)) {

This is getting a mess. cntbase tells you it is a memory mapped timer,
node pointer that you are probing through DT, and to top it all
acpi_disabled detects if you are probing in ACPI or DT mode.

I think this function should be simplified, this driver is also
pending a refactoring to split arch timer and the memory mapped one
so I think you'd better wait that work to make things simpler.

[...]

> +/* Initialize all the generic timers presented in GTDT */
> +void __init acpi_generic_timer_init(void)
> +{
> +	if (acpi_disabled)
> +		return;

acpi_disabled used again here, I repeat myself this is going to be
hard to track. You should try to organize the code something like:

if (acpi_disabled)
	timer_dt_probe();
else
	timer_acpi_probe();

mixing the code paths is getting unwieldy, see above to see my
reasoning.

> +
> +	acpi_table_parse(ACPI_SIG_GTDT, arch_timer_acpi_init);
> +}
> +#endif
> diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h
> index abcafaa..af6155a 100644
> --- a/include/linux/clocksource.h
> +++ b/include/linux/clocksource.h
> @@ -346,4 +346,10 @@ extern void clocksource_of_init(void);
>  static inline void clocksource_of_init(void) {}
>  #endif
>  
> +#ifdef CONFIG_ACPI
> +void acpi_generic_timer_init(void);
> +#else
> +static inline void acpi_generic_timer_init(void) { }
> +#endif
> +

That's not nice, it is a generic header, arch specific stuff should be
avoided. I think you should have an ACPI generic layer similar to
clocksource_of_init(), and probe from there when matching the respective
timers.

Lorenzo
Hanjun Guo Feb. 5, 2015, 10:11 a.m. UTC | #4
Hi Lorenzo,

On 2015?02?05? 02:59, Lorenzo Pieralisi wrote:
> On Mon, Feb 02, 2015 at 12:45:45PM +0000, Hanjun Guo wrote:
>> Using the information presented by GTDT (Generic Timer Description Table)
>> to initialize the arch timer (not memory-mapped).
>
> Why are you not initializing the memory mapped timer ?

We left it for later work because no need for that to boot
available ARM64 hardware at now, and we have no hardware to
test unless I missed some of platforms.

>
>> CC: Daniel Lezcano <daniel.lezcano@linaro.org>
>> Originally-by: Amit Daniel Kachhap <amit.daniel@samsung.com>
>> Tested-by: Suravee Suthikulpanit <Suravee.Suthikulpanit@amd.com>
>> Tested-by: Yijing Wang <wangyijing@huawei.com>
>> Tested-by: Mark Langsdorf <mlangsdo@redhat.com>
>> Tested-by: Jon Masters <jcm@redhat.com>
>> Tested-by: Timur Tabi <timur@codeaurora.org>
>> Signed-off-by: Hanjun Guo <hanjun.guo@linaro.org>
>> ---
>>   arch/arm64/kernel/time.c             |   7 ++
>>   drivers/clocksource/arm_arch_timer.c | 132 ++++++++++++++++++++++++++++-------
>>   include/linux/clocksource.h          |   6 ++
>>   3 files changed, 118 insertions(+), 27 deletions(-)
>>
>> diff --git a/arch/arm64/kernel/time.c b/arch/arm64/kernel/time.c
>> index 1a7125c..42f9195 100644
>> --- a/arch/arm64/kernel/time.c
>> +++ b/arch/arm64/kernel/time.c
>> @@ -35,6 +35,7 @@
>>   #include <linux/delay.h>
>>   #include <linux/clocksource.h>
>>   #include <linux/clk-provider.h>
>> +#include <linux/acpi.h>
>>
>>   #include <clocksource/arm_arch_timer.h>
>>
>> @@ -72,6 +73,12 @@ void __init time_init(void)
>>
>>   	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();
>>   	if (!arch_timer_rate)
>>   		panic("Unable to initialise architected timer.\n");
>> diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
>> index 095c177..407aa63 100644
>> --- a/drivers/clocksource/arm_arch_timer.c
>> +++ b/drivers/clocksource/arm_arch_timer.c
>> @@ -21,6 +21,7 @@
>>   #include <linux/io.h>
>>   #include <linux/slab.h>
>>   #include <linux/sched_clock.h>
>> +#include <linux/acpi.h>
>>
>>   #include <asm/arch_timer.h>
>>   #include <asm/virt.h>
>> @@ -370,8 +371,12 @@ arch_timer_detect_rate(void __iomem *cntbase, struct device_node *np)
>>   	if (arch_timer_rate)
>>   		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)) {
>
> This is getting a mess. cntbase tells you it is a memory mapped timer,
> node pointer that you are probing through DT, and to top it all
> acpi_disabled detects if you are probing in ACPI or DT mode.
>
> I think this function should be simplified, this driver is also
> pending a refactoring to split arch timer and the memory mapped one
> so I think you'd better wait that work to make things simpler.

Does anyone working on this now?

>
> [...]
>
>> +/* Initialize all the generic timers presented in GTDT */
>> +void __init acpi_generic_timer_init(void)
>> +{
>> +	if (acpi_disabled)
>> +		return;
>
> acpi_disabled used again here, I repeat myself this is going to be
> hard to track. You should try to organize the code something like:
>
> if (acpi_disabled)
> 	timer_dt_probe();
> else
> 	timer_acpi_probe();
>
> mixing the code paths is getting unwieldy, see above to see my
> reasoning.

Olof is unhappy with such approach, I think if (acpi_disabled) is
self-contained because we only get DT or ACPI in the system, we
can call this function time_init() without more if (acpi_disabled).

>
>> +
>> +	acpi_table_parse(ACPI_SIG_GTDT, arch_timer_acpi_init);
>> +}
>> +#endif
>> diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h
>> index abcafaa..af6155a 100644
>> --- a/include/linux/clocksource.h
>> +++ b/include/linux/clocksource.h
>> @@ -346,4 +346,10 @@ extern void clocksource_of_init(void);
>>   static inline void clocksource_of_init(void) {}
>>   #endif
>>
>> +#ifdef CONFIG_ACPI
>> +void acpi_generic_timer_init(void);
>> +#else
>> +static inline void acpi_generic_timer_init(void) { }
>> +#endif
>> +
>
> That's not nice, it is a generic header, arch specific stuff should be
> avoided. I think you should have an ACPI generic layer similar to
> clocksource_of_init(), and probe from there when matching the respective
> timers.

I think I'm OK with it, but do we really need to introduce a heavy
framework to init just arm arch timer (memory mapped or not) ?

Thanks
Hanjun
diff mbox

Patch

diff --git a/arch/arm64/kernel/time.c b/arch/arm64/kernel/time.c
index 1a7125c..42f9195 100644
--- a/arch/arm64/kernel/time.c
+++ b/arch/arm64/kernel/time.c
@@ -35,6 +35,7 @@ 
 #include <linux/delay.h>
 #include <linux/clocksource.h>
 #include <linux/clk-provider.h>
+#include <linux/acpi.h>
 
 #include <clocksource/arm_arch_timer.h>
 
@@ -72,6 +73,12 @@  void __init time_init(void)
 
 	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();
 	if (!arch_timer_rate)
 		panic("Unable to initialise architected timer.\n");
diff --git a/drivers/clocksource/arm_arch_timer.c b/drivers/clocksource/arm_arch_timer.c
index 095c177..407aa63 100644
--- a/drivers/clocksource/arm_arch_timer.c
+++ b/drivers/clocksource/arm_arch_timer.c
@@ -21,6 +21,7 @@ 
 #include <linux/io.h>
 #include <linux/slab.h>
 #include <linux/sched_clock.h>
+#include <linux/acpi.h>
 
 #include <asm/arch_timer.h>
 #include <asm/virt.h>
@@ -370,8 +371,12 @@  arch_timer_detect_rate(void __iomem *cntbase, struct device_node *np)
 	if (arch_timer_rate)
 		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)
 			arch_timer_rate = readl_relaxed(cntbase + CNTFRQ);
 		else
@@ -690,28 +695,8 @@  static void __init arch_timer_common_init(void)
 	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
 	 * has been configured to be accessible from PL1. Use it, so
@@ -730,13 +715,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_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)
 {
@@ -803,3 +814,70 @@  static void __init arch_timer_mem_init(struct device_node *np)
 }
 CLOCKSOURCE_OF_DECLARE(armv7_arch_timer_mem, "arm,armv7-timer-mem",
 		       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
diff --git a/include/linux/clocksource.h b/include/linux/clocksource.h
index abcafaa..af6155a 100644
--- a/include/linux/clocksource.h
+++ b/include/linux/clocksource.h
@@ -346,4 +346,10 @@  extern void clocksource_of_init(void);
 static inline void clocksource_of_init(void) {}
 #endif
 
+#ifdef CONFIG_ACPI
+void acpi_generic_timer_init(void);
+#else
+static inline void acpi_generic_timer_init(void) { }
+#endif
+
 #endif /* _LINUX_CLOCKSOURCE_H */