aboutsummaryrefslogtreecommitdiff
path: root/arch/arm/plat-omap/include/plat/trace-clock.h
blob: 7fcdbf98063c947525cf8a08f550458b2e04ffa8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/*
 * 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 <linux/timer.h>
#include <linux/percpu.h>
#include <plat/clock.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@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

/* Resync with 32k clock each 100ms */
#define TC_RESYNC_PERIOD		100

struct tc_cur_freq {
	u64 cur_cpu_freq;	/* in khz */
	/* cur time : (now - base) * (max_freq / cur_freq) + base */
	u32 mul_fact;		/* (max_cpu_freq << 10) / cur_freq */
	u64 hw_base;		/* stamp of last cpufreq change, hw cycles */
	u64 virt_base;		/* same as above, virtual trace clock cycles */
	u64 floor;		/* floor value, so time never go back */
};

/* 32KHz counter per-cpu count save upon PM sleep and cpufreq management */
struct pm_save_count {
	struct tc_cur_freq cf[2];	/* rcu-protected */
	unsigned int index;		/* tc_cur_freq current read index */
	/*
	 * Is fast clock ready to be read ?  Read with preemption off. Modified
	 * only by local CPU in thread and interrupt context or by start/stop
	 * when time is not read concurrently.
	 */
	int fast_clock_ready;

	u64 int_fast_clock;
	struct timer_list clear_ccnt_ms_timer;
	struct timer_list clock_resync_timer;
	u32 ext_32k;
	int refcount;
	u32 init_clock;
	raw_spinlock_t lock;		/* spinlock only sync the refcount */
	unsigned int dvfs_count;	/* Number of DVFS updates in period */
	/* cpufreq management */
	u64 max_cpu_freq;		/* in khz */
};

DECLARE_PER_CPU(struct pm_save_count, pm_save_count);

extern u64 trace_clock_read_synthetic_tsc(void);
extern void _trace_clock_write_synthetic_tsc(u64 value);
extern unsigned long long cpu_hz;

DECLARE_PER_CPU(int, fast_clock_ready);
extern u64 _trace_clock_read_slow(void);

/*
 * 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);

#ifdef CONFIG_DEBUG_TRACE_CLOCK
DECLARE_PER_CPU(unsigned int, last_clock_nest);
extern void trace_clock_debug(u64 value);
#else
static inline void trace_clock_debug(u64 value)
{
}
#endif

static inline u32 read_ccnt(void)
{
	u32 val;
        __asm__ __volatile__ ("mrc p15, 0, %0, c9, c13, 0" : "=r" (val));
	return val & ~(1 << TC_HW_BITS);
}

static inline u32 trace_clock_read32(void)
{
	u32 val;

	isb();
	val = read_ccnt();
	isb();
	return val;
}

static inline u64 trace_clock_read64(void)
{
	struct pm_save_count *pm_count;
	struct tc_cur_freq *cf;
	u64 val;
#ifdef CONFIG_DEBUG_TRACE_CLOCK
	unsigned long flags;

	local_irq_save(flags);
	per_cpu(last_clock_nest, smp_processor_id())++;
	barrier();
#endif

	preempt_disable();
	pm_count = &per_cpu(pm_save_count, smp_processor_id());
	if (likely(pm_count->fast_clock_ready)) {
		cf = &pm_count->cf[ACCESS_ONCE(pm_count->index)];
		val = max((((trace_clock_read_synthetic_tsc() - cf->hw_base)
		      * cf->mul_fact) >> 10) + cf->virt_base, cf->floor);
	} else
		val = _trace_clock_read_slow();
	trace_clock_debug(val);
	preempt_enable();

#ifdef CONFIG_DEBUG_TRACE_CLOCK
	barrier();
	per_cpu(last_clock_nest, smp_processor_id())--;
	local_irq_restore(flags);
#endif
	return val;
}

static inline u64 trace_clock_frequency(void)
{
	return cpu_hz;
}

static inline u32 trace_clock_freq_scale(void)
{
	return 1;
}

extern int get_trace_clock(void);
extern void put_trace_clock(void);
extern void get_synthetic_tsc(void);
extern void put_synthetic_tsc(void);

extern void resync_trace_clock(void);
extern void save_sync_trace_clock(void);
extern void start_trace_clock(void);
extern void stop_trace_clock(void);

static inline void set_trace_clock_is_sync(int state)
{
}
#endif /* _ASM_MIPS_TRACE_CLOCK_OMAP3_H */