Needs polishing, probably going back and forth to integrate it cleanly.
Signed-off-by: Daniel Lezcano daniel.lezcano@linaro.org --- drivers/acpi/acpi_gtdt.c | 174 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 drivers/acpi/acpi_gtdt.c
diff --git a/drivers/acpi/acpi_gtdt.c b/drivers/acpi/acpi_gtdt.c new file mode 100644 index 0000000..cb5ac2b --- /dev/null +++ b/drivers/acpi/acpi_gtdt.c @@ -0,0 +1,174 @@ +/* + * ARM Specific GTDT table Support + * + * Copyright (C) 2016, Linaro Ltd. + * Author: Fu Wei fu.wei@linaro.org + * Hanjun Guo hanjun.guo@linaro.org + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/acpi.h> +#include <linux/device.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> + +enum ppi_nr { + PHYS_SECURE_PPI, + PHYS_NONSECURE_PPI, + VIRT_PPI, + HYP_PPI, + MAX_TIMER_PPI +}; + +struct acpi_gtdt_desc { + struct acpi_table_gtdt *gtdt; + void *start; + void *end; +}; + +static struct acpi_gtdt_desc acpi_gtdt_desc __initdata; + +static inline void *gtdt_next(struct acpi_table_gtdt *gtdt, void *end, int type) +{ + struct acpi_gtdt_header *gh = (struct acpi_gtdt_header *)gtdt; + + while ((void *)(gh += gh->length) < end) + if (gh->type == type) + return (void *)gtdt; + return NULL; +} + +#define for_each_gtdt_type(_g, _t) \ + for (_g = acpi_gtdt_desc.start; _g; \ + _g = gtdt_next(_g, acpi_gtdt_desc.end, _t)) + +#define for_each_gtdt_timer(_g) \ + for_each_gtdt_type(_g, ACPI_GTDT_TYPE_TIMER_BLOCK) + +#define for_each_gtdt_watchdog(_g) \ + for_each_gtdt_type(_g, ACPI_GTDT_TYPE_WATCHDOG) + + +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); +} + +/** + * acpi_gtdt_map_irq - + * @flag: + * + * + */ +int __init acpi_gtdt_map_irq(int flag) +{ + struct acpi_table_gtdt *gtdt = acpi_gtdt_desc.gtdt; + + switch(flag) { + case PHYS_SECURE_PPI: + return map_generic_timer_interrupt(gtdt->secure_el1_interrupt, + gtdt->secure_el1_flags); + case PHYS_NONSECURE_PPI: + return map_generic_timer_interrupt(gtdt->non_secure_el1_interrupt, + gtdt->non_secure_el1_flags); + case VIRT_PPI: + return map_generic_timer_interrupt(gtdt->virtual_timer_interrupt, + gtdt->virtual_timer_flags); + + case HYP_PPI: + return map_generic_timer_interrupt(gtdt->non_secure_el2_interrupt, + gtdt->non_secure_el2_flags); + default: + return -EINVAL; + } +} + +/** + * acpi_gtdt_c3stop - + * + * Returns 1 if the timer is powered in deep idle state, 0 otherwise. + */ +int __init acpi_gtdt_c3stop(void) +{ + struct acpi_table_gtdt *gtdt = acpi_gtdt_desc.gtdt; + + return !(gtdt->non_secure_el1_flags & ACPI_GTDT_ALWAYS_ON); +} + +/** + * acpi_gtdt_timer_init - + * + * + */ +int __init acpi_gtdt_timer_init(void) +{ + struct acpi_table_gtdt *gtdt = acpi_gtdt_desc.gtdt; + int count = 0; + + if (gtdt->header.revision < 2) + return -ENOTSUPP; + + for_each_gtdt_timer(gtdt) { + /* */ + count++; + } + + pr_notice("Found %d timer(s)", count); + + return 0; +} + +/** + * acpi_gtdt_watchdog_init - + * + */ +int __init acpi_gtdt_watchdog_init(void) +{ + struct acpi_table_gtdt *gtdt = acpi_gtdt_desc.gtdt; + int count = 0; + + for_each_gtdt_watchdog(gtdt) { + /* */ + count++; + } + + pr_notice("Found %d watchdog(s)", count); + + return 0; +} + +/** + * acpi_gtdt_init - + * + */ +int __init acpi_gtdt_init(void) +{ + struct acpi_table_header *table; + + if (acpi_disabled) + return 0; + + if (ACPI_FAILURE(acpi_get_table(ACPI_SIG_GTDT, 0, &table))) + return -EINVAL; + + acpi_gtdt_desc.gtdt = container_of(table, struct acpi_table_gtdt, header); + acpi_gtdt_desc.start = acpi_gtdt_desc.gtdt + acpi_gtdt_desc.gtdt->platform_timer_offset; + acpi_gtdt_desc.end = (void *)table + table->length; + + return 0; +}