@@ -53,6 +53,7 @@ cflatobjs += lib/arm/psci.o
cflatobjs += lib/arm/smp.o
cflatobjs += lib/arm/delay.o
cflatobjs += lib/arm/gic.o lib/arm/gic-v2.o lib/arm/gic-v3.o
+cflatobjs += lib/arm/timer.o
OBJDIRS += lib/arm
@@ -27,5 +27,7 @@ extern struct timer_state __timer_state;
#define TIMER_PTIMER_IRQ (__timer_state.ptimer.irq)
#define TIMER_VTIMER_IRQ (__timer_state.vtimer.irq)
+void timer_save_state(void);
+
#endif /* !__ASSEMBLY__ */
#endif /* _ASMARM_TIMER_H_ */
@@ -18,6 +18,7 @@
#define FACP_SIGNATURE ACPI_SIGNATURE('F','A','C','P')
#define FACS_SIGNATURE ACPI_SIGNATURE('F','A','C','S')
#define SPCR_SIGNATURE ACPI_SIGNATURE('S','P','C','R')
+#define GTDT_SIGNATURE ACPI_SIGNATURE('G','T','D','T')
#define ACPI_SIGNATURE_8BYTE(c1, c2, c3, c4, c5, c6, c7, c8) \
@@ -173,6 +174,23 @@ struct spcr_descriptor {
u32 reserved2;
};
+struct acpi_table_gtdt {
+ ACPI_TABLE_HEADER_DEF /* ACPI common table header */
+ u64 counter_block_addresss;
+ u32 reserved;
+ u32 secure_el1_interrupt;
+ u32 secure_el1_flags;
+ u32 non_secure_el1_interrupt;
+ u32 non_secure_el1_flags;
+ u32 virtual_timer_interrupt;
+ u32 virtual_timer_flags;
+ u32 non_secure_el2_interrupt;
+ u32 non_secure_el2_flags;
+ u64 counter_read_block_address;
+ u32 platform_timer_count;
+ u32 platform_timer_offset;
+};
+
#pragma pack(0)
void set_efi_rsdp(struct rsdp_descriptor *rsdp);
@@ -35,8 +35,6 @@
extern unsigned long etext;
-struct timer_state __timer_state;
-
char *initrd;
u32 initrd_size;
@@ -199,43 +197,6 @@ static void mem_init(phys_addr_t freemem_start)
page_alloc_ops_enable();
}
-static void timer_save_state(void)
-{
- const struct fdt_property *prop;
- const void *fdt = dt_fdt();
- int node, len;
- u32 *data;
-
- node = fdt_node_offset_by_compatible(fdt, -1, "arm,armv8-timer");
- assert(node >= 0 || node == -FDT_ERR_NOTFOUND);
-
- if (node == -FDT_ERR_NOTFOUND) {
- __timer_state.ptimer.irq = -1;
- __timer_state.vtimer.irq = -1;
- return;
- }
-
- /*
- * From Linux devicetree timer binding documentation
- *
- * interrupts <type irq flags>:
- * secure timer irq
- * non-secure timer irq (ptimer)
- * virtual timer irq (vtimer)
- * hypervisor timer irq
- */
- prop = fdt_get_property(fdt, node, "interrupts", &len);
- assert(prop && len == (4 * 3 * sizeof(u32)));
-
- data = (u32 *)prop->data;
- assert(fdt32_to_cpu(data[3]) == 1 /* PPI */);
- __timer_state.ptimer.irq = fdt32_to_cpu(data[4]);
- __timer_state.ptimer.irq_flags = fdt32_to_cpu(data[5]);
- assert(fdt32_to_cpu(data[6]) == 1 /* PPI */);
- __timer_state.vtimer.irq = fdt32_to_cpu(data[7]);
- __timer_state.vtimer.irq_flags = fdt32_to_cpu(data[8]);
-}
-
void setup(const void *fdt, phys_addr_t freemem_start)
{
void *freemem;
new file mode 100644
@@ -0,0 +1,79 @@
+/*
+ * Initialize timers.
+ *
+ * Copyright (C) 2022, Arm Ltd., Nikos Nikoleris <nikos.nikoleris@arm.com>
+ * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
+ *
+ * This work is licensed under the terms of the GNU LGPL, version 2.
+ */
+#include <libcflat.h>
+#include <acpi.h>
+#include <devicetree.h>
+#include <libfdt/libfdt.h>
+#include <asm/gic.h>
+#include <asm/timer.h>
+
+struct timer_state __timer_state;
+
+static void timer_save_state_fdt(void)
+{
+ const struct fdt_property *prop;
+ const void *fdt = dt_fdt();
+ int node, len;
+ u32 *data;
+
+ node = fdt_node_offset_by_compatible(fdt, -1, "arm,armv8-timer");
+ assert(node >= 0 || node == -FDT_ERR_NOTFOUND);
+
+ if (node == -FDT_ERR_NOTFOUND) {
+ __timer_state.ptimer.irq = -1;
+ __timer_state.vtimer.irq = -1;
+ return;
+ }
+
+ /*
+ * From Linux devicetree timer binding documentation
+ *
+ * interrupts <type irq flags>:
+ * secure timer irq
+ * non-secure timer irq (ptimer)
+ * virtual timer irq (vtimer)
+ * hypervisor timer irq
+ */
+ prop = fdt_get_property(fdt, node, "interrupts", &len);
+ assert(prop && len == (4 * 3 * sizeof(u32)));
+
+ data = (u32 *)prop->data;
+ assert(fdt32_to_cpu(data[3]) == 1 /* PPI */);
+ __timer_state.ptimer.irq = PPI(fdt32_to_cpu(data[4]));
+ __timer_state.ptimer.irq_flags = fdt32_to_cpu(data[5]);
+ assert(fdt32_to_cpu(data[6]) == 1 /* PPI */);
+ __timer_state.vtimer.irq = PPI(fdt32_to_cpu(data[7]));
+ __timer_state.vtimer.irq_flags = fdt32_to_cpu(data[8]);
+}
+
+static void timer_save_state_acpi(void)
+{
+ struct acpi_table_gtdt *gtdt = find_acpi_table_addr(GTDT_SIGNATURE);
+
+ if (!gtdt) {
+ printf("Cannot find ACPI GTDT");
+ __timer_state.ptimer.irq = -1;
+ __timer_state.vtimer.irq = -1;
+ return;
+ }
+
+ __timer_state.ptimer.irq = gtdt->non_secure_el1_interrupt;
+ __timer_state.ptimer.irq_flags = gtdt->non_secure_el1_flags;
+
+ __timer_state.vtimer.irq = gtdt->virtual_timer_interrupt;
+ __timer_state.vtimer.irq_flags = gtdt->virtual_timer_flags;
+}
+
+void timer_save_state(void)
+{
+ if (dt_available())
+ timer_save_state_fdt();
+ else
+ timer_save_state_acpi();
+}
@@ -43,7 +43,7 @@ static void gic_irq_handler(struct pt_regs *regs)
irq_received = true;
gic_write_eoir(irqstat);
- if (irqstat == PPI(TIMER_VTIMER_IRQ)) {
+ if (irqstat == TIMER_VTIMER_IRQ) {
write_sysreg((ARCH_TIMER_CTL_IMASK | ARCH_TIMER_CTL_ENABLE),
cntv_ctl_el0);
isb();
@@ -229,7 +229,7 @@ static bool timer_prep(void)
assert_msg(0, "Unreachable");
}
- writel(1 << PPI(TIMER_VTIMER_IRQ), gic_isenabler);
+ writel(1 << TIMER_VTIMER_IRQ, gic_isenabler);
write_sysreg(ARCH_TIMER_CTL_IMASK | ARCH_TIMER_CTL_ENABLE, cntv_ctl_el0);
isb();
@@ -139,7 +139,7 @@ static struct timer_info ptimer_info = {
static void set_timer_irq_enabled(struct timer_info *info, bool enabled)
{
- u32 val = 1 << PPI(info->irq);
+ u32 val = 1 << info->irq;
if (enabled)
writel(val, gic_isenabler);
@@ -153,9 +153,9 @@ static void irq_handler(struct pt_regs *regs)
u32 irqstat = gic_read_iar();
u32 irqnr = gic_iar_irqnr(irqstat);
- if (irqnr == PPI(vtimer_info.irq)) {
+ if (irqnr == vtimer_info.irq) {
info = &vtimer_info;
- } else if (irqnr == PPI(ptimer_info.irq)) {
+ } else if (irqnr == ptimer_info.irq) {
info = &ptimer_info;
} else {
if (irqnr != GICC_INT_SPURIOUS)
@@ -185,9 +185,9 @@ static bool gic_timer_check_state(struct timer_info *info,
/* Wait for up to 1s for the GIC to sample the interrupt. */
for (i = 0; i < 10; i++) {
mdelay(100);
- if (gic_irq_state(PPI(info->irq)) == expected_state) {
+ if (gic_irq_state(info->irq) == expected_state) {
mdelay(100);
- if (gic_irq_state(PPI(info->irq)) == expected_state)
+ if (gic_irq_state(info->irq) == expected_state)
return true;
}
}