From 03446f5554aa2720c0175fa8d32b9bba34b869e9 Mon Sep 17 00:00:00 2001 From: Ben Gras Date: Tue, 31 Jul 2007 15:01:49 +0000 Subject: [PATCH] micro_delay in sysutil, used in ti1225, dp8390, fxp and orinoco now. Uses a combination of tickdelay (where possible) and calibrated busywait (where necessary). --- drivers/dp8390/rtl8029.c | 8 ---- drivers/fxp/fxp.c | 12 ++---- drivers/orinoco/hermes.c | 18 -------- drivers/ti1225/ti1225.c | 10 ++--- drivers/tty/keyboard.c | 6 +-- include/minix/sysutil.h | 10 +++-- lib/sysutil/Makefile.in | 1 + lib/sysutil/micro_delay.c | 89 +++++++++++++++++++++++++++++++++++++++ 8 files changed, 105 insertions(+), 49 deletions(-) create mode 100644 lib/sysutil/micro_delay.c diff --git a/drivers/dp8390/rtl8029.c b/drivers/dp8390/rtl8029.c index 189e2e30c..d2c7c337f 100644 --- a/drivers/dp8390/rtl8029.c +++ b/drivers/dp8390/rtl8029.c @@ -22,8 +22,6 @@ Created: April 2000 by Philip Homburg #if ENABLE_PCI -#define MICROS_TO_TICKS(m) (((m)*HZ/1000000)+1) - PRIVATE struct pcitab { u16_t vid; @@ -43,7 +41,6 @@ _PROTOTYPE( static void ee_wen, (dpeth_t *dep) ); _PROTOTYPE( static void set_ee_word, (dpeth_t *dep, int a, U16_t w) ); _PROTOTYPE( static void ee_wds, (dpeth_t *dep) ); #endif -_PROTOTYPE( static void micro_delay, (unsigned long usecs) ); PUBLIC int rtl_probe(dep) struct dpeth *dep; @@ -373,11 +370,6 @@ dpeth_t *dep; } #endif -static void micro_delay(unsigned long usecs) -{ - tickdelay(MICROS_TO_TICKS(usecs)); -} - #endif /* ENABLE_PCI */ /* diff --git a/drivers/fxp/fxp.c b/drivers/fxp/fxp.c index 437e39ce4..117303a71 100644 --- a/drivers/fxp/fxp.c +++ b/drivers/fxp/fxp.c @@ -112,13 +112,9 @@ typedef int irq_hook_t; #define structof(type, field, ptr) \ ((type *) (((char *) (ptr)) - offsetof(type, field))) -#define MICROS_TO_TICKS(m) (((m)*HZ/1000000)+1) - static timer_t *fxp_timers= NULL; static clock_t fxp_next_timeout= 0; -static void micro_delay(unsigned long usecs); - /* ignore interrupt for the moment */ #define interrupt(x) 0 @@ -295,6 +291,9 @@ int main(int argc, char *argv[]) fxp_init_buf(fp); #endif + if((r=micro_delay_calibrate()) != OK) + panic("FXP","rmicro_delay_calibrate failed", r); + /* Try to notify inet that we are present (again) */ r= ds_retrieve_u32("inet", &tasknr); if (r == OK) @@ -2875,11 +2874,6 @@ PRIVATE void fxp_expire_timers() } } -static void micro_delay(unsigned long usecs) -{ - tickdelay(MICROS_TO_TICKS(usecs)); -} - static u8_t do_inb(port_t port) { int r; diff --git a/drivers/orinoco/hermes.c b/drivers/orinoco/hermes.c index 4f904e252..640edf01a 100755 --- a/drivers/orinoco/hermes.c +++ b/drivers/orinoco/hermes.c @@ -46,24 +46,6 @@ int this_proc; #define MICROS_TO_TICKS(m) (((m)*HZ/1000000)+1) -/***************************************************************************** - * milli_delay * - * * - * Wait usecs micro seconds. Clearly needs revision * - *****************************************************************************/ -static void micro_delay(unsigned long usecs) -{ - int i, j; - - if(usecs >= 100) { - /* If the delay is long, we might as well use ticks */ - tickdelay(MICROS_TO_TICKS(usecs)); - } else { - /* use another type of hack :-), or a proper implementation */ - for(i=0; i < 1000 * usecs; i++){j+=1;} - } -} - /***************************************************************************** * milli_delay * * * diff --git a/drivers/ti1225/ti1225.c b/drivers/ti1225/ti1225.c index 225169c95..b330b46fe 100644 --- a/drivers/ti1225/ti1225.c +++ b/drivers/ti1225/ti1225.c @@ -14,8 +14,6 @@ Created: Dec 2005 by Philip Homburg /* The use of interrupts is not yet ready for prime time */ #define USE_INTS 0 -#define MICROS_TO_TICKS(m) (((m)*HZ/1000000)+1) - #define NR_PORTS 2 PRIVATE struct port @@ -59,7 +57,6 @@ FORWARD _PROTOTYPE( void do_int, (struct port *pp) ); FORWARD _PROTOTYPE( u8_t read_exca, (struct port *pp, int socket, int reg) ); FORWARD _PROTOTYPE( void do_outb, (port_t port, u8_t value) ); FORWARD _PROTOTYPE( u8_t do_inb, (port_t port) ); -FORWARD _PROTOTYPE( void micro_delay, (unsigned long usecs) ); int main(int argc, char *argv[]) { @@ -68,6 +65,9 @@ int main(int argc, char *argv[]) (progname=strrchr(argv[0],'/')) ? progname++ : (progname=argv[0]); + if((r=micro_delay_calibrate()) != OK) + panic("ti1225", "micro_delay_calibrate failed", r); + debug= 0; while (c= getopt(argc, argv, "d?"), c != -1) { @@ -492,8 +492,4 @@ PRIVATE void do_outb(port_t port, u8_t value) panic("ti1225","sys_outb failed", r); } -PRIVATE void micro_delay(unsigned long usecs) -{ - tickdelay(MICROS_TO_TICKS(usecs)); -} diff --git a/drivers/tty/keyboard.c b/drivers/tty/keyboard.c index 34331fed0..3325cd951 100644 --- a/drivers/tty/keyboard.c +++ b/drivers/tty/keyboard.c @@ -65,8 +65,6 @@ int aux_irq_hook_id = -1; * keyboard. */ -#define MICROS_TO_TICKS(m) (((m)*HZ/1000000)+1) - #define CONSOLE 0 /* line number for console */ #define KB_IN_BYTES 32 /* size of keyboard input buffer */ PRIVATE char ibuf[KB_IN_BYTES]; /* input buffer */ @@ -153,7 +151,6 @@ FORWARD _PROTOTYPE( void set_leds, (void) ); FORWARD _PROTOTYPE( void show_key_mappings, (void) ); FORWARD _PROTOTYPE( int kb_read, (struct tty *tp, int try) ); FORWARD _PROTOTYPE( unsigned map_key, (int scode) ); -FORWARD _PROTOTYPE( void micro_delay, (unsigned long usecs) ); FORWARD _PROTOTYPE( void kbd_watchdog, (timer_t *tmrp) ); /*===========================================================================* @@ -1263,9 +1260,10 @@ int *isauxp; #endif } -static void micro_delay(unsigned long usecs) +int micro_delay(u32_t usecs) { tickdelay(MICROS_TO_TICKS(usecs)); + return OK; } /*===========================================================================* diff --git a/include/minix/sysutil.h b/include/minix/sysutil.h index 145b1c6d8..ad4dac0d7 100644 --- a/include/minix/sysutil.h +++ b/include/minix/sysutil.h @@ -1,5 +1,5 @@ -#ifndef _EXTRALIB_H -#define _EXTRALIB_H +#ifndef _MINIX_SYSUTIL_H +#define _MINIX_SYSUTIL_H 1 /* Extra system library definitions to support device drivers and servers. * @@ -46,6 +46,10 @@ _PROTOTYPE( void report, (char *who, char *mess, int num)); _PROTOTYPE( void panic, (char *who, char *mess, int num)); _PROTOTYPE( int getuptime, (clock_t *ticks)); _PROTOTYPE( int tickdelay, (clock_t ticks)); +_PROTOTYPE( int micro_delay_calibrate, (void)); +_PROTOTYPE( int micro_delay, (u32_t micros)); -#endif /* _EXTRALIB_H */ +#define MICROS_TO_TICKS(m) (((m)*HZ/1000000)+1) + +#endif /* _MINIX_SYSUTIL_H */ diff --git a/lib/sysutil/Makefile.in b/lib/sysutil/Makefile.in index 0443579d4..79a646740 100644 --- a/lib/sysutil/Makefile.in +++ b/lib/sysutil/Makefile.in @@ -15,6 +15,7 @@ libsysutil_FILES=" \ env_panic.c \ env_prefix.c \ fkey_ctl.c \ + micro_delay.c \ report.c \ taskcall.c \ read_tsc.s \ diff --git a/lib/sysutil/micro_delay.c b/lib/sysutil/micro_delay.c new file mode 100644 index 000000000..9789bfd81 --- /dev/null +++ b/lib/sysutil/micro_delay.c @@ -0,0 +1,89 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "sysutil.h" + +#define CALIBRATE_TICKS (HZ/5) +#define MICROHZ 1000000 /* number of micros per second */ +#define MICROSPERTICK (MICROHZ/HZ) /* number of micros per HZ tick */ + +static u32_t calib_tsc; +static int calibrated = 0; + +int +micro_delay_calibrate(void) +{ + u64_t start, end, diff, hz; + struct tms tms; + unsigned long t = 0; + + /* Wait for clock to tick. */ + while(!t || (t == times(&tms))) + t = times(&tms); + + t++; + + /* Wait for clock to tick CALIBRATE_TICKS times, and time + * this using the TSC. + */ + read_tsc_64(&start); + while(times(&tms) < t+CALIBRATE_TICKS) ; + read_tsc_64(&end); + + diff = sub64(end, start); + if(ex64hi(diff) != 0) + panic(__FILE__, + "micro_delay_calibrate: CALIBRATE_TICKS too high " + "for TSC frequency\n", NO_NUM); + calib_tsc = ex64lo(diff); + printf("micro_delay_calibrate: " + "%lu cycles/%d ticks of %d Hz; %lu cycles/s\n", + calib_tsc, CALIBRATE_TICKS, HZ, + div64u(mul64u(calib_tsc, HZ), CALIBRATE_TICKS)); + calibrated = 1; + + return OK; +} + +int +micro_delay(u32_t micros) +{ + u64_t now, end; + + /* Start of delay. */ + read_tsc_64(&now); + + /* We have to be calibrated. */ + if(!calibrated) { + int r; + printf("micro_delay: calibrating\n"); + if((r=micro_delay_calibrate()) != OK) + panic(__FILE__, "micro_delay: calibrate failed\n", r); + } + + /* We have to know when to end the delay. */ + end = add64u(now, div64u(mul64u(calib_tsc, + micros * HZ / CALIBRATE_TICKS), MICROHZ)); + + /* 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. + */ + if(micros >= MICROSPERTICK) + tickdelay(micros*HZ/MICROHZ); + + /* Wait (the rest) of the delay time using busywait. */ + while(cmp64(now, end) < 0) + read_tsc_64(&now); + + return OK; +}