xref: /minix3/minix/kernel/arch/i386/arch_clock.c (revision 366d18b2b85b51f93b2a90700336b4a150a9149d)
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 
15*366d18b2SDavid van Moolenbroek #include <sys/sched.h> /* for CP_*, CPUSTATES */
16*366d18b2SDavid van Moolenbroek #if CPUSTATES != MINIX_CPUSTATES
17*366d18b2SDavid van Moolenbroek /* If this breaks, the code in this file may have to be adapted accordingly. */
18*366d18b2SDavid van Moolenbroek #error "MINIX_CPUSTATES value is out of sync with NetBSD's!"
19*366d18b2SDavid van Moolenbroek #endif
20433d6423SLionel Sambuc 
21433d6423SLionel Sambuc #ifdef USE_APIC
22433d6423SLionel Sambuc #include "apic.h"
23433d6423SLionel Sambuc #endif
24433d6423SLionel Sambuc 
25433d6423SLionel Sambuc #include "kernel/spinlock.h"
26433d6423SLionel Sambuc 
27433d6423SLionel Sambuc #ifdef CONFIG_SMP
28433d6423SLionel Sambuc #include "kernel/smp.h"
29433d6423SLionel Sambuc #endif
30433d6423SLionel Sambuc 
31433d6423SLionel Sambuc #define CLOCK_ACK_BIT   0x80    /* PS/2 clock interrupt acknowledge bit */
32433d6423SLionel Sambuc 
33433d6423SLionel Sambuc /* Clock parameters. */
34433d6423SLionel Sambuc #define COUNTER_FREQ (2*TIMER_FREQ) /* counter frequency using square wave */
35433d6423SLionel Sambuc #define LATCH_COUNT     0x00    /* cc00xxxx, c = channel, x = any */
36433d6423SLionel Sambuc #define SQUARE_WAVE     0x36    /* ccaammmb, a = access, m = mode, b = BCD */
37433d6423SLionel Sambuc                                 /*   11x11, 11 = LSB then MSB, x11 = sq wave */
38433d6423SLionel Sambuc #define TIMER_FREQ  1193182    /* clock frequency for timer in PC and AT */
39433d6423SLionel Sambuc #define TIMER_COUNT(freq) (TIMER_FREQ/(freq)) /* initial value for counter*/
40433d6423SLionel Sambuc 
41433d6423SLionel Sambuc static irq_hook_t pic_timer_hook;		/* interrupt handler hook */
42433d6423SLionel Sambuc 
43433d6423SLionel Sambuc static unsigned probe_ticks;
44433d6423SLionel Sambuc static u64_t tsc0, tsc1;
45433d6423SLionel Sambuc #define PROBE_TICKS	(system_hz / 10)
46433d6423SLionel Sambuc 
47433d6423SLionel Sambuc static unsigned tsc_per_ms[CONFIG_MAX_CPUS];
48*366d18b2SDavid van Moolenbroek static unsigned tsc_per_tick[CONFIG_MAX_CPUS];
49*366d18b2SDavid van Moolenbroek static uint64_t tsc_per_state[CONFIG_MAX_CPUS][CPUSTATES];
50433d6423SLionel Sambuc 
51433d6423SLionel Sambuc /*===========================================================================*
52433d6423SLionel Sambuc  *				init_8235A_timer			     *
53433d6423SLionel Sambuc  *===========================================================================*/
54433d6423SLionel Sambuc int init_8253A_timer(const unsigned freq)
55433d6423SLionel Sambuc {
56433d6423SLionel Sambuc 	/* Initialize channel 0 of the 8253A timer to, e.g., 60 Hz,
57433d6423SLionel Sambuc 	 * and register the CLOCK task's interrupt handler to be run
58433d6423SLionel Sambuc 	 * on every clock tick.
59433d6423SLionel Sambuc 	 */
60433d6423SLionel Sambuc 	outb(TIMER_MODE, SQUARE_WAVE);  /* run continuously */
61433d6423SLionel Sambuc 	outb(TIMER0, (TIMER_COUNT(freq) & 0xff)); /* timer low byte */
62433d6423SLionel Sambuc 	outb(TIMER0, TIMER_COUNT(freq) >> 8); /* timer high byte */
63433d6423SLionel Sambuc 
64433d6423SLionel Sambuc 	return OK;
65433d6423SLionel Sambuc }
66433d6423SLionel Sambuc 
67433d6423SLionel Sambuc /*===========================================================================*
68433d6423SLionel Sambuc  *				stop_8235A_timer			     *
69433d6423SLionel Sambuc  *===========================================================================*/
70433d6423SLionel Sambuc void stop_8253A_timer(void)
71433d6423SLionel Sambuc {
72433d6423SLionel Sambuc 	/* Reset the clock to the BIOS rate. (For rebooting.) */
73433d6423SLionel Sambuc 	outb(TIMER_MODE, 0x36);
74433d6423SLionel Sambuc 	outb(TIMER0, 0);
75433d6423SLionel Sambuc 	outb(TIMER0, 0);
76433d6423SLionel Sambuc }
77433d6423SLionel Sambuc 
78433d6423SLionel Sambuc void arch_timer_int_handler(void)
79433d6423SLionel Sambuc {
80433d6423SLionel Sambuc }
81433d6423SLionel Sambuc 
82433d6423SLionel Sambuc static int calib_cpu_handler(irq_hook_t * UNUSED(hook))
83433d6423SLionel Sambuc {
84433d6423SLionel Sambuc 	u64_t tsc;
85433d6423SLionel Sambuc 
86433d6423SLionel Sambuc 	probe_ticks++;
87433d6423SLionel Sambuc 	read_tsc_64(&tsc);
88433d6423SLionel Sambuc 
89433d6423SLionel Sambuc 
90433d6423SLionel Sambuc 	if (probe_ticks == 1) {
91433d6423SLionel Sambuc 		tsc0 = tsc;
92433d6423SLionel Sambuc 	}
93433d6423SLionel Sambuc 	else if (probe_ticks == PROBE_TICKS) {
94433d6423SLionel Sambuc 		tsc1 = tsc;
95433d6423SLionel Sambuc 	}
96433d6423SLionel Sambuc 
97433d6423SLionel Sambuc 	/* just in case we are in an SMP single cpu fallback mode */
98433d6423SLionel Sambuc 	BKL_UNLOCK();
99433d6423SLionel Sambuc 	return 1;
100433d6423SLionel Sambuc }
101433d6423SLionel Sambuc 
102433d6423SLionel Sambuc static void estimate_cpu_freq(void)
103433d6423SLionel Sambuc {
104433d6423SLionel Sambuc 	u64_t tsc_delta;
105433d6423SLionel Sambuc 	u64_t cpu_freq;
106433d6423SLionel Sambuc 
107433d6423SLionel Sambuc 	irq_hook_t calib_cpu;
108433d6423SLionel Sambuc 
109433d6423SLionel Sambuc 	/* set the probe, we use the legacy timer, IRQ 0 */
110433d6423SLionel Sambuc 	put_irq_handler(&calib_cpu, CLOCK_IRQ, calib_cpu_handler);
111433d6423SLionel Sambuc 
112433d6423SLionel Sambuc 	/* just in case we are in an SMP single cpu fallback mode */
113433d6423SLionel Sambuc 	BKL_UNLOCK();
114433d6423SLionel Sambuc 	/* set the PIC timer to get some time */
115433d6423SLionel Sambuc 	intr_enable();
116433d6423SLionel Sambuc 
117433d6423SLionel Sambuc 	/* loop for some time to get a sample */
118433d6423SLionel Sambuc 	while(probe_ticks < PROBE_TICKS) {
119433d6423SLionel Sambuc 		intr_enable();
120433d6423SLionel Sambuc 	}
121433d6423SLionel Sambuc 
122433d6423SLionel Sambuc 	intr_disable();
123433d6423SLionel Sambuc 	/* just in case we are in an SMP single cpu fallback mode */
124433d6423SLionel Sambuc 	BKL_LOCK();
125433d6423SLionel Sambuc 
126433d6423SLionel Sambuc 	/* remove the probe */
127433d6423SLionel Sambuc 	rm_irq_handler(&calib_cpu);
128433d6423SLionel Sambuc 
129433d6423SLionel Sambuc 	tsc_delta = tsc1 - tsc0;
130433d6423SLionel Sambuc 
131433d6423SLionel Sambuc 	cpu_freq = (tsc_delta / (PROBE_TICKS - 1)) * system_hz;
132433d6423SLionel Sambuc 	cpu_set_freq(cpuid, cpu_freq);
133433d6423SLionel Sambuc 	cpu_info[cpuid].freq = (unsigned long)(cpu_freq / 1000000);
134433d6423SLionel Sambuc 	BOOT_VERBOSE(cpu_print_freq(cpuid));
135433d6423SLionel Sambuc }
136433d6423SLionel Sambuc 
137433d6423SLionel Sambuc int init_local_timer(unsigned freq)
138433d6423SLionel Sambuc {
139433d6423SLionel Sambuc #ifdef USE_APIC
140433d6423SLionel Sambuc 	/* if we know the address, lapic is enabled and we should use it */
141433d6423SLionel Sambuc 	if (lapic_addr) {
142433d6423SLionel Sambuc 		unsigned cpu = cpuid;
143*366d18b2SDavid van Moolenbroek 		tsc_per_ms[cpu] = (unsigned)(cpu_get_freq(cpu) / 1000);
144*366d18b2SDavid van Moolenbroek 		tsc_per_tick[cpu] = (unsigned)(cpu_get_freq(cpu) / system_hz);
145433d6423SLionel Sambuc 		lapic_set_timer_one_shot(1000000 / system_hz);
146433d6423SLionel Sambuc 	} else {
147da9af514SLionel Sambuc 		DEBUGBASIC(("Initiating legacy i8253 timer\n"));
148433d6423SLionel Sambuc #else
149433d6423SLionel Sambuc 	{
150433d6423SLionel Sambuc #endif
151433d6423SLionel Sambuc 		init_8253A_timer(freq);
152433d6423SLionel Sambuc 		estimate_cpu_freq();
153433d6423SLionel Sambuc 		/* always only 1 cpu in the system */
154433d6423SLionel Sambuc 		tsc_per_ms[0] = (unsigned long)(cpu_get_freq(0) / 1000);
155*366d18b2SDavid van Moolenbroek 		tsc_per_tick[0] = (unsigned)(cpu_get_freq(0) / system_hz);
156433d6423SLionel Sambuc 	}
157433d6423SLionel Sambuc 
158433d6423SLionel Sambuc 	return 0;
159433d6423SLionel Sambuc }
160433d6423SLionel Sambuc 
161433d6423SLionel Sambuc void stop_local_timer(void)
162433d6423SLionel Sambuc {
163433d6423SLionel Sambuc #ifdef USE_APIC
164433d6423SLionel Sambuc 	if (lapic_addr) {
165433d6423SLionel Sambuc 		lapic_stop_timer();
166433d6423SLionel Sambuc 		apic_eoi();
167433d6423SLionel Sambuc 	} else
168433d6423SLionel Sambuc #endif
169433d6423SLionel Sambuc 	{
170433d6423SLionel Sambuc 		stop_8253A_timer();
171433d6423SLionel Sambuc 	}
172433d6423SLionel Sambuc }
173433d6423SLionel Sambuc 
174433d6423SLionel Sambuc void restart_local_timer(void)
175433d6423SLionel Sambuc {
176433d6423SLionel Sambuc #ifdef USE_APIC
177433d6423SLionel Sambuc 	if (lapic_addr) {
178433d6423SLionel Sambuc 		lapic_restart_timer();
179433d6423SLionel Sambuc 	}
180433d6423SLionel Sambuc #endif
181433d6423SLionel Sambuc }
182433d6423SLionel Sambuc 
183433d6423SLionel Sambuc int register_local_timer_handler(const irq_handler_t handler)
184433d6423SLionel Sambuc {
185433d6423SLionel Sambuc #ifdef USE_APIC
186433d6423SLionel Sambuc 	if (lapic_addr) {
187433d6423SLionel Sambuc 		/* Using APIC, it is configured in apic_idt_init() */
188433d6423SLionel Sambuc 		BOOT_VERBOSE(printf("Using LAPIC timer as tick source\n"));
189433d6423SLionel Sambuc 	} else
190433d6423SLionel Sambuc #endif
191433d6423SLionel Sambuc 	{
192433d6423SLionel Sambuc 		/* Using PIC, Initialize the CLOCK's interrupt hook. */
193433d6423SLionel Sambuc 		pic_timer_hook.proc_nr_e = NONE;
194433d6423SLionel Sambuc 		pic_timer_hook.irq = CLOCK_IRQ;
195433d6423SLionel Sambuc 
196433d6423SLionel Sambuc 		put_irq_handler(&pic_timer_hook, CLOCK_IRQ, handler);
197433d6423SLionel Sambuc 	}
198433d6423SLionel Sambuc 
199433d6423SLionel Sambuc 	return 0;
200433d6423SLionel Sambuc }
201433d6423SLionel Sambuc 
202433d6423SLionel Sambuc void cycles_accounting_init(void)
203433d6423SLionel Sambuc {
204433d6423SLionel Sambuc #ifdef CONFIG_SMP
205433d6423SLionel Sambuc 	unsigned cpu = cpuid;
206433d6423SLionel Sambuc #endif
207433d6423SLionel Sambuc 
208433d6423SLionel Sambuc 	read_tsc_64(get_cpu_var_ptr(cpu, tsc_ctr_switch));
209433d6423SLionel Sambuc 
210433d6423SLionel Sambuc        get_cpu_var(cpu, cpu_last_tsc) = 0;
211433d6423SLionel Sambuc        get_cpu_var(cpu, cpu_last_idle) = 0;
212433d6423SLionel Sambuc }
213433d6423SLionel Sambuc 
214433d6423SLionel Sambuc void context_stop(struct proc * p)
215433d6423SLionel Sambuc {
216433d6423SLionel Sambuc 	u64_t tsc, tsc_delta;
217433d6423SLionel Sambuc 	u64_t * __tsc_ctr_switch = get_cpulocal_var_ptr(tsc_ctr_switch);
218*366d18b2SDavid van Moolenbroek 	unsigned int cpu, counter;
219433d6423SLionel Sambuc #ifdef CONFIG_SMP
220433d6423SLionel Sambuc 	int must_bkl_unlock = 0;
221433d6423SLionel Sambuc 
222*366d18b2SDavid van Moolenbroek 	cpu = cpuid;
223*366d18b2SDavid van Moolenbroek 
224433d6423SLionel Sambuc 	/*
225433d6423SLionel Sambuc 	 * This function is called only if we switch from kernel to user or idle
226433d6423SLionel Sambuc 	 * or back. Therefore this is a perfect location to place the big kernel
227433d6423SLionel Sambuc 	 * lock which will hopefully disappear soon.
228433d6423SLionel Sambuc 	 *
229433d6423SLionel Sambuc 	 * If we stop accounting for KERNEL we must unlock the BKL. If account
230433d6423SLionel Sambuc 	 * for IDLE we must not hold the lock
231433d6423SLionel Sambuc 	 */
232433d6423SLionel Sambuc 	if (p == proc_addr(KERNEL)) {
233433d6423SLionel Sambuc 		u64_t tmp;
234433d6423SLionel Sambuc 
235433d6423SLionel Sambuc 		read_tsc_64(&tsc);
236433d6423SLionel Sambuc 		tmp = tsc - *__tsc_ctr_switch;
237433d6423SLionel Sambuc 		kernel_ticks[cpu] = kernel_ticks[cpu] + tmp;
238433d6423SLionel Sambuc 		p->p_cycles = p->p_cycles + tmp;
239433d6423SLionel Sambuc 		must_bkl_unlock = 1;
240433d6423SLionel Sambuc 	} else {
241433d6423SLionel Sambuc 		u64_t bkl_tsc;
242433d6423SLionel Sambuc 		atomic_t succ;
243433d6423SLionel Sambuc 
244433d6423SLionel Sambuc 		read_tsc_64(&bkl_tsc);
245433d6423SLionel Sambuc 		/* this only gives a good estimate */
246433d6423SLionel Sambuc 		succ = big_kernel_lock.val;
247433d6423SLionel Sambuc 
248433d6423SLionel Sambuc 		BKL_LOCK();
249433d6423SLionel Sambuc 
250433d6423SLionel Sambuc 		read_tsc_64(&tsc);
251433d6423SLionel Sambuc 
252433d6423SLionel Sambuc 		bkl_ticks[cpu] = bkl_ticks[cpu] + tsc - bkl_tsc;
253433d6423SLionel Sambuc 		bkl_tries[cpu]++;
254433d6423SLionel Sambuc 		bkl_succ[cpu] += !(!(succ == 0));
255433d6423SLionel Sambuc 
256433d6423SLionel Sambuc 		p->p_cycles = p->p_cycles + tsc - *__tsc_ctr_switch;
257433d6423SLionel Sambuc 
258433d6423SLionel Sambuc #ifdef CONFIG_SMP
259433d6423SLionel Sambuc 		/*
260433d6423SLionel Sambuc 		 * Since at the time we got a scheduling IPI we might have been
261433d6423SLionel Sambuc 		 * waiting for BKL already, we may miss it due to a similar IPI to
262433d6423SLionel Sambuc 		 * the cpu which is already waiting for us to handle its. This
263433d6423SLionel Sambuc 		 * results in a live-lock of these two cpus.
264433d6423SLionel Sambuc 		 *
265433d6423SLionel Sambuc 		 * Therefore we always check if there is one pending and if so,
266433d6423SLionel Sambuc 		 * we handle it straight away so the other cpu can continue and
267433d6423SLionel Sambuc 		 * we do not deadlock.
268433d6423SLionel Sambuc 		 */
269433d6423SLionel Sambuc 		smp_sched_handler();
270433d6423SLionel Sambuc #endif
271433d6423SLionel Sambuc 	}
272433d6423SLionel Sambuc #else
273433d6423SLionel Sambuc 	read_tsc_64(&tsc);
274433d6423SLionel Sambuc 	p->p_cycles = p->p_cycles + tsc - *__tsc_ctr_switch;
275*366d18b2SDavid van Moolenbroek 	cpu = 0;
276433d6423SLionel Sambuc #endif
277433d6423SLionel Sambuc 
278433d6423SLionel Sambuc 	tsc_delta = tsc - *__tsc_ctr_switch;
279433d6423SLionel Sambuc 
280433d6423SLionel Sambuc 	if (kbill_ipc) {
281433d6423SLionel Sambuc 		kbill_ipc->p_kipc_cycles =
282433d6423SLionel Sambuc 			kbill_ipc->p_kipc_cycles + tsc_delta;
283433d6423SLionel Sambuc 		kbill_ipc = NULL;
284433d6423SLionel Sambuc 	}
285433d6423SLionel Sambuc 
286433d6423SLionel Sambuc 	if (kbill_kcall) {
287433d6423SLionel Sambuc 		kbill_kcall->p_kcall_cycles =
288433d6423SLionel Sambuc 			kbill_kcall->p_kcall_cycles + tsc_delta;
289433d6423SLionel Sambuc 		kbill_kcall = NULL;
290433d6423SLionel Sambuc 	}
291433d6423SLionel Sambuc 
292433d6423SLionel Sambuc 	/*
293433d6423SLionel Sambuc 	 * deduct the just consumed cpu cycles from the cpu time left for this
294433d6423SLionel Sambuc 	 * process during its current quantum. Skip IDLE and other pseudo kernel
295*366d18b2SDavid van Moolenbroek 	 * tasks, except for global accounting purposes.
296433d6423SLionel Sambuc 	 */
297433d6423SLionel Sambuc 	if (p->p_endpoint >= 0) {
298*366d18b2SDavid van Moolenbroek 		/* On MINIX3, the "system" counter covers system processes. */
299*366d18b2SDavid van Moolenbroek 		if (p->p_priv != priv_addr(USER_PRIV_ID))
300*366d18b2SDavid van Moolenbroek 			counter = CP_SYS;
301*366d18b2SDavid van Moolenbroek 		else if (p->p_misc_flags & MF_NICED)
302*366d18b2SDavid van Moolenbroek 			counter = CP_NICE;
303*366d18b2SDavid van Moolenbroek 		else
304*366d18b2SDavid van Moolenbroek 			counter = CP_USER;
305*366d18b2SDavid van Moolenbroek 
306433d6423SLionel Sambuc #if DEBUG_RACE
307433d6423SLionel Sambuc 		p->p_cpu_time_left = 0;
308433d6423SLionel Sambuc #else
309433d6423SLionel Sambuc 		/* if (tsc_delta < p->p_cpu_time_left) in 64bit */
310433d6423SLionel Sambuc 		if (ex64hi(tsc_delta) < ex64hi(p->p_cpu_time_left) ||
311433d6423SLionel Sambuc 				(ex64hi(tsc_delta) == ex64hi(p->p_cpu_time_left) &&
312433d6423SLionel Sambuc 				 ex64lo(tsc_delta) < ex64lo(p->p_cpu_time_left)))
313433d6423SLionel Sambuc 			p->p_cpu_time_left = p->p_cpu_time_left - tsc_delta;
314433d6423SLionel Sambuc 		else {
315433d6423SLionel Sambuc 			p->p_cpu_time_left = 0;
316433d6423SLionel Sambuc 		}
317433d6423SLionel Sambuc #endif
318*366d18b2SDavid van Moolenbroek 	} else {
319*366d18b2SDavid van Moolenbroek 		/* On MINIX3, the "interrupts" counter covers the kernel. */
320*366d18b2SDavid van Moolenbroek 		if (p->p_endpoint == IDLE)
321*366d18b2SDavid van Moolenbroek 			counter = CP_IDLE;
322*366d18b2SDavid van Moolenbroek 		else
323*366d18b2SDavid van Moolenbroek 			counter = CP_INTR;
324433d6423SLionel Sambuc 	}
325433d6423SLionel Sambuc 
326*366d18b2SDavid van Moolenbroek 	tsc_per_state[cpu][counter] += tsc_delta;
327*366d18b2SDavid van Moolenbroek 
328433d6423SLionel Sambuc 	*__tsc_ctr_switch = tsc;
329433d6423SLionel Sambuc 
330433d6423SLionel Sambuc #ifdef CONFIG_SMP
331433d6423SLionel Sambuc 	if(must_bkl_unlock) {
332433d6423SLionel Sambuc 		BKL_UNLOCK();
333433d6423SLionel Sambuc 	}
334433d6423SLionel Sambuc #endif
335433d6423SLionel Sambuc }
336433d6423SLionel Sambuc 
337433d6423SLionel Sambuc void context_stop_idle(void)
338433d6423SLionel Sambuc {
339433d6423SLionel Sambuc 	int is_idle;
340433d6423SLionel Sambuc #ifdef CONFIG_SMP
341433d6423SLionel Sambuc 	unsigned cpu = cpuid;
342433d6423SLionel Sambuc #endif
343433d6423SLionel Sambuc 
344433d6423SLionel Sambuc 	is_idle = get_cpu_var(cpu, cpu_is_idle);
345433d6423SLionel Sambuc 	get_cpu_var(cpu, cpu_is_idle) = 0;
346433d6423SLionel Sambuc 
347433d6423SLionel Sambuc 	context_stop(get_cpulocal_var_ptr(idle_proc));
348433d6423SLionel Sambuc 
349433d6423SLionel Sambuc 	if (is_idle)
350433d6423SLionel Sambuc 		restart_local_timer();
351433d6423SLionel Sambuc #if SPROFILE
352433d6423SLionel Sambuc 	if (sprofiling)
353433d6423SLionel Sambuc 		get_cpulocal_var(idle_interrupted) = 1;
354433d6423SLionel Sambuc #endif
355433d6423SLionel Sambuc }
356433d6423SLionel Sambuc 
357433d6423SLionel Sambuc u64_t ms_2_cpu_time(unsigned ms)
358433d6423SLionel Sambuc {
359433d6423SLionel Sambuc 	return (u64_t)tsc_per_ms[cpuid] * ms;
360433d6423SLionel Sambuc }
361433d6423SLionel Sambuc 
362433d6423SLionel Sambuc unsigned cpu_time_2_ms(u64_t cpu_time)
363433d6423SLionel Sambuc {
364433d6423SLionel Sambuc 	return (unsigned long)(cpu_time / tsc_per_ms[cpuid]);
365433d6423SLionel Sambuc }
366433d6423SLionel Sambuc 
367433d6423SLionel Sambuc short cpu_load(void)
368433d6423SLionel Sambuc {
369433d6423SLionel Sambuc 	u64_t current_tsc, *current_idle;
370433d6423SLionel Sambuc 	u64_t tsc_delta, idle_delta, busy;
371433d6423SLionel Sambuc 	struct proc *idle;
372433d6423SLionel Sambuc 	short load;
373433d6423SLionel Sambuc #ifdef CONFIG_SMP
374433d6423SLionel Sambuc 	unsigned cpu = cpuid;
375433d6423SLionel Sambuc #endif
376433d6423SLionel Sambuc 
377433d6423SLionel Sambuc 	u64_t *last_tsc, *last_idle;
378433d6423SLionel Sambuc 
379433d6423SLionel Sambuc 	last_tsc = get_cpu_var_ptr(cpu, cpu_last_tsc);
380433d6423SLionel Sambuc 	last_idle = get_cpu_var_ptr(cpu, cpu_last_idle);
381433d6423SLionel Sambuc 
382433d6423SLionel Sambuc 	idle = get_cpu_var_ptr(cpu, idle_proc);;
383433d6423SLionel Sambuc 	read_tsc_64(&current_tsc);
384433d6423SLionel Sambuc 	current_idle = &idle->p_cycles; /* ptr to idle proc */
385433d6423SLionel Sambuc 
386433d6423SLionel Sambuc 	/* calculate load since last cpu_load invocation */
387433d6423SLionel Sambuc 	if (*last_tsc) {
388433d6423SLionel Sambuc 		tsc_delta = current_tsc - *last_tsc;
389433d6423SLionel Sambuc 		idle_delta = *current_idle - *last_idle;
390433d6423SLionel Sambuc 
391433d6423SLionel Sambuc 		busy = tsc_delta - idle_delta;
392433d6423SLionel Sambuc 		busy = busy * 100;
393433d6423SLionel Sambuc 		load = ex64lo(busy / tsc_delta);
394433d6423SLionel Sambuc 
395433d6423SLionel Sambuc 		if (load > 100)
396433d6423SLionel Sambuc 			load = 100;
397433d6423SLionel Sambuc 	} else
398433d6423SLionel Sambuc 		load = 0;
399433d6423SLionel Sambuc 
400433d6423SLionel Sambuc 	*last_tsc = current_tsc;
401433d6423SLionel Sambuc 	*last_idle = *current_idle;
402433d6423SLionel Sambuc 	return load;
403433d6423SLionel Sambuc }
404433d6423SLionel Sambuc 
405433d6423SLionel Sambuc void busy_delay_ms(int ms)
406433d6423SLionel Sambuc {
407433d6423SLionel Sambuc 	u64_t cycles = ms_2_cpu_time(ms), tsc0, tsc, tsc1;
408433d6423SLionel Sambuc 	read_tsc_64(&tsc0);
409433d6423SLionel Sambuc 	tsc1 = tsc0 + cycles;
410433d6423SLionel Sambuc 	do { read_tsc_64(&tsc); } while(tsc < tsc1);
411433d6423SLionel Sambuc 	return;
412433d6423SLionel Sambuc }
413433d6423SLionel Sambuc 
414*366d18b2SDavid van Moolenbroek /*
415*366d18b2SDavid van Moolenbroek  * Return the number of clock ticks spent in each of a predefined number of
416*366d18b2SDavid van Moolenbroek  * CPU states.
417*366d18b2SDavid van Moolenbroek  */
418*366d18b2SDavid van Moolenbroek void
419*366d18b2SDavid van Moolenbroek get_cpu_ticks(unsigned int cpu, uint64_t ticks[CPUSTATES])
420*366d18b2SDavid van Moolenbroek {
421*366d18b2SDavid van Moolenbroek 	int i;
422*366d18b2SDavid van Moolenbroek 
423*366d18b2SDavid van Moolenbroek 	/* TODO: make this inter-CPU safe! */
424*366d18b2SDavid van Moolenbroek 	for (i = 0; i < CPUSTATES; i++)
425*366d18b2SDavid van Moolenbroek 		ticks[i] = tsc_per_state[cpu][i] / tsc_per_tick[cpu];
426*366d18b2SDavid van Moolenbroek }
427