This uses the generic arch_early_time support added in the pervious patch to define an ARM globaltimer-based implementation that can be configured through CONFIG_.
It uses the early time to provide detailed and consistent time information in your logs. For example, what used to look like this
[ 0.000000] Booting Linux on physical CPU 0x0 [ 0.000000] Linux version 3.11.0-linaro_fjslt-tracking-bsp2.0.0-rc1+ (agreen@build.warmcat.com) (gcc version 4.7.3 20130205 (prerelease) (crosstool-NG linaro-1.13.1-4.7-2013.02-01-20130221 - Linaro GCC 2013.02) ) #193 SMP PREEMPT Thu Aug 8 09:13:53 CST 2013 [ 0.000000] CPU: ARMv7 Processor [412fc092] revision 2 (ARMv7), cr=50c5387d [ 0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache [ 0.000000] Machine: Fujitsu Semiconductor MB8AC0300-EVB, model: Fujitsu MB8AC0300 Evaluation Board [ 0.000000] early_init_dt_add_memory_arch: 40000000 10000000 [ 0.000000] cma: CMA: reserved 64 MiB at 4c000000 [ 0.000000] Memory policy: ECC disabled, Data cache writealloc [ 0.000000] On node 0 totalpages: 65536 [ 0.000000] Normal zone: 512 pages used for memmap [ 0.000000] Normal zone: 0 pages reserved [ 0.000000] Normal zone: 65536 pages, LIFO batch:15 [ 0.000000] PERCPU: Embedded 7 pages/cpu @804f4000 s7296 r8192 d13184 u32768 [ 0.000000] pcpu-alloc: s7296 r8192 d13184 u32768 alloc=8*4096 [ 0.000000] pcpu-alloc: [0] 0 [0] 1 [ 0.000000] Built 1 zonelists in Zone order, mobility grouping on. Total pages: 65024 [ 0.000000] Kernel command line: loglevel=4 mb8ac0300_ts.adjust_y=-40 console=tty0 console=ttyS0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rw rootwait [ 0.000000] PID hash table entries: 1024 (order: 0, 4096 bytes) [ 0.000000] Dentry cache hash table entries: 32768 (order: 5, 131072 bytes) [ 0.000000] Inode-cache hash table entries: 16384 (order: 4, 65536 bytes) [ 0.000000] Memory: 191100K/262144K available (1734K kernel code, 115K rwdata, 716K rodata, 107K init, 236K bss, 71044K reserved, 0K highmem) [ 0.000000] Virtual kernel memory layout: [ 0.000000] vector : 0xffff0000 - 0xffff1000 ( 4 kB) [ 0.000000] fixmap : 0xfff00000 - 0xfffe0000 ( 896 kB) [ 0.000000] vmalloc : 0x90800000 - 0xff000000 (1768 MB) [ 0.000000] lowmem : 0x80000000 - 0x90000000 ( 256 MB) [ 0.000000] pkmap : 0x7fe00000 - 0x80000000 ( 2 MB) [ 0.000000] modules : 0x7f800000 - 0x7fe00000 ( 6 MB) [ 0.000000] .text : 0x80008000 - 0x8026cb28 (2451 kB) [ 0.000000] .init : 0x8026d000 - 0x80287c80 ( 108 kB) [ 0.000000] .data : 0x80288000 - 0x802a4ca0 ( 116 kB) [ 0.000000] .bss : 0x802a4ca0 - 0x802dff74 ( 237 kB) [ 0.000000] Preemptible hierarchical RCU implementation. [ 0.000000] Dump stacks of tasks blocking RCU-preempt GP. [ 0.000000] RCU restricting CPUs from NR_CPUS=4 to nr_cpu_ids=2. [ 0.000000] NR_IRQS:16 nr_irqs:16 16 [ 0.000000] sched_clock: 32 bits at 41MHz, resolution 24ns, wraps every 103727ms [ 0.000000] Console: colour dummy device 80x30 [ 0.000000] console [tty0] enabled [ 0.000734] Calibrating delay loop... 1318.91 BogoMIPS (lpj=6594560) ...
will look like this
[ 0.000418] Booting Linux on physical CPU 0x0 [ 0.000439] Linux version 3.11.0-linaro_fjslt-tracking-bsp2.0.0-rc1+ (agreen@build.warmcat.com) (gcc version 4.7.3 20130205 (prerelease) (crosstool-NG linaro-1.13.1-4.7-2013.02-01-20130221 - Linaro GCC 2013.02) ) #194 SMP PREEMPT Thu Aug 8 09:16:22 CST 2013 [ 0.000468] CPU: ARMv7 Processor [412fc092] revision 2 (ARMv7), cr=50c5387d [ 0.000481] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache [ 0.000504] Machine: Fujitsu Semiconductor MB8AC0300-EVB, model: Fujitsu MB8AC0300 Evaluation Board [ 0.000664] early_init_dt_add_memory_arch: 40000000 10000000 [ 0.000824] cma: CMA: reserved 64 MiB at 4c000000 [ 0.000838] Memory policy: ECC disabled, Data cache writealloc [ 0.010319] On node 0 totalpages: 65536 [ 0.010339] Normal zone: 512 pages used for memmap [ 0.010349] Normal zone: 0 pages reserved [ 0.010361] Normal zone: 65536 pages, LIFO batch:15 [ 0.032012] PERCPU: Embedded 7 pages/cpu @804f4000 s7296 r8192 d13184 u32768 [ 0.032042] pcpu-alloc: s7296 r8192 d13184 u32768 alloc=8*4096 [ 0.032053] pcpu-alloc: [0] 0 [0] 1 [ 0.032091] Built 1 zonelists in Zone order, mobility grouping on. Total pages: 65024 [ 0.032106] Kernel command line: loglevel=4 mb8ac0300_ts.adjust_y=-40 console=tty0 console=ttyS0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rw rootwait [ 0.032292] PID hash table entries: 1024 (order: 0, 4096 bytes) [ 0.032444] Dentry cache hash table entries: 32768 (order: 5, 131072 bytes) [ 0.032708] Inode-cache hash table entries: 16384 (order: 4, 65536 bytes) [ 0.040695] Memory: 191100K/262144K available (1734K kernel code, 115K rwdata, 716K rodata, 107K init, 236K bss, 71044K reserved, 0K highmem) [ 0.040750] Virtual kernel memory layout: [ 0.040750] vector : 0xffff0000 - 0xffff1000 ( 4 kB) [ 0.040750] fixmap : 0xfff00000 - 0xfffe0000 ( 896 kB) [ 0.040750] vmalloc : 0x90800000 - 0xff000000 (1768 MB) [ 0.040750] lowmem : 0x80000000 - 0x90000000 ( 256 MB) [ 0.040750] pkmap : 0x7fe00000 - 0x80000000 ( 2 MB) [ 0.040750] modules : 0x7f800000 - 0x7fe00000 ( 6 MB) [ 0.040750] .text : 0x80008000 - 0x8026cb28 (2451 kB) [ 0.040750] .init : 0x8026d000 - 0x80287c80 ( 108 kB) [ 0.040750] .data : 0x80288000 - 0x802a4ca0 ( 116 kB) [ 0.040750] .bss : 0x802a4ca0 - 0x802dff74 ( 237 kB) [ 0.040997] Preemptible hierarchical RCU implementation. [ 0.041010] Dump stacks of tasks blocking RCU-preempt GP. [ 0.041022] RCU restricting CPUs from NR_CPUS=4 to nr_cpu_ids=2. [ 0.041059] NR_IRQS:16 nr_irqs:16 16 [ 0.048966] sched_clock: 32 bits at 41MHz, resolution 24ns, wraps every 103727ms [ 0.049495] Console: colour dummy device 80x30 [ 0.049688] console [tty0] enabled [ 0.050483] Calibrating delay loop... 1318.91 BogoMIPS (lpj=6594560) [ 0.138981] pid_max: default: 32768 minimum: 301 ...
when normal time starts, it is offset by the corresponding early time, so the logs are monotonic and consistent in time.
Like DEBUG_LL it's not compatible with ARCH_MULTIPLATFORM and not designed to be on by default, it's a debugging aid for studying where the real time is going during early kernel boot.
If you have a globaltimer on your SoC, to use it set CONFIG_EARLY_TIME_ARM_GLOBALTIMER=y and ..._PHYS to the base address of the SoC region that contains the globaltimer at +0x200.
..._VIRT needs to be set to somewhere in the static virtual mapping region where it won't step on anything: if you already have a static virtual mapping for this area you can re-use the static mapping virtual address here.
..._FREQ needs to be the clock rate of the globaltimer clock in Hz.
Normally you want to enable ..._ZERO, however it's configurable because you may also start the globaltimer in your bootloader, which will let you measure the total time from that to events in the kernel boot flow.
For example:
CONFIG_EARLY_TIME_ARM_GLOBALTIMER=y CONFIG_EARLY_TIME_ARM_GLOBALTIMER_ZERO=y CONFIG_EARLY_TIME_ARM_GLOBALTIMER_PHYS=0xf8100000 CONFIG_EARLY_TIME_ARM_GLOBALTIMER_VIRT=0xfd100000 CONFIG_EARLY_TIME_ARM_GLOBALTIMER_FREQ=165500000
Signed-off-by: Andy Green andy.green@linaro.org --- arch/arm/Kconfig | 4 +++ arch/arm/Kconfig.debug | 35 ++++++++++++++++++++++++++ arch/arm/kernel/head.S | 65 ++++++++++++++++++++++++++++++++++++++++++++++++ arch/arm/kernel/time.c | 41 ++++++++++++++++++++++++++++++ 4 files changed, 145 insertions(+)
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index f3f4bcf..a3b1d75 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -2311,6 +2311,10 @@ config ARM_CPU_SUSPEND
endmenu
+config HAS_ARCH_EARLY_TIME + bool + default n + source "net/Kconfig"
source "drivers/Kconfig" diff --git a/arch/arm/Kconfig.debug b/arch/arm/Kconfig.debug index de41088..e3a6570 100644 --- a/arch/arm/Kconfig.debug +++ b/arch/arm/Kconfig.debug @@ -900,4 +900,39 @@ config PID_IN_CONTEXTIDR additional instructions during context switch. Say Y here only if you are planning to use hardware trace tools with this kernel.
+config EARLY_TIME_ARM_GLOBALTIMER + bool "Enable early Arm globaltimer support" + default n + select HAS_ARCH_EARLY_TIME + help + If you have a spare globaltimer in your SoC, you can use it to + get accurate log timings starting from very early in boot and + before the very first log entry. + You'll need to speify some additional gnarly details if enabled. + +config EARLY_TIME_ARM_GLOBALTIMER_ZERO + bool "zeroing the globaltimer early in boot" + depends on EARLY_TIME_ARM_GLOBALTIMER + default y + help + If you started the globaltimer in your bootloader, you can disable + this to stop the kernel code zeroing the timer. Then your logs + will reflect time from the point it was zeroed in the bootloader. + But normally, you want to see time from very early in kernel boot. + +config EARLY_TIME_ARM_GLOBALTIMER_PHYS + hex "Physical address of your globaltimer" + depends on EARLY_TIME_ARM_GLOBALTIMER + +config EARLY_TIME_ARM_GLOBALTIMER_VIRT + hex "Virtual mapping address of your globaltimer" + depends on EARLY_TIME_ARM_GLOBALTIMER + help + This shoud match a static mapping so the timer can be used + seamlessly after the static mappings are set up + +config EARLY_TIME_ARM_GLOBALTIMER_FREQ + int "Frequency of your globaltimer in Hz" + depends on EARLY_TIME_ARM_GLOBALTIMER + endmenu diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S index 19aaa24..1725846 100644 --- a/arch/arm/kernel/head.S +++ b/arch/arm/kernel/head.S @@ -336,6 +336,71 @@ __create_page_tables: str r3, [r0] #endif #endif + +#ifdef CONFIG_EARLY_TIME_ARM_GLOBALTIMER + + /* allow cpu0 and cpu1 nonsec access to global timer */ + + ldr r3, =CONFIG_EARLY_TIME_ARM_GLOBALTIMER_PHYS /* no mmu yet*/ + orr r3, r3, #0x54 + ldr r7, [r3] + orr r7, r7, #0x300 /* cpu0 and 1 nonsec access */ + str r7, [r3] + +#ifdef CONFIG_EARLY_TIME_ARM_GLOBALTIMER_ZERO + + /* disable global timer */ + + ldr r3, =CONFIG_EARLY_TIME_ARM_GLOBALTIMER_PHYS + orr r3, r3, #0x208 + mov r7, #0 + str r7, [r3] + + /* zero global timer */ + + ldr r3, =CONFIG_EARLY_TIME_ARM_GLOBALTIMER_PHYS + orr r3, r3, #0x200 + mov r7, #0 + str r7, [r3] + add r3, r3, #4 + str r7, [r3] + + /* enable global timer */ + + add r3, r3, #4 + mov r7, #1 + str r7, [r3] + +#endif + + /* map it DEBUG_LL style */ + + ldr r7, =CONFIG_EARLY_TIME_ARM_GLOBALTIMER_PHYS + ldr r3, =CONFIG_EARLY_TIME_ARM_GLOBALTIMER_VIRT + + mov r3, r3, lsr #SECTION_SHIFT + mov r3, r3, lsl #PMD_ORDER + + add r0, r4, r3 + mov r3, r7, lsr #SECTION_SHIFT + ldr r7, [r10, #PROCINFO_IO_MMUFLAGS] @ io_mmuflags + orr r3, r7, r3, lsl #SECTION_SHIFT +#ifdef CONFIG_ARM_LPAE + mov r7, #1 << (54 - 32) @ XN +#ifdef CONFIG_CPU_ENDIAN_BE8 + str r7, [r0], #4 + str r3, [r0], #4 +#else + str r3, [r0], #4 + str r7, [r0], #4 +#endif +#else + orr r3, r3, #PMD_SECT_XN + str r3, [r0], #4 +#endif +#endif /* CONFIG_EARLY_TIME_ARM_GLOBALTIMER */ + + #ifdef CONFIG_ARM_LPAE sub r4, r4, #0x1000 @ point to the PGD table mov r4, r4, lsr #ARCH_PGD_SHIFT diff --git a/arch/arm/kernel/time.c b/arch/arm/kernel/time.c index 98aee32..0f84f8e 100644 --- a/arch/arm/kernel/time.c +++ b/arch/arm/kernel/time.c @@ -121,3 +121,44 @@ void __init time_init(void) else clocksource_of_init(); } + +#ifdef CONFIG_EARLY_TIME_ARM_GLOBALTIMER + +u64 arch_early_time(u64 normal) +{ + u32 __iomem *p = (u32 __iomem *) + (CONFIG_EARLY_TIME_ARM_GLOBALTIMER_VIRT + 0x200); + u64 ts; + static u64 handover; + + /* if normal time had started already, just return the offset */ + + if (handover) + return handover + normal; + + ts = __raw_readl(p); + ts |= ((u64)__raw_readl(p + 1)) << 32; + + /* accumulate ps */ + + ts = (ts * (int)(1024000000000.0 * + (1.0 / (double)CONFIG_EARLY_TIME_ARM_GLOBALTIMER_FREQ))) >> 10; + + /* + * has some normal time appeared? Let's stop our early time and + * keep it for use as an offset, so we get monotonic logs + * Subtract the normal time to make the sample above a valid + * offset for 0 normal time. + */ + + if (!handover && normal) { + ts -= normal; + handover = ts; + } + + return ts + normal; +} +EXPORT_SYMBOL_GPL(arch_early_time); + +#endif +