summaryrefslogblamecommitdiffstats
path: root/bsd_eth_drivers/libbsdport/rtems_udelay.c
blob: d8f80f4f7521a550a32775e52c769a7996e2b704 (plain) (tree)































































































































































































                                                                                                        
#include <rtems.h>
#include <rtems/error.h>
#include <bsp.h>

#include "rtems_udelay.h"

#if defined(__PPC__)

#include <rtems/powerpc/registers.h>

/* Ouch. stupid bookE doesn't implement mftb so we must
 * use mfspr (which wouldn't work on classic ppc if we
 * were in user mode but luckily we're not.
 */
static inline uint64_t __read_hires_clicks()
{
uint32_t tbl, tbu1, tbu2;
	asm volatile(
		"	mfspr %0, %4	\n"
		"	mfspr %1, %3	\n"
		"	mfspr %2, %4	\n"
		:"=r"(tbu1),"=r"(tbl),"=r"(tbu2)
		:"i"(TBRL), "i"(TBRU)
		);
	if ( tbu1 != tbu2 )
		asm volatile("mfspr %0, %1":"=r"(tbl):"i"(TBRL));
	return ((uint64_t)tbu2<<32) | tbl;
}

#define __rtems_hires_kHz (BSP_bus_frequency/BSP_time_base_divisor)

#elif defined(__i386__)

static inline uint64_t __read_hires_clicks()
{
uint32_t lo,hi;
	asm volatile("rdtsc":"=a"(lo),"=d"(hi));
	return ( (uint64_t)hi << 32 ) | lo;
}

#else
#error "rtems_udelay.c not ported to this CPU yet"
#endif

#ifndef __rtems_hires_kHz
/* Clock frequency of high-resolution timer */
uint32_t __rtems_hires_kHz = 0;
uint32_t rtems_udelay_calibrate();
#endif


void rtems_usec_delay(uint32_t usecs)
{
uint64_t clicks = __read_hires_clicks();
int      ticks;
	if (usecs > 10) {
		if ( _ISR_Is_in_progress() ) {
			rtems_panic("rtems_usec_delay for more than 10us in ISR!!");
		}
		if ( _ISR_Get_level() > 0 ) {
			rtems_panic("rtems_usec_delay for more than 10us with IRQs disabled!!");
		}
	}
	ticks   = usecs/rtems_configuration_get_microseconds_per_tick();
#ifndef __rtems_hires_kHz
	/* If it's not a macro; do lazy init */
	if ( 0 == __rtems_hires_kHz ) {
		uint64_t clicks = rtems_udelay_calibrate();
		__rtems_hires_kHz = (clicks * 1000) / rtems_configuration_get_microseconds_per_tick();
		ticks--;
	}
#endif
	clicks += (usecs * __rtems_hires_kHz)/1000;
	if ( ticks > 0 )
		rtems_task_wake_after(ticks);

	while ( clicks > __read_hires_clicks() )
		/* busy wait */;
}

/* This doesn't belong here; also, the RTEMS timeout() implementation is buggy:
 * if a timeout is added when the networking task is asleep then I believe 'timeout()'
 * is unable to schedule a wakeup. Therefore, I implemented the 'callout' facility.
 */
#ifdef UNTESTED
/* Must be executed with the network semaphore held */
void
rtems_bsdnet_untimeout(timeout_func_t fn, void *arg)
{
register struct callout *l, *p;

	for ( l = &calltodo; (p=l->c_next); l=p ) {
		if ( p->c_func == fn && p->c_arg == arg ) {
			register struct callout *n
			/* found it */
			if ( (n = p->c_next) && p->c_time > 0 ) {
				/* adjust time of following entry */
				n->c_time += p->c_time;
			}
			/* extract */
			l->c_next = n;
			/* return to extract first occurrence; continue
			 * to extract all
			 */
#if 0
			return;
#else
			p = l;
#endif
		}
	}
}
#endif

struct caldat {
	uint64_t t0,t1,t2,t3;
	rtems_id thetid;
};

#ifndef __rtems_hires_kHz
static void
tickmeas1(rtems_id myself, void *arg)
{
struct caldat *p = arg;
	p->t2 = __read_hires_clicks();
	rtems_event_send(p->thetid, RTEMS_EVENT_0);
}

static void
tickmeas0(rtems_id myself, void *arg)
{
struct caldat *p = arg;
	p->t1 = __read_hires_clicks();
	rtems_timer_fire_after( myself, 1, tickmeas1, arg );
}

/* Calibrate high-resolution timer */
uint32_t
rtems_udelay_calibrate()
{
rtems_id          timer;
rtems_status_code sc;
rtems_event_set   ev;
struct caldat     d;

	/* measure a clock tick with the hires timer;
	 * note that we can't just sleep for 1 tick because
	 * that results in sleeping for an unknown fraction
	 * of a tick...
	 */
	sc = rtems_timer_create( rtems_build_name('h','r','e','s'), &timer );
	if ( RTEMS_SUCCESSFUL != sc ) {
		rtems_panic("Unable to create timer:%i\n", sc);
	}
	sc = rtems_task_ident(RTEMS_SELF, RTEMS_LOCAL, &d.thetid);
	if ( RTEMS_SUCCESSFUL != sc ) {
		rtems_panic("Unable to read my own TID:%i\n", sc);
	}
#ifdef DEBUG
	d.t0 = __read_hires_clicks();
#endif
	sc = rtems_timer_fire_after(timer, 1, tickmeas0, &d);
	if ( RTEMS_SUCCESSFUL != sc ) {
		rtems_panic("Unable to fire timer:%i\n", sc);
	}
	sc = rtems_event_receive( RTEMS_EVENT_0, RTEMS_EVENT_ANY | RTEMS_WAIT , RTEMS_NO_TIMEOUT, &ev); 
#ifdef DEBUG
	d.t3 = __read_hires_clicks();
#endif
	if ( RTEMS_SUCCESSFUL != sc ) {
		rtems_panic("Unable to synchronize with timer:%i\n", sc);
	}
	rtems_timer_delete(timer);
#ifdef DEBUG
	printf("Diffs: %llu %llu %llu\n",
	d.t3-d.t2, d.t2-d.t1, d.t1-d.t0);
#endif
	return d.t2-d.t1;
}
#endif

#ifdef DEBUG
unsigned
hdiff(unsigned s)
{
uint64_t now = __read_hires_clicks();
	rtems_task_wake_after(s);
	now = __read_hires_clicks() - now;
	printf("Diff was %llu clicks\n",now);
	return (unsigned)now;
}
#endif