2007-07-31 17:01:49 +02:00
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <sys/times.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <minix/u64.h>
|
|
|
|
#include <minix/config.h>
|
|
|
|
#include <minix/const.h>
|
2010-09-23 11:26:42 +02:00
|
|
|
#include <minix/minlib.h>
|
2007-07-31 17:01:49 +02:00
|
|
|
|
|
|
|
#include "sysutil.h"
|
|
|
|
|
2011-01-11 12:03:37 +01:00
|
|
|
#ifndef CONFIG_MAX_CPUS
|
|
|
|
#define CONFIG_MAX_CPUS 1
|
|
|
|
#endif
|
|
|
|
|
2007-07-31 17:01:49 +02:00
|
|
|
#define MICROHZ 1000000 /* number of micros per second */
|
2008-11-19 13:26:10 +01:00
|
|
|
#define MICROSPERTICK(h) (MICROHZ/(h)) /* number of micros per HZ tick */
|
2007-07-31 17:01:49 +02:00
|
|
|
|
2008-11-19 13:26:10 +01:00
|
|
|
#define CALIBRATE \
|
|
|
|
if(!calibrated) { \
|
|
|
|
int r; \
|
2010-01-14 16:24:16 +01:00
|
|
|
if((r=tsc_calibrate()) != OK) \
|
2010-03-05 16:05:11 +01:00
|
|
|
panic("calibrate failed: %d", r); \
|
2008-11-19 13:26:10 +01:00
|
|
|
}
|
|
|
|
|
2011-01-11 12:03:37 +01:00
|
|
|
static u32_t calib_mhz, Hz = 0;
|
2007-07-31 17:01:49 +02:00
|
|
|
static int calibrated = 0;
|
|
|
|
|
|
|
|
int
|
2010-01-14 16:24:16 +01:00
|
|
|
tsc_calibrate(void)
|
2007-07-31 17:01:49 +02:00
|
|
|
{
|
2011-01-11 12:03:37 +01:00
|
|
|
struct cpu_info cpu_info[CONFIG_MAX_CPUS];
|
2007-07-31 17:01:49 +02:00
|
|
|
|
2008-11-19 13:26:10 +01:00
|
|
|
/* Get HZ. */
|
2008-12-11 15:36:37 +01:00
|
|
|
Hz = sys_hz();
|
2008-11-19 13:26:10 +01:00
|
|
|
|
2011-01-11 12:03:37 +01:00
|
|
|
/* Obtain CPU frequency from kernel */
|
|
|
|
if (sys_getcpuinfo(&cpu_info)) {
|
|
|
|
printf("tsc_calibrate: cannot get cpu info\n");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* For now, use the frequency of the first CPU; everything here will
|
|
|
|
* break down in case we get scheduled on multiple CPUs with different
|
|
|
|
* frequencies regardless
|
2007-07-31 17:01:49 +02:00
|
|
|
*/
|
2011-01-11 12:03:37 +01:00
|
|
|
calib_mhz = cpu_info[0].freq;
|
2007-07-31 17:01:49 +02:00
|
|
|
calibrated = 1;
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
micro_delay(u32_t micros)
|
|
|
|
{
|
|
|
|
u64_t now, end;
|
|
|
|
|
|
|
|
/* Start of delay. */
|
|
|
|
read_tsc_64(&now);
|
|
|
|
|
2008-11-19 13:26:10 +01:00
|
|
|
CALIBRATE;
|
2007-07-31 17:01:49 +02:00
|
|
|
|
|
|
|
/* We have to know when to end the delay. */
|
2011-01-11 12:03:37 +01:00
|
|
|
end = add64(now, mul64u(micros, calib_mhz * 1000));
|
2007-07-31 17:01:49 +02:00
|
|
|
|
|
|
|
/* If we have to wait for at least one HZ tick, use the regular
|
|
|
|
* tickdelay first. Round downwards on purpose, so the average
|
|
|
|
* half-tick we wait short (depending on where in the current tick
|
|
|
|
* we call tickdelay). We can correct for both overhead of tickdelay
|
|
|
|
* itself and the short wait in the busywait later.
|
|
|
|
*/
|
2008-11-19 13:26:10 +01:00
|
|
|
if(micros >= MICROSPERTICK(Hz))
|
|
|
|
tickdelay(micros*Hz/MICROHZ);
|
2007-07-31 17:01:49 +02:00
|
|
|
|
|
|
|
/* Wait (the rest) of the delay time using busywait. */
|
|
|
|
while(cmp64(now, end) < 0)
|
|
|
|
read_tsc_64(&now);
|
|
|
|
|
|
|
|
return OK;
|
|
|
|
}
|
2008-11-19 13:26:10 +01:00
|
|
|
|
2010-01-14 16:24:16 +01:00
|
|
|
u32_t tsc_64_to_micros(u64_t tsc)
|
|
|
|
{
|
2010-09-23 11:26:42 +02:00
|
|
|
u64_t tmp;
|
2010-01-14 16:24:16 +01:00
|
|
|
|
2010-09-23 11:26:42 +02:00
|
|
|
CALIBRATE;
|
2010-01-14 16:24:16 +01:00
|
|
|
|
2011-01-11 12:03:37 +01:00
|
|
|
tmp = div64u64(tsc, calib_mhz);
|
|
|
|
if (ex64hi(tmp)) {
|
|
|
|
printf("tsc_64_to_micros: more than 2^32ms\n");
|
|
|
|
return ~0UL;
|
|
|
|
} else {
|
|
|
|
return ex64lo(tmp);
|
2010-01-14 16:24:16 +01:00
|
|
|
}
|
2010-09-23 11:26:42 +02:00
|
|
|
}
|
2010-01-14 16:24:16 +01:00
|
|
|
|
2010-09-23 11:26:42 +02:00
|
|
|
u32_t tsc_to_micros(u32_t low, u32_t high)
|
|
|
|
{
|
|
|
|
return tsc_64_to_micros(make64(low, high));
|
2010-01-14 16:24:16 +01:00
|
|
|
}
|
|
|
|
|
2010-05-03 21:41:04 +02:00
|
|
|
u32_t tsc_get_khz(void)
|
|
|
|
{
|
|
|
|
CALIBRATE;
|
|
|
|
|
2011-01-11 12:03:37 +01:00
|
|
|
return calib_mhz * 1000;
|
2010-05-03 21:41:04 +02:00
|
|
|
}
|