xref: /minix3/minix/kernel/arch/i386/arch_clock.c (revision da9af514b56b48cf4ed3843769d88a6a6b54eb2d)
1433d6423SLionel Sambuc 
2433d6423SLionel Sambuc /* i386-specific clock functions. */
3433d6423SLionel Sambuc 
4433d6423SLionel Sambuc #include <machine/ports.h>
5433d6423SLionel Sambuc #include <minix/portio.h>
6433d6423SLionel Sambuc 
7433d6423SLionel Sambuc #include "kernel/kernel.h"
8433d6423SLionel Sambuc 
9433d6423SLionel Sambuc #include "kernel/clock.h"
10433d6423SLionel Sambuc #include "kernel/interrupt.h"
11433d6423SLionel Sambuc #include <minix/u64.h>
12433d6423SLionel Sambuc #include "glo.h"
13433d6423SLionel Sambuc #include "kernel/profile.h"
14433d6423SLionel Sambuc 
15433d6423SLionel Sambuc 
16433d6423SLionel Sambuc #ifdef USE_APIC
17433d6423SLionel Sambuc #include "apic.h"
18433d6423SLionel Sambuc #endif
19433d6423SLionel Sambuc 
20433d6423SLionel Sambuc #include "kernel/spinlock.h"
21433d6423SLionel Sambuc 
22433d6423SLionel Sambuc #ifdef CONFIG_SMP
23433d6423SLionel Sambuc #include "kernel/smp.h"
24433d6423SLionel Sambuc #endif
25433d6423SLionel Sambuc 
26433d6423SLionel Sambuc #define CLOCK_ACK_BIT   0x80    /* PS/2 clock interrupt acknowledge bit */
27433d6423SLionel Sambuc 
28433d6423SLionel Sambuc /* Clock parameters. */
29433d6423SLionel Sambuc #define COUNTER_FREQ (2*TIMER_FREQ) /* counter frequency using square wave */
30433d6423SLionel Sambuc #define LATCH_COUNT     0x00    /* cc00xxxx, c = channel, x = any */
31433d6423SLionel Sambuc #define SQUARE_WAVE     0x36    /* ccaammmb, a = access, m = mode, b = BCD */
32433d6423SLionel Sambuc                                 /*   11x11, 11 = LSB then MSB, x11 = sq wave */
33433d6423SLionel Sambuc #define TIMER_FREQ  1193182    /* clock frequency for timer in PC and AT */
34433d6423SLionel Sambuc #define TIMER_COUNT(freq) (TIMER_FREQ/(freq)) /* initial value for counter*/
35433d6423SLionel Sambuc 
36433d6423SLionel Sambuc static irq_hook_t pic_timer_hook;		/* interrupt handler hook */
37433d6423SLionel Sambuc 
38433d6423SLionel Sambuc static unsigned probe_ticks;
39433d6423SLionel Sambuc static u64_t tsc0, tsc1;
40433d6423SLionel Sambuc #define PROBE_TICKS	(system_hz / 10)
41433d6423SLionel Sambuc 
42433d6423SLionel Sambuc static unsigned tsc_per_ms[CONFIG_MAX_CPUS];
43433d6423SLionel Sambuc 
44433d6423SLionel Sambuc /*===========================================================================*
45433d6423SLionel Sambuc  *				init_8235A_timer			     *
46433d6423SLionel Sambuc  *===========================================================================*/
47433d6423SLionel Sambuc int init_8253A_timer(const unsigned freq)
48433d6423SLionel Sambuc {
49433d6423SLionel Sambuc 	/* Initialize channel 0 of the 8253A timer to, e.g., 60 Hz,
50433d6423SLionel Sambuc 	 * and register the CLOCK task's interrupt handler to be run
51433d6423SLionel Sambuc 	 * on every clock tick.
52433d6423SLionel Sambuc 	 */
53433d6423SLionel Sambuc 	outb(TIMER_MODE, SQUARE_WAVE);  /* run continuously */
54433d6423SLionel Sambuc 	outb(TIMER0, (TIMER_COUNT(freq) & 0xff)); /* timer low byte */
55433d6423SLionel Sambuc 	outb(TIMER0, TIMER_COUNT(freq) >> 8); /* timer high byte */
56433d6423SLionel Sambuc 
57433d6423SLionel Sambuc 	return OK;
58433d6423SLionel Sambuc }
59433d6423SLionel Sambuc 
60433d6423SLionel Sambuc /*===========================================================================*
61433d6423SLionel Sambuc  *				stop_8235A_timer			     *
62433d6423SLionel Sambuc  *===========================================================================*/
63433d6423SLionel Sambuc void stop_8253A_timer(void)
64433d6423SLionel Sambuc {
65433d6423SLionel Sambuc 	/* Reset the clock to the BIOS rate. (For rebooting.) */
66433d6423SLionel Sambuc 	outb(TIMER_MODE, 0x36);
67433d6423SLionel Sambuc 	outb(TIMER0, 0);
68433d6423SLionel Sambuc 	outb(TIMER0, 0);
69433d6423SLionel Sambuc }
70433d6423SLionel Sambuc 
71433d6423SLionel Sambuc void arch_timer_int_handler(void)
72433d6423SLionel Sambuc {
73433d6423SLionel Sambuc }
74433d6423SLionel Sambuc 
75433d6423SLionel Sambuc static int calib_cpu_handler(irq_hook_t * UNUSED(hook))
76433d6423SLionel Sambuc {
77433d6423SLionel Sambuc 	u64_t tsc;
78433d6423SLionel Sambuc 
79433d6423SLionel Sambuc 	probe_ticks++;
80433d6423SLionel Sambuc 	read_tsc_64(&tsc);
81433d6423SLionel Sambuc 
82433d6423SLionel Sambuc 
83433d6423SLionel Sambuc 	if (probe_ticks == 1) {
84433d6423SLionel Sambuc 		tsc0 = tsc;
85433d6423SLionel Sambuc 	}
86433d6423SLionel Sambuc 	else if (probe_ticks == PROBE_TICKS) {
87433d6423SLionel Sambuc 		tsc1 = tsc;
88433d6423SLionel Sambuc 	}
89433d6423SLionel Sambuc 
90433d6423SLionel Sambuc 	/* just in case we are in an SMP single cpu fallback mode */
91433d6423SLionel Sambuc 	BKL_UNLOCK();
92433d6423SLionel Sambuc 	return 1;
93433d6423SLionel Sambuc }
94433d6423SLionel Sambuc 
95433d6423SLionel Sambuc static void estimate_cpu_freq(void)
96433d6423SLionel Sambuc {
97433d6423SLionel Sambuc 	u64_t tsc_delta;
98433d6423SLionel Sambuc 	u64_t cpu_freq;
99433d6423SLionel Sambuc 
100433d6423SLionel Sambuc 	irq_hook_t calib_cpu;
101433d6423SLionel Sambuc 
102433d6423SLionel Sambuc 	/* set the probe, we use the legacy timer, IRQ 0 */
103433d6423SLionel Sambuc 	put_irq_handler(&calib_cpu, CLOCK_IRQ, calib_cpu_handler);
104433d6423SLionel Sambuc 
105433d6423SLionel Sambuc 	/* just in case we are in an SMP single cpu fallback mode */
106433d6423SLionel Sambuc 	BKL_UNLOCK();
107433d6423SLionel Sambuc 	/* set the PIC timer to get some time */
108433d6423SLionel Sambuc 	intr_enable();
109433d6423SLionel Sambuc 
110433d6423SLionel Sambuc 	/* loop for some time to get a sample */
111433d6423SLionel Sambuc 	while(probe_ticks < PROBE_TICKS) {
112433d6423SLionel Sambuc 		intr_enable();
113433d6423SLionel Sambuc 	}
114433d6423SLionel Sambuc 
115433d6423SLionel Sambuc 	intr_disable();
116433d6423SLionel Sambuc 	/* just in case we are in an SMP single cpu fallback mode */
117433d6423SLionel Sambuc 	BKL_LOCK();
118433d6423SLionel Sambuc 
119433d6423SLionel Sambuc 	/* remove the probe */
120433d6423SLionel Sambuc 	rm_irq_handler(&calib_cpu);
121433d6423SLionel Sambuc 
122433d6423SLionel Sambuc 	tsc_delta = tsc1 - tsc0;
123433d6423SLionel Sambuc 
124433d6423SLionel Sambuc 	cpu_freq = (tsc_delta / (PROBE_TICKS - 1)) * system_hz;
125433d6423SLionel Sambuc 	cpu_set_freq(cpuid, cpu_freq);
126433d6423SLionel Sambuc 	cpu_info[cpuid].freq = (unsigned long)(cpu_freq / 1000000);
127433d6423SLionel Sambuc 	BOOT_VERBOSE(cpu_print_freq(cpuid));
128433d6423SLionel Sambuc }
129433d6423SLionel Sambuc 
130433d6423SLionel Sambuc int init_local_timer(unsigned freq)
131433d6423SLionel Sambuc {
132433d6423SLionel Sambuc #ifdef USE_APIC
133433d6423SLionel Sambuc 	/* if we know the address, lapic is enabled and we should use it */
134433d6423SLionel Sambuc 	if (lapic_addr) {
135433d6423SLionel Sambuc 		unsigned cpu = cpuid;
136433d6423SLionel Sambuc 		tsc_per_ms[cpu] = (unsigned long)(cpu_get_freq(cpu) / 1000);
137433d6423SLionel Sambuc 		lapic_set_timer_one_shot(1000000 / system_hz);
138433d6423SLionel Sambuc 	} else {
139*da9af514SLionel Sambuc 		DEBUGBASIC(("Initiating legacy i8253 timer\n"));
140433d6423SLionel Sambuc #else
141433d6423SLionel Sambuc 	{
142433d6423SLionel Sambuc #endif
143433d6423SLionel Sambuc 		init_8253A_timer(freq);
144433d6423SLionel Sambuc 		estimate_cpu_freq();
145433d6423SLionel Sambuc 		/* always only 1 cpu in the system */
146433d6423SLionel Sambuc 		tsc_per_ms[0] = (unsigned long)(cpu_get_freq(0) / 1000);
147433d6423SLionel Sambuc 	}
148433d6423SLionel Sambuc 
149433d6423SLionel Sambuc 	return 0;
150433d6423SLionel Sambuc }
151433d6423SLionel Sambuc 
152433d6423SLionel Sambuc void stop_local_timer(void)
153433d6423SLionel Sambuc {
154433d6423SLionel Sambuc #ifdef USE_APIC
155433d6423SLionel Sambuc 	if (lapic_addr) {
156433d6423SLionel Sambuc 		lapic_stop_timer();
157433d6423SLionel Sambuc 		apic_eoi();
158433d6423SLionel Sambuc 	} else
159433d6423SLionel Sambuc #endif
160433d6423SLionel Sambuc 	{
161433d6423SLionel Sambuc 		stop_8253A_timer();
162433d6423SLionel Sambuc 	}
163433d6423SLionel Sambuc }
164433d6423SLionel Sambuc 
165433d6423SLionel Sambuc void restart_local_timer(void)
166433d6423SLionel Sambuc {
167433d6423SLionel Sambuc #ifdef USE_APIC
168433d6423SLionel Sambuc 	if (lapic_addr) {
169433d6423SLionel Sambuc 		lapic_restart_timer();
170433d6423SLionel Sambuc 	}
171433d6423SLionel Sambuc #endif
172433d6423SLionel Sambuc }
173433d6423SLionel Sambuc 
174433d6423SLionel Sambuc int register_local_timer_handler(const irq_handler_t handler)
175433d6423SLionel Sambuc {
176433d6423SLionel Sambuc #ifdef USE_APIC
177433d6423SLionel Sambuc 	if (lapic_addr) {
178433d6423SLionel Sambuc 		/* Using APIC, it is configured in apic_idt_init() */
179433d6423SLionel Sambuc 		BOOT_VERBOSE(printf("Using LAPIC timer as tick source\n"));
180433d6423SLionel Sambuc 	} else
181433d6423SLionel Sambuc #endif
182433d6423SLionel Sambuc 	{
183433d6423SLionel Sambuc 		/* Using PIC, Initialize the CLOCK's interrupt hook. */
184433d6423SLionel Sambuc 		pic_timer_hook.proc_nr_e = NONE;
185433d6423SLionel Sambuc 		pic_timer_hook.irq = CLOCK_IRQ;
186433d6423SLionel Sambuc 
187433d6423SLionel Sambuc 		put_irq_handler(&pic_timer_hook, CLOCK_IRQ, handler);
188433d6423SLionel Sambuc 	}
189433d6423SLionel Sambuc 
190433d6423SLionel Sambuc 	return 0;
191433d6423SLionel Sambuc }
192433d6423SLionel Sambuc 
193433d6423SLionel Sambuc void cycles_accounting_init(void)
194433d6423SLionel Sambuc {
195433d6423SLionel Sambuc #ifdef CONFIG_SMP
196433d6423SLionel Sambuc 	unsigned cpu = cpuid;
197433d6423SLionel Sambuc #endif
198433d6423SLionel Sambuc 
199433d6423SLionel Sambuc 	read_tsc_64(get_cpu_var_ptr(cpu, tsc_ctr_switch));
200433d6423SLionel Sambuc 
201433d6423SLionel Sambuc        get_cpu_var(cpu, cpu_last_tsc) = 0;
202433d6423SLionel Sambuc        get_cpu_var(cpu, cpu_last_idle) = 0;
203433d6423SLionel Sambuc }
204433d6423SLionel Sambuc 
205433d6423SLionel Sambuc void context_stop(struct proc * p)
206433d6423SLionel Sambuc {
207433d6423SLionel Sambuc 	u64_t tsc, tsc_delta;
208433d6423SLionel Sambuc 	u64_t * __tsc_ctr_switch = get_cpulocal_var_ptr(tsc_ctr_switch);
209433d6423SLionel Sambuc #ifdef CONFIG_SMP
210433d6423SLionel Sambuc 	unsigned cpu = cpuid;
211433d6423SLionel Sambuc 	int must_bkl_unlock = 0;
212433d6423SLionel Sambuc 
213433d6423SLionel Sambuc 	/*
214433d6423SLionel Sambuc 	 * This function is called only if we switch from kernel to user or idle
215433d6423SLionel Sambuc 	 * or back. Therefore this is a perfect location to place the big kernel
216433d6423SLionel Sambuc 	 * lock which will hopefully disappear soon.
217433d6423SLionel Sambuc 	 *
218433d6423SLionel Sambuc 	 * If we stop accounting for KERNEL we must unlock the BKL. If account
219433d6423SLionel Sambuc 	 * for IDLE we must not hold the lock
220433d6423SLionel Sambuc 	 */
221433d6423SLionel Sambuc 	if (p == proc_addr(KERNEL)) {
222433d6423SLionel Sambuc 		u64_t tmp;
223433d6423SLionel Sambuc 
224433d6423SLionel Sambuc 		read_tsc_64(&tsc);
225433d6423SLionel Sambuc 		tmp = tsc - *__tsc_ctr_switch;
226433d6423SLionel Sambuc 		kernel_ticks[cpu] = kernel_ticks[cpu] + tmp;
227433d6423SLionel Sambuc 		p->p_cycles = p->p_cycles + tmp;
228433d6423SLionel Sambuc 		must_bkl_unlock = 1;
229433d6423SLionel Sambuc 	} else {
230433d6423SLionel Sambuc 		u64_t bkl_tsc;
231433d6423SLionel Sambuc 		atomic_t succ;
232433d6423SLionel Sambuc 
233433d6423SLionel Sambuc 		read_tsc_64(&bkl_tsc);
234433d6423SLionel Sambuc 		/* this only gives a good estimate */
235433d6423SLionel Sambuc 		succ = big_kernel_lock.val;
236433d6423SLionel Sambuc 
237433d6423SLionel Sambuc 		BKL_LOCK();
238433d6423SLionel Sambuc 
239433d6423SLionel Sambuc 		read_tsc_64(&tsc);
240433d6423SLionel Sambuc 
241433d6423SLionel Sambuc 		bkl_ticks[cpu] = bkl_ticks[cpu] + tsc - bkl_tsc;
242433d6423SLionel Sambuc 		bkl_tries[cpu]++;
243433d6423SLionel Sambuc 		bkl_succ[cpu] += !(!(succ == 0));
244433d6423SLionel Sambuc 
245433d6423SLionel Sambuc 		p->p_cycles = p->p_cycles + tsc - *__tsc_ctr_switch;
246433d6423SLionel Sambuc 
247433d6423SLionel Sambuc #ifdef CONFIG_SMP
248433d6423SLionel Sambuc 		/*
249433d6423SLionel Sambuc 		 * Since at the time we got a scheduling IPI we might have been
250433d6423SLionel Sambuc 		 * waiting for BKL already, we may miss it due to a similar IPI to
251433d6423SLionel Sambuc 		 * the cpu which is already waiting for us to handle its. This
252433d6423SLionel Sambuc 		 * results in a live-lock of these two cpus.
253433d6423SLionel Sambuc 		 *
254433d6423SLionel Sambuc 		 * Therefore we always check if there is one pending and if so,
255433d6423SLionel Sambuc 		 * we handle it straight away so the other cpu can continue and
256433d6423SLionel Sambuc 		 * we do not deadlock.
257433d6423SLionel Sambuc 		 */
258433d6423SLionel Sambuc 		smp_sched_handler();
259433d6423SLionel Sambuc #endif
260433d6423SLionel Sambuc 	}
261433d6423SLionel Sambuc #else
262433d6423SLionel Sambuc 	read_tsc_64(&tsc);
263433d6423SLionel Sambuc 	p->p_cycles = p->p_cycles + tsc - *__tsc_ctr_switch;
264433d6423SLionel Sambuc #endif
265433d6423SLionel Sambuc 
266433d6423SLionel Sambuc 	tsc_delta = tsc - *__tsc_ctr_switch;
267433d6423SLionel Sambuc 
268433d6423SLionel Sambuc 	if (kbill_ipc) {
269433d6423SLionel Sambuc 		kbill_ipc->p_kipc_cycles =
270433d6423SLionel Sambuc 			kbill_ipc->p_kipc_cycles + tsc_delta;
271433d6423SLionel Sambuc 		kbill_ipc = NULL;
272433d6423SLionel Sambuc 	}
273433d6423SLionel Sambuc 
274433d6423SLionel Sambuc 	if (kbill_kcall) {
275433d6423SLionel Sambuc 		kbill_kcall->p_kcall_cycles =
276433d6423SLionel Sambuc 			kbill_kcall->p_kcall_cycles + tsc_delta;
277433d6423SLionel Sambuc 		kbill_kcall = NULL;
278433d6423SLionel Sambuc 	}
279433d6423SLionel Sambuc 
280433d6423SLionel Sambuc 	/*
281433d6423SLionel Sambuc 	 * deduct the just consumed cpu cycles from the cpu time left for this
282433d6423SLionel Sambuc 	 * process during its current quantum. Skip IDLE and other pseudo kernel
283433d6423SLionel Sambuc 	 * tasks
284433d6423SLionel Sambuc 	 */
285433d6423SLionel Sambuc 	if (p->p_endpoint >= 0) {
286433d6423SLionel Sambuc #if DEBUG_RACE
287433d6423SLionel Sambuc 		p->p_cpu_time_left = 0;
288433d6423SLionel Sambuc #else
289433d6423SLionel Sambuc 		/* if (tsc_delta < p->p_cpu_time_left) in 64bit */
290433d6423SLionel Sambuc 		if (ex64hi(tsc_delta) < ex64hi(p->p_cpu_time_left) ||
291433d6423SLionel Sambuc 				(ex64hi(tsc_delta) == ex64hi(p->p_cpu_time_left) &&
292433d6423SLionel Sambuc 				 ex64lo(tsc_delta) < ex64lo(p->p_cpu_time_left)))
293433d6423SLionel Sambuc 			p->p_cpu_time_left = p->p_cpu_time_left - tsc_delta;
294433d6423SLionel Sambuc 		else {
295433d6423SLionel Sambuc 			p->p_cpu_time_left = 0;
296433d6423SLionel Sambuc 		}
297433d6423SLionel Sambuc #endif
298433d6423SLionel Sambuc 	}
299433d6423SLionel Sambuc 
300433d6423SLionel Sambuc 	*__tsc_ctr_switch = tsc;
301433d6423SLionel Sambuc 
302433d6423SLionel Sambuc #ifdef CONFIG_SMP
303433d6423SLionel Sambuc 	if(must_bkl_unlock) {
304433d6423SLionel Sambuc 		BKL_UNLOCK();
305433d6423SLionel Sambuc 	}
306433d6423SLionel Sambuc #endif
307433d6423SLionel Sambuc }
308433d6423SLionel Sambuc 
309433d6423SLionel Sambuc void context_stop_idle(void)
310433d6423SLionel Sambuc {
311433d6423SLionel Sambuc 	int is_idle;
312433d6423SLionel Sambuc #ifdef CONFIG_SMP
313433d6423SLionel Sambuc 	unsigned cpu = cpuid;
314433d6423SLionel Sambuc #endif
315433d6423SLionel Sambuc 
316433d6423SLionel Sambuc 	is_idle = get_cpu_var(cpu, cpu_is_idle);
317433d6423SLionel Sambuc 	get_cpu_var(cpu, cpu_is_idle) = 0;
318433d6423SLionel Sambuc 
319433d6423SLionel Sambuc 	context_stop(get_cpulocal_var_ptr(idle_proc));
320433d6423SLionel Sambuc 
321433d6423SLionel Sambuc 	if (is_idle)
322433d6423SLionel Sambuc 		restart_local_timer();
323433d6423SLionel Sambuc #if SPROFILE
324433d6423SLionel Sambuc 	if (sprofiling)
325433d6423SLionel Sambuc 		get_cpulocal_var(idle_interrupted) = 1;
326433d6423SLionel Sambuc #endif
327433d6423SLionel Sambuc }
328433d6423SLionel Sambuc 
329433d6423SLionel Sambuc u64_t ms_2_cpu_time(unsigned ms)
330433d6423SLionel Sambuc {
331433d6423SLionel Sambuc 	return (u64_t)tsc_per_ms[cpuid] * ms;
332433d6423SLionel Sambuc }
333433d6423SLionel Sambuc 
334433d6423SLionel Sambuc unsigned cpu_time_2_ms(u64_t cpu_time)
335433d6423SLionel Sambuc {
336433d6423SLionel Sambuc 	return (unsigned long)(cpu_time / tsc_per_ms[cpuid]);
337433d6423SLionel Sambuc }
338433d6423SLionel Sambuc 
339433d6423SLionel Sambuc short cpu_load(void)
340433d6423SLionel Sambuc {
341433d6423SLionel Sambuc 	u64_t current_tsc, *current_idle;
342433d6423SLionel Sambuc 	u64_t tsc_delta, idle_delta, busy;
343433d6423SLionel Sambuc 	struct proc *idle;
344433d6423SLionel Sambuc 	short load;
345433d6423SLionel Sambuc #ifdef CONFIG_SMP
346433d6423SLionel Sambuc 	unsigned cpu = cpuid;
347433d6423SLionel Sambuc #endif
348433d6423SLionel Sambuc 
349433d6423SLionel Sambuc 	u64_t *last_tsc, *last_idle;
350433d6423SLionel Sambuc 
351433d6423SLionel Sambuc 	last_tsc = get_cpu_var_ptr(cpu, cpu_last_tsc);
352433d6423SLionel Sambuc 	last_idle = get_cpu_var_ptr(cpu, cpu_last_idle);
353433d6423SLionel Sambuc 
354433d6423SLionel Sambuc 	idle = get_cpu_var_ptr(cpu, idle_proc);;
355433d6423SLionel Sambuc 	read_tsc_64(&current_tsc);
356433d6423SLionel Sambuc 	current_idle = &idle->p_cycles; /* ptr to idle proc */
357433d6423SLionel Sambuc 
358433d6423SLionel Sambuc 	/* calculate load since last cpu_load invocation */
359433d6423SLionel Sambuc 	if (*last_tsc) {
360433d6423SLionel Sambuc 		tsc_delta = current_tsc - *last_tsc;
361433d6423SLionel Sambuc 		idle_delta = *current_idle - *last_idle;
362433d6423SLionel Sambuc 
363433d6423SLionel Sambuc 		busy = tsc_delta - idle_delta;
364433d6423SLionel Sambuc 		busy = busy * 100;
365433d6423SLionel Sambuc 		load = ex64lo(busy / tsc_delta);
366433d6423SLionel Sambuc 
367433d6423SLionel Sambuc 		if (load > 100)
368433d6423SLionel Sambuc 			load = 100;
369433d6423SLionel Sambuc 	} else
370433d6423SLionel Sambuc 		load = 0;
371433d6423SLionel Sambuc 
372433d6423SLionel Sambuc 	*last_tsc = current_tsc;
373433d6423SLionel Sambuc 	*last_idle = *current_idle;
374433d6423SLionel Sambuc 	return load;
375433d6423SLionel Sambuc }
376433d6423SLionel Sambuc 
377433d6423SLionel Sambuc void busy_delay_ms(int ms)
378433d6423SLionel Sambuc {
379433d6423SLionel Sambuc 	u64_t cycles = ms_2_cpu_time(ms), tsc0, tsc, tsc1;
380433d6423SLionel Sambuc 	read_tsc_64(&tsc0);
381433d6423SLionel Sambuc 	tsc1 = tsc0 + cycles;
382433d6423SLionel Sambuc 	do { read_tsc_64(&tsc); } while(tsc < tsc1);
383433d6423SLionel Sambuc 	return;
384433d6423SLionel Sambuc }
385433d6423SLionel Sambuc 
386