xref: /openbmc/linux/init/calibrate.c (revision a1e58bbd)
1 /* calibrate.c: default delay calibration
2  *
3  * Excised from init/main.c
4  *  Copyright (C) 1991, 1992  Linus Torvalds
5  */
6 
7 #include <linux/jiffies.h>
8 #include <linux/delay.h>
9 #include <linux/init.h>
10 #include <linux/timex.h>
11 
12 unsigned long preset_lpj;
13 static int __init lpj_setup(char *str)
14 {
15 	preset_lpj = simple_strtoul(str,NULL,0);
16 	return 1;
17 }
18 
19 __setup("lpj=", lpj_setup);
20 
21 #ifdef ARCH_HAS_READ_CURRENT_TIMER
22 
23 /* This routine uses the read_current_timer() routine and gets the
24  * loops per jiffy directly, instead of guessing it using delay().
25  * Also, this code tries to handle non-maskable asynchronous events
26  * (like SMIs)
27  */
28 #define DELAY_CALIBRATION_TICKS			((HZ < 100) ? 1 : (HZ/100))
29 #define MAX_DIRECT_CALIBRATION_RETRIES		5
30 
31 static unsigned long __cpuinit calibrate_delay_direct(void)
32 {
33 	unsigned long pre_start, start, post_start;
34 	unsigned long pre_end, end, post_end;
35 	unsigned long start_jiffies;
36 	unsigned long tsc_rate_min, tsc_rate_max;
37 	unsigned long good_tsc_sum = 0;
38 	unsigned long good_tsc_count = 0;
39 	int i;
40 
41 	if (read_current_timer(&pre_start) < 0 )
42 		return 0;
43 
44 	/*
45 	 * A simple loop like
46 	 *	while ( jiffies < start_jiffies+1)
47 	 *		start = read_current_timer();
48 	 * will not do. As we don't really know whether jiffy switch
49 	 * happened first or timer_value was read first. And some asynchronous
50 	 * event can happen between these two events introducing errors in lpj.
51 	 *
52 	 * So, we do
53 	 * 1. pre_start <- When we are sure that jiffy switch hasn't happened
54 	 * 2. check jiffy switch
55 	 * 3. start <- timer value before or after jiffy switch
56 	 * 4. post_start <- When we are sure that jiffy switch has happened
57 	 *
58 	 * Note, we don't know anything about order of 2 and 3.
59 	 * Now, by looking at post_start and pre_start difference, we can
60 	 * check whether any asynchronous event happened or not
61 	 */
62 
63 	for (i = 0; i < MAX_DIRECT_CALIBRATION_RETRIES; i++) {
64 		pre_start = 0;
65 		read_current_timer(&start);
66 		start_jiffies = jiffies;
67 		while (jiffies <= (start_jiffies + 1)) {
68 			pre_start = start;
69 			read_current_timer(&start);
70 		}
71 		read_current_timer(&post_start);
72 
73 		pre_end = 0;
74 		end = post_start;
75 		while (jiffies <=
76 		       (start_jiffies + 1 + DELAY_CALIBRATION_TICKS)) {
77 			pre_end = end;
78 			read_current_timer(&end);
79 		}
80 		read_current_timer(&post_end);
81 
82 		tsc_rate_max = (post_end - pre_start) / DELAY_CALIBRATION_TICKS;
83 		tsc_rate_min = (pre_end - post_start) / DELAY_CALIBRATION_TICKS;
84 
85 		/*
86 	 	 * If the upper limit and lower limit of the tsc_rate is
87 		 * >= 12.5% apart, redo calibration.
88 		 */
89 		if (pre_start != 0 && pre_end != 0 &&
90 		    (tsc_rate_max - tsc_rate_min) < (tsc_rate_max >> 3)) {
91 			good_tsc_count++;
92 			good_tsc_sum += tsc_rate_max;
93 		}
94 	}
95 
96 	if (good_tsc_count)
97 		return (good_tsc_sum/good_tsc_count);
98 
99 	printk(KERN_WARNING "calibrate_delay_direct() failed to get a good "
100 	       "estimate for loops_per_jiffy.\nProbably due to long platform interrupts. Consider using \"lpj=\" boot option.\n");
101 	return 0;
102 }
103 #else
104 static unsigned long __cpuinit calibrate_delay_direct(void) {return 0;}
105 #endif
106 
107 /*
108  * This is the number of bits of precision for the loops_per_jiffy.  Each
109  * bit takes on average 1.5/HZ seconds.  This (like the original) is a little
110  * better than 1%
111  */
112 #define LPS_PREC 8
113 
114 void __cpuinit calibrate_delay(void)
115 {
116 	unsigned long ticks, loopbit;
117 	int lps_precision = LPS_PREC;
118 
119 	if (preset_lpj) {
120 		loops_per_jiffy = preset_lpj;
121 		printk("Calibrating delay loop (skipped)... "
122 			"%lu.%02lu BogoMIPS preset\n",
123 			loops_per_jiffy/(500000/HZ),
124 			(loops_per_jiffy/(5000/HZ)) % 100);
125 	} else if ((loops_per_jiffy = calibrate_delay_direct()) != 0) {
126 		printk("Calibrating delay using timer specific routine.. ");
127 		printk("%lu.%02lu BogoMIPS (lpj=%lu)\n",
128 			loops_per_jiffy/(500000/HZ),
129 			(loops_per_jiffy/(5000/HZ)) % 100,
130 			loops_per_jiffy);
131 	} else {
132 		loops_per_jiffy = (1<<12);
133 
134 		printk(KERN_DEBUG "Calibrating delay loop... ");
135 		while ((loops_per_jiffy <<= 1) != 0) {
136 			/* wait for "start of" clock tick */
137 			ticks = jiffies;
138 			while (ticks == jiffies)
139 				/* nothing */;
140 			/* Go .. */
141 			ticks = jiffies;
142 			__delay(loops_per_jiffy);
143 			ticks = jiffies - ticks;
144 			if (ticks)
145 				break;
146 		}
147 
148 		/*
149 		 * Do a binary approximation to get loops_per_jiffy set to
150 		 * equal one clock (up to lps_precision bits)
151 		 */
152 		loops_per_jiffy >>= 1;
153 		loopbit = loops_per_jiffy;
154 		while (lps_precision-- && (loopbit >>= 1)) {
155 			loops_per_jiffy |= loopbit;
156 			ticks = jiffies;
157 			while (ticks == jiffies)
158 				/* nothing */;
159 			ticks = jiffies;
160 			__delay(loops_per_jiffy);
161 			if (jiffies != ticks)	/* longer than 1 tick */
162 				loops_per_jiffy &= ~loopbit;
163 		}
164 
165 		/* Round the value and print it */
166 		printk("%lu.%02lu BogoMIPS (lpj=%lu)\n",
167 			loops_per_jiffy/(500000/HZ),
168 			(loops_per_jiffy/(5000/HZ)) % 100,
169 			loops_per_jiffy);
170 	}
171 
172 }
173