[ltt-dev] [patch 7/7] omap trace clock

Tony Lindgren tony at atomide.com
Wed Feb 18 11:58:41 EST 2009


* Mathieu Desnoyers <mathieu.desnoyers at polymtl.ca> [090218 00:08]:
> Implement LTTng trace clock for omap. Should eventually make it so it can be
> compiled-out, but that would imply fixing other architecture's trace clocks
> too.
> 
> It only supports uniprocessor for now. IPIs would be needed to restore each
> CPU's ccnt register in sync with the 32k clock upon resync_trace_clock to
> support SMP.
> 
> Eventually, looking at how much time is lost lost when clearing the top
> ccnt bit should be done, so we can compensate for the cycles lost.

Sounds like the omap specific parts should live in the LTT tree until the
core parts are integrated into the mainline kernel. Anyways, let me know if
there are some omap related pieces that should get integrated earlier.

Regards,

Tony


> Signed-off-by: Mathieu Desnoyers <mathieu.desnoyers at polymtl.ca>
> ---
>  arch/arm/include/asm/trace-clock.h            |    1 
>  arch/arm/mach-omap2/Makefile                  |    2 
>  arch/arm/mach-omap2/clock34xx.c               |    5 
>  arch/arm/mach-omap2/trace-clock.c             |  209 ++++++++++++++++++++++++++
>  arch/arm/plat-omap/Kconfig                    |    3 
>  arch/arm/plat-omap/include/mach/trace-clock.h |   95 +++++++++++
>  6 files changed, 314 insertions(+), 1 deletion(-)
> 
> Index: linux-omap-2.6/arch/arm/mach-omap2/trace-clock.c
> ===================================================================
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ linux-omap-2.6/arch/arm/mach-omap2/trace-clock.c	2009-02-18 02:28:42.000000000 -0500
> @@ -0,0 +1,209 @@
> +/*
> + * arch/arm/mach-omap2/trace-clock.c
> + *
> + * Trace clock for ARM OMAP3
> + * Currently uniprocessor-only.
> + *
> + * Mathieu Desnoyers <mathieu.desnoyers at polymtl.ca>, February 2009
> + */
> +
> +#include <linux/module.h>
> +#include <linux/clocksource.h>
> +#include <linux/timer.h>
> +#include <mach/dmtimer.h>
> +#include <mach/trace-clock.h>
> +
> +/* Need direct access to the clock from kernel/time/timekeeping.c */
> +extern struct clocksource *clock;
> +
> +static void clear_ccnt_ms(unsigned long data);
> +
> +static DEFINE_TIMER(clear_ccnt_ms_timer, clear_ccnt_ms, 0, 0);
> +
> +/* According to timer32k.c, this is a 32768Hz clock, not a 32000Hz clock. */
> +#define TIMER_32K_FREQ	32768
> +#define TIMER_32K_SHIFT	15
> +
> +/*
> + * Clear ccnt twice per 31-bit overflow, or 4 times per 32-bits period.
> + */
> +#define CLEAR_CCNT_INTERVAL	(cpu_hz / 4)
> +
> +static DEFINE_SPINLOCK(trace_clock_lock);
> +static int trace_clock_refcount;
> +
> +/*
> + * Cycle counter management.
> + */
> +
> +static inline void write_pmnc(u32 val)
> +{
> +	__asm__ __volatile__ ("mcr p15, 0, %0, c9, c12, 0" : : "r" (val));
> +}
> +
> +static inline u32 read_pmnc(void)
> +{
> +	u32 val;
> +	__asm__ __volatile__ ("mrc p15, 0, %0, c9, c12, 0" : "=r" (val));
> +        return val;
> +}
> +
> +static inline void write_ctens(u32 val)
> +{
> +	__asm__ __volatile__ ("mcr p15, 0, %0, c9, c12, 1" : : "r" (val));
> +}
> +
> +static inline u32 read_ctens(void)
> +{
> +	u32 val;
> +	__asm__ __volatile__ ("mrc p15, 0, %0, c9, c12, 1" : "=r" (val));
> +	return val;
> +}
> +
> +static inline void write_intenc(u32 val)
> +{
> +	__asm__ __volatile__ ("mcr p15, 0, %0, c9, c14, 2" : : "r" (val));
> +}
> +
> +static inline u32 read_intenc(void)
> +{
> +	u32 val;
> +        __asm__ __volatile__ ("mrc p15, 0, %0, c9, c14, 2" : "=r" (val));
> +	return val;
> +}
> +
> +static inline void write_useren(u32 val)
> +{
> +	__asm__ __volatile__ ("mcr p15, 0, %0, c9, c14, 0" : : "r" (val));
> +}
> +
> +static inline u32 read_useren(void)
> +{
> +	u32 val;
> +        __asm__ __volatile__ ("mrc p15, 0, %0, c9, c14, 0" : "=r" (val));
> +	return val;
> +}
> +
> +/*
> + * Must disable counter before writing to it.
> + */
> +static inline void write_ccnt(u32 val)
> +{
> +	__asm__ __volatile__ ("mcr p15, 0, %0, c9, c13, 0" : : "r" (val));
> +}
> +
> +/*
> + * Periodical timer handler, clears ccnt most significant bit each half-period
> + * of 31-bit overflow. Makes sure the ccnt never overflows.
> + */
> +static void clear_ccnt_ms(unsigned long data)
> +{
> +	unsigned int cycles;
> +	unsigned long flags;
> +
> +	local_irq_save(flags);
> +	isb();	/* clear the pipeline so we can execute ASAP */
> +	write_ctens(read_ctens() & ~(1 << 31));	/* disable counter */
> +	cycles = read_ccnt();
> +	write_ccnt(cycles & ~(1 << 31));
> +	write_ctens(read_ctens() |  (1 << 31));	/* enable counter */
> +	local_irq_restore(flags);
> +
> +	mod_timer(&clear_ccnt_ms_timer, jiffies + CLEAR_CCNT_INTERVAL);
> +}
> +
> +void _start_trace_clock(void)
> +{
> +	unsigned long flags;
> +	unsigned int count_32k, count_trace_clock;
> +	u32 regval;
> +	u64 ref_time;
> +
> +	/* Let userspace access performance counter registers */
> +	regval = read_useren();
> +	regval |=  (1 << 0);	/* User mode enable */
> +	write_useren(regval);
> +
> +	regval = read_intenc();
> +	regval |=  (1 << 31);	/* CCNT overflow interrupt disable */
> +	write_intenc(regval);
> +
> +	regval = read_pmnc();
> +	regval |=  (1 << 0);	/* Enable all counters */
> +	regval &= ~(1 << 3);	/* count every cycles */
> +	regval &= ~(1 << 5);	/* Enable even in non-invasive debug prohib. */
> +	write_pmnc(regval);
> +
> +	mod_timer(&clear_ccnt_ms_timer, jiffies + CLEAR_CCNT_INTERVAL);
> +
> +	/*
> +	 * Set the timer's value MSBs to the same as current 32K timer.
> +	 */
> +	local_irq_save(flags);
> +	count_32k = clocksource_read(clock);
> +	ref_time = (u64)count_32k * (cpu_hz >> TIMER_32K_SHIFT);
> +	write_ctens(read_ctens() & ~(1 << 31));	/* disable counter */
> +	write_ccnt((u32)ref_time);
> +	write_ctens(read_ctens() |  (1 << 31));	/* enable counter */
> +	count_trace_clock = trace_clock_read32();
> +	local_irq_restore(flags);
> +
> +	get_synthetic_tsc();
> +
> +	printk(KERN_INFO "Trace clock using cycle counter at %llu HZ\n"
> +	       "32k clk value 0x%08X, cycle counter value 0x%08X\n"
> +	       "synthetic value (write, read) 0x%016llX, 0x%016llX\n",
> +	       cpu_hz, count_32k,
> +	       count_trace_clock, ref_time, trace_clock_read64());
> +	printk(KERN_INFO "Reference clock used : %s\n", clock->name);
> +}
> +
> +void _stop_trace_clock(void)
> +{
> +	del_timer_sync(&clear_ccnt_ms_timer);
> +	put_synthetic_tsc();
> +}
> +
> +void start_trace_clock(void)
> +{
> +	spin_lock(&trace_clock_lock);
> +	if (!trace_clock_refcount)
> +		goto end;
> +	_start_trace_clock();
> +end:
> +	spin_unlock(&trace_clock_lock);
> +}
> +
> +void stop_trace_clock(void)
> +{
> +	spin_lock(&trace_clock_lock);
> +	if (!trace_clock_refcount)
> +		goto end;
> +	_stop_trace_clock();
> +end:
> +	spin_unlock(&trace_clock_lock);
> +}
> +
> +void get_trace_clock(void)
> +{
> +	spin_lock(&trace_clock_lock);
> +	if (trace_clock_refcount++)
> +		goto end;
> +	_start_trace_clock();
> +end:
> +	spin_unlock(&trace_clock_lock);
> +}
> +EXPORT_SYMBOL_GPL(get_trace_clock);
> +
> +void put_trace_clock(void)
> +{
> +	spin_lock(&trace_clock_lock);
> +	WARN_ON(trace_clock_refcount <= 0);
> +	if (trace_clock_refcount != 1)
> +		goto end;
> +	_stop_trace_clock();
> +end:
> +	trace_clock_refcount--;
> +	spin_unlock(&trace_clock_lock);
> +}
> +EXPORT_SYMBOL_GPL(put_trace_clock);
> Index: linux-omap-2.6/arch/arm/plat-omap/include/mach/trace-clock.h
> ===================================================================
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ linux-omap-2.6/arch/arm/plat-omap/include/mach/trace-clock.h	2009-02-18 02:28:42.000000000 -0500
> @@ -0,0 +1,95 @@
> +/*
> + * Copyright (C) 2009 Mathieu Desnoyers
> + *
> + * Trace clock ARM OMAP3 definitions.
> + */
> +
> +#ifndef _ASM_ARM_TRACE_CLOCK_OMAP3_H
> +#define _ASM_ARM_TRACE_CLOCK_OMAP3_H
> +
> +#include <linux/clk.h>
> +#include <asm/system.h>
> +#include <mach/dmtimer.h>
> +
> +/*
> + * Number of hardware clock bits. The higher order bits are expected to be 0.
> + * If the hardware clock source has more than 32 bits, the bits higher than the
> + * 32nd will be truncated by a cast to a 32 bits unsigned. Range : 1 - 32.
> + * (too few bits would be unrealistic though, since we depend on the timer to
> + * detect the overflows).
> + * OMAP3-specific : we clear bit 31 periodically so it never overflows. There is
> + * a hardware bug with CP14 and CP15 being executed at the same time a ccnt overflow
> + * occurs.
> + *
> + * Siarhei Siamashka <siarhei.siamashka at nokia.com> :
> + * Performance monitoring unit breaks if somebody is accessing CP14/CP15
> + * coprocessor register exactly at the same time as CCNT overflows (regardless
> + * of the fact if generation of interrupts is enabled or not). A workaround
> + * suggested by ARM was to never allow it to overflow and reset it
> + * periodically.
> + */
> +#define TC_HW_BITS			31
> +
> +/* Expected maximum interrupt latency in ms : 15ms, *2 for security */
> +#define TC_EXPECTED_INTERRUPT_LATENCY	30
> +
> +extern u64 trace_clock_read_synthetic_tsc(void);
> +extern struct omap_dm_timer *trace_clock_timer;
> +extern unsigned long long cpu_hz;
> +
> +/*
> + * ARM OMAP3 timers only return 32-bits values. We ened to extend it to a
> + * 64-bit value, which is provided by trace-clock-32-to-64.
> + */
> +extern u64 trace_clock_async_tsc_read(void);
> +/*
> + * Update done by the architecture upon wakeup.
> + */
> +extern void _trace_clock_write_synthetic_tsc(u64 value);
> +
> +static inline u32 read_ccnt(void)
> +{
> +	u32 val;
> +        __asm__ __volatile__ ("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));
> +	return val;
> +}
> +
> +static inline u32 trace_clock_read32(void)
> +{
> +	u32 val;
> +
> +	isb();
> +	val = read_ccnt();
> +	isb();
> +	return val;
> +}
> +
> +static inline u64 trace_clock_read64(void)
> +{
> +	return trace_clock_read_synthetic_tsc();
> +}
> +
> +static inline u64 trace_clock_frequency(void)
> +{
> +	return cpu_hz;
> +}
> +
> +static inline u32 trace_clock_freq_scale(void)
> +{
> +	return 1;
> +}
> +
> +extern void get_trace_clock(void);
> +extern void put_trace_clock(void);
> +extern void get_synthetic_tsc(void);
> +extern void put_synthetic_tsc(void);
> +
> +/* Used by the architecture upon wakeup from PM idle */
> +extern void start_trace_clock(void);
> +/* Used by the architecture when going to PM idle */
> +extern void stop_trace_clock(void);
> +
> +static inline void set_trace_clock_is_sync(int state)
> +{
> +}
> +#endif /* _ASM_MIPS_TRACE_CLOCK_OMAP3_H */
> Index: linux-omap-2.6/arch/arm/plat-omap/Kconfig
> ===================================================================
> --- linux-omap-2.6.orig/arch/arm/plat-omap/Kconfig	2009-02-18 02:26:15.000000000 -0500
> +++ linux-omap-2.6/arch/arm/plat-omap/Kconfig	2009-02-18 02:26:34.000000000 -0500
> @@ -19,6 +19,9 @@ config ARCH_OMAP2
>  config ARCH_OMAP3
>  	bool "TI OMAP3"
>  	select CPU_V7
> +	select HAVE_TRACE_CLOCK
> +	select HAVE_TRACE_CLOCK_32_TO_64
> +	select OMAP_32K_TIMER
>  
>  endchoice
>  
> Index: linux-omap-2.6/arch/arm/mach-omap2/Makefile
> ===================================================================
> --- linux-omap-2.6.orig/arch/arm/mach-omap2/Makefile	2009-02-18 02:26:15.000000000 -0500
> +++ linux-omap-2.6/arch/arm/mach-omap2/Makefile	2009-02-18 02:26:34.000000000 -0500
> @@ -89,4 +89,4 @@ ifneq ($(CONFIG_USB_EHCI_HCD),)
>  	obj-y				+= usb-ehci.o
>  endif
>  
> -
> +obj-$(CONFIG_HAVE_TRACE_CLOCK)		+= trace-clock.o
> Index: linux-omap-2.6/arch/arm/include/asm/trace-clock.h
> ===================================================================
> --- /dev/null	1970-01-01 00:00:00.000000000 +0000
> +++ linux-omap-2.6/arch/arm/include/asm/trace-clock.h	2009-02-18 02:26:34.000000000 -0500
> @@ -0,0 +1 @@
> +#include <mach/trace-clock.h>
> Index: linux-omap-2.6/arch/arm/mach-omap2/clock34xx.c
> ===================================================================
> --- linux-omap-2.6.orig/arch/arm/mach-omap2/clock34xx.c	2009-02-18 02:26:15.000000000 -0500
> +++ linux-omap-2.6/arch/arm/mach-omap2/clock34xx.c	2009-02-18 02:26:34.000000000 -0500
> @@ -45,6 +45,9 @@
>  
>  #define MAX_DPLL_WAIT_TRIES		1000000
>  
> +unsigned long long cpu_hz;
> +EXPORT_SYMBOL(cpu_hz);
> +
>  /**
>   * omap3_dpll_recalc - recalculate DPLL rate
>   * @clk: DPLL struct clk
> @@ -754,6 +757,8 @@ int __init omap2_clk_init(void)
>  	       (osc_sys_ck.rate / 1000000), (osc_sys_ck.rate / 100000) % 10,
>  	       (core_ck.rate / 1000000), (arm_fck.rate / 1000000));
>  
> +	cpu_hz = arm_fck.rate;
> +
>  	/*
>  	 * Only enable those clocks we will need, let the drivers
>  	 * enable other clocks as necessary
> 
> -- 
> Mathieu Desnoyers
> OpenPGP key fingerprint: 8CD5 52C3 8E3C 4140 715F  BA06 3F25 A8FE 3BAE 9A68




More information about the lttng-dev mailing list