summaryrefslogtreecommitdiff
path: root/arch/arm/mach-ux500/pm/timer.c
blob: 61f92bf73daff2caca30cb0feaa26d9c20eb8455 (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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
/*
 * Copyright (C) ST-Ericsson SA 2010-2011
 *
 * Author: Jonas Aaberg <jonas.aberg@stericsson.com> for ST-Ericsson
 *
 * License Terms: GNU General Public License v2
 *
 * The RTC timer block is a ST Microelectronics variant of ARM PL031.
 * Clockwatch part is the same as PL031, while the timer part is only
 * present on the ST Microelectronics variant.
 * Here only the timer part is used.
 *
 * The timer part is quite troublesome to program correctly. Lots
 * of long delays must be there in order to secure that you actually get what
 * you wrote.
 *
 * In other words, this timer is and should only used from cpuidle during
 * special conditions when the surroundings are know in order to be able
 * to remove the number of delays.
 */
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/ktime.h>
#include <linux/delay.h>

#include <asm/errno.h>

#include <mach/hardware.h>

#define RTC_IMSC	0x10
#define RTC_MIS		0x18
#define RTC_ICR		0x1C
#define RTC_TDR		0x20
#define	RTC_TLR1	0x24
#define RTC_TCR		0x28

#define RTC_TLR2	0x2C
#define RTC_TPR1	0x3C

#define RTC_TCR_RTTOS	(1 << 0)
#define RTC_TCR_RTTEN	(1 << 1)
#define RTC_TCR_RTTSS	(1 << 2)

#define RTC_IMSC_TIMSC	(1 << 1)
#define RTC_ICR_TIC	(1 << 1)
#define RTC_MIS_RTCTMIS	(1 << 1)

#define RTC_TCR_RTTPS_2	(1 << 4)
#define RTC_TCR_RTTPS_3	(2 << 4)
#define RTC_TCR_RTTPS_4	(3 << 4)
#define RTC_TCR_RTTPS_5	(4 << 4)
#define RTC_TCR_RTTPS_6	(5 << 4)
#define RTC_TCR_RTTPS_7	(6 << 4)
#define RTC_TCR_RTTPS_8	(7 << 4)

#define WRITE_DELAY 130 /* 4 cycles plus margin */

/*
 * Count down measure point. It just have to be high to differ
 * from scheduled values.
 */
#define MEASURE_VAL 0xffffffff

/* Just a value bigger than any reason able scheduled timeout. */
#define MEASURE_VAL_LIMIT 0xf0000000

#define TICKS_TO_NS(x) ((s64)x * 30518)
#define US_TO_TICKS(x) ((u32)((1000 * x) / 30518))

static void __iomem *rtc_base;
static bool measure_latency;

#ifdef CONFIG_UX500_CPUIDLE_DEBUG

/*
 * The plan here is to be able to measure the ApSleep/ApDeepSleep exit latency
 * by having a know timer pattern.
 * The first entry in the pattern, LR1, is the value that the scheduler
 * wants us to sleep. The second pattern in a high value, too large to be
 * scheduled, so we can differ between a running scheduled value and a
 * time measure value.
 * When a RTT interrupt has occured, the block will automatically start
 * to execute the measure value in LR2 and when the ARM is awake, it reads
 * how far the RTT has decreased the value loaded from LR2 and from that
 * calculate how long time it took to wake up.
 */
ktime_t u8500_rtc_exit_latency_get(void)
{
	u32 ticks;

	if (measure_latency) {
		ticks = MEASURE_VAL - readl(rtc_base + RTC_TDR);

		/*
		 * Check if we are actually counting on a LR2 value.
		 * If not we have woken on another interrupt.
		 */
		if (ticks < MEASURE_VAL_LIMIT) {
			/* convert 32 kHz ticks to ns */
			return ktime_set(0, TICKS_TO_NS(ticks));
		}
	}
	return ktime_set(0, 0);
}

static void measure_latency_start(void)
{
	udelay(WRITE_DELAY);
	/*
	 * Disable RTT and clean self-start due to we want to restart,
	 * not continue from current pattern. (See below)
	 */
	writel(0, rtc_base + RTC_TCR);
	udelay(WRITE_DELAY);

	/*
	 * Program LR2 (load register two) to maximum value to ease
	 * identification of timer interrupt vs other.
	 */
	writel(MEASURE_VAL, rtc_base + RTC_TLR2);
	/*
	 * Set Load Register execution pattern, bit clear
	 * means pick LR1, bit set means LR2
	 * 0xfe, binary 11111110 means first do LR1 then do
	 * LR2 seven times
	 */
	writel(0xfe, rtc_base + RTC_TPR1);

	udelay(WRITE_DELAY);

	/*
	 * Enable self-start, plus a pattern of eight.
	 */
	writel(RTC_TCR_RTTSS | RTC_TCR_RTTPS_8,
	       rtc_base + RTC_TCR);
	udelay(WRITE_DELAY);
}

void ux500_rtcrtt_measure_latency(bool enable)
{
	if (enable) {
		measure_latency_start();
	} else {
		writel(RTC_TCR_RTTSS | RTC_TCR_RTTOS, rtc_base + RTC_TCR);
		writel(RTC_ICR_TIC, rtc_base + RTC_ICR);
		writel(RTC_IMSC_TIMSC, rtc_base + RTC_IMSC);
	}
	measure_latency = enable;
}
#else
static inline void measure_latency_start(void) { }
static inline void ux500_rtcrtt_measure_latency(bool enable)
{
	writel(RTC_TCR_RTTSS | RTC_TCR_RTTOS, rtc_base + RTC_TCR);
	writel(RTC_ICR_TIC, rtc_base + RTC_ICR);
	writel(RTC_IMSC_TIMSC, rtc_base + RTC_IMSC);
}
#endif

void ux500_rtcrtt_off(void)
{
	if (measure_latency) {
		measure_latency_start();
	} else {
		/* Clear eventual interrupts */
		if (readl(rtc_base + RTC_MIS) & RTC_MIS_RTCTMIS)
			writel(RTC_ICR_TIC, rtc_base + RTC_ICR);

		/* Disable, self start and oneshot mode */
		writel(RTC_TCR_RTTSS | RTC_TCR_RTTOS, rtc_base + RTC_TCR);
	}
}

void ux500_rtcrtt_next(u32 time_us)
{
	writel(US_TO_TICKS(time_us), rtc_base + RTC_TLR1);
}

static int __init ux500_rtcrtt_init(void)
{
	if (cpu_is_u8500()) {
		rtc_base  = __io_address(U8500_RTC_BASE);
	} else if (cpu_is_u5500()) {
		rtc_base  = __io_address(U5500_RTC_BASE);
	} else {
		pr_err("timer-rtt: Unknown DB Asic!\n");
		return -EINVAL;
	}
	ux500_rtcrtt_measure_latency(false);
	return 0;
}
subsys_initcall(ux500_rtcrtt_init);