⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 rtc.c

📁 pebble
💻 C
字号:
/* 
 * Copyright 1999, 2000, 2001, 2002 Lucent Technologies Inc.
 * All Rights Reserved.
 * Information Sciences Research Center, Bell Labs.
 *
 * LUCENT TECHNOLOGIES DOES NOT CLAIM MERCHANTABILITY OF THIS SOFTWARE 
 * OR THE SUITABILITY OF THIS SOFTWARE FOR ANY PARTICULAR PURPOSE. The
 * software is provided "as is" without expressed or implied warranty 
 * of any kind.
 *
 * These notices must be retained in any copies of any part of this
 * software.
 *
 */

/*
 * device driver for RTC
 *
 * This code can be compiled either as a separate component, or 
 * inside the nucleus.
 * The symbolic variable NUCLEUS is defined when compiling it inside
 * the nucleus.
 * The symbolic variable RTC_INSIDE has the value 0 if the RTC driver
 * is a separate component, and 1 if it is included inside the
 * nucleus.
 *
 * Note that this source file is included in the nucleus even when the
 * RTC driver is a separate component.
 * In that case, the preprocessor ignores the body of this file.
 *
 * As a separate component, runs in user mode with interrupts enabled.
 * In the nucleus, runs in kernel mode with interrupts disabled.
 *
 * The only non-obvious part of this driver is the mechanism for
 * measuring the number of high-resolution clock ticks per second.
 * We enable the update cycle interrupt output from the RTC, and poll
 * the interrupt control unit until the RTC generates an interrupt.
 * Update cycle interrupts are generated every second.
 *
 * Why do we do it in this convoluted way?
 * Polling the update cycle flag from RTC register C (interrupts register)
 * or polling the RTC seconds register seem to show that the updates
 * are much more frequent than once per second!
 */
/*
	Note that on the Cobalt Qube, there is no external RTC
	that is independent of the CPU's clock cycle.
	Hence we just assume the CPU's clock speed to be correct
	and set ticks_per_sec appropriately (in measure_ticks()).
	sys_time returns hrtime()/ticks_per_sec
 */

#include "inside.h"
#if	!defined(NUCLEUS) || (defined(NUCLEUS) && (RTC_INSIDE == 1))

/* now we know that if NUCLEUS is defined, then RTC_INSIDE is also 1 */

#include "string.h"
#include "machine/cpu.h"
#include "pebble.h"
#include "mem.h"
#include "time.h"
#include "pebble-time.h"
#include "synch.h"

#ifdef	NUCLEUS
#include "nucleus.h"
#endif

/* the nucleus must call kprintf() and kpanic() and not printf(), panic() */
#ifdef  NUCLEUS
# define	PRINTF	kprintf
# define	PANIC	kpanic
#else
# define	PRINTF	printf
# define	PANIC	panic
#endif
#define BYTE_SHIFT(x) (x >> 3)
#define BYTE_MASK     0xFF


#define	N	4		/* number of measurements. must be >= 4 */

static int ticks_per_sec = 0;	/* high-resolution clock ticks per second */
#ifndef Cobalt
static int secs = 0;		/* seconds since boot time */
#endif

#if	defined(P4032)
# define ICU_DEV_RTC	INTR_DEV_RTC
# ifdef	NUCLEUS
#  define RTC_addr	((volatile uchar *) PA_TO_KVA1(RTC_BASE + RTC_ADDR))
#  define RTC_data	((volatile uchar *) PA_TO_KVA1(RTC_BASE + RTC_DATA))
#  define icu		((volatile p4032icu *) PA_TO_KVA1(ICU_BASE))
# else
#  define RTC_addr	((volatile uchar *) (RTC_BASE + RTC_ADDR + IO_BASE))
#  define RTC_data	((volatile uchar *) (RTC_BASE + RTC_DATA + IO_BASE))
#  define icu		((volatile p4032icu *) (ICU_BASE + IO_BASE))
#endif
#elif	defined(P5064)
# ifdef	NUCLEUS
#  define RTC_addr	((volatile uchar *) PA_TO_KVA1(ISAPORT_BASE(RTC_ADDR_PORT)))
#  define RTC_data	((volatile uchar *) PA_TO_KVA1(ISAPORT_BASE(RTC_DATA_PORT)))
#  define icu		((volatile p5064icu *) PA_TO_KVA1(ICU_BASE))
# else
#  define RTC_addr	((volatile uchar *) (ISAPORT_BASE(RTC_ADDR_PORT) + IO_BASE))
#  define RTC_data	((volatile uchar *) (ISAPORT_BASE(RTC_DATA_PORT) + IO_BASE))
#  define icu		((volatile p5064icu *) (ICU_BASE + IO_BASE))
#endif
#elif defined(Cobalt)
#else
#error	"invalid board type"
#endif


const char RTC_BUF_LEN[] = "buffer length must match RTC data size";
const char RTC_BATTERY[] = "real-time clock battery is missing";

#ifndef Cobalt
#ifndef	NUCLEUS
static int rtc_sem;	/* interrupts will post to this semaphore */
#endif
static uchar rtc_mode;	/* soft copy of RTC register B */
#endif

uchar
rtc_readb(uint reg)
{
#ifndef Cobalt
	*RTC_addr = reg;
	wbflush();
	return *RTC_data;
#else
	return 0;
#endif
}


void
rtc_writeb(uint reg, uchar val)
{
#ifndef Cobalt
	*RTC_addr = reg;
	*RTC_data = val;
	wbflush();
#endif
}


#ifndef Cobalt
/* wait for RTC to finish updates */
static void
rtc_wait_update(void)
{
#ifndef Cobalt
	while (rtc_readb(RTC_STATUSA) & RTCSA_UIP)
		;
#endif
}
#endif


/* buffer should be of length RTC_NTODREGS, but elements that have values of
   RTC_UNUSED are not stored in RTC
*/
int
sys_rtc_write(Thread *t, uchar *s, int len)
{
#ifndef Cobalt
	int i;

	param_check(s, len, 0);
	if (len < RTC_NTODREGS)
		error(RTC_BUF_LEN);

	/* must wait for lengthy updates, like changing mode to binary */
	rtc_wait_update();
	
	/* turn off updates */
	rtc_writeb(RTC_STATUSB, rtc_mode | RTCSB_UTI);
	
	/* send the buffer bytes to the rtc */
	/* don't load elements that have value of RTC_UNUSED */
	for (i = 0; i < RTC_NTODREGS; i++){
		if (s[i] != RTC_UNUSED)
			rtc_writeb(i, (uchar)s[i]);
	}

	/* turn on updates and update cycle interrupts */
	rtc_writeb(RTC_STATUSB, rtc_mode);

	return RTC_NTODREGS;
#else
	return -1;
#endif
}


/* read the values of RTC into buffer s */
int
sys_rtc_read(Thread *t, char *s, const int len)
{
#ifndef Cobalt
	int i;
	param_check(s, len, 0);

	/* check for valid battery */
	if ((rtc_readb(RTC_STATUSD) & RTCSD_VRT) == 0)
		error(RTC_BATTERY);

	/* wait for RTC to finish current update */
	rtc_wait_update();
	
	/* turn off updates */
	rtc_writeb(RTC_STATUSB, rtc_mode | RTCSB_UTI);
	
	/* read */
	for (i = 0; i < RTC_NTODREGS && i < len; i++){
		s[i] = rtc_readb(i);
	}

	/* restore updates */
	rtc_writeb(RTC_STATUSB, rtc_mode);

	return i;
#else
	return -1;
#endif
}

/* return ticks per second */
int 
sys_sec2ticks(Thread *t)
{
	return ticks_per_sec;
}


/* read a byte from nvram */
int
sys_nvram_get(Thread *t, int offset)
{
#ifndef Cobalt
	return rtc_readb(offset);
#else
	return -1;
#endif
}


/* set a byte in nvram */
int
sys_nvram_set(Thread *t, int offset, uint byte)
{
#ifndef Cobalt
	rtc_writeb(offset, byte);
	return rtc_readb(offset);
#else
	return -1;
#endif
}


#ifdef	NUCLEUS
/* interrupts handler for inside the nucleus */
int
rtc_handler(void)
{
#ifndef Cobalt
	/* clear RTC update cycle interrupt */
	rtc_readb(RTC_INTR);
	secs++;

	return 0;
#endif
}

#else

/* interrupts handler for a separate component */
void
rtc_handler(int x)
{
#ifndef Cobalt
	printf("rtc: interrupt handler active\n");	/* debug */
	while (1) {
		intr_enable(HW_INTR_RTC);
		if (sem_wait(rtc_sem) < 0)
			panic("sem_wait failed");
		/* clear RTC update cycle interrupt */
		rtc_readb(RTC_INTR);
		secs++;
	}
#endif
}
#endif


int
sys_time()
{
#ifndef Cobalt
	return secs;
#else
	return hrtime()/ticks_per_sec;
#endif
}


#ifndef Cobalt
/*
 * Time comparison routine for qsort.
 */
static int
compare(const void *a, const void *b)
{
	if (*((const Time *)a) > *((const Time *)b))
		return 1;
	else if (*((const Time *)a) < *((const Time *)b))
		return -1;
	else
		return 0;
}
#endif


/*
 *	Measure the number of high-resolution clock ticks per second.
 *	Do N measurements, sort them, and take the average of the middle
 *	N-2 measurements.
 *
 *	This routine should be called BEFORE the RTC interrupts are
 *	enabled to avoid race with the interrupt dispatcher
 *	(on the RTC interrupt).
 */
static void
measure_ticks(void)
{
#ifndef Cobalt
	int i;
	Time old_ticks, new_ticks, sum;
	Time elapsed[N];
	
	PRINTF("measure ticks per second:\n");
	/* wait for RTC to finish current update, */
	/* which is caused by changing mode to binary */
	rtc_wait_update();

	old_ticks = hrtime();
	for (i = 0; i < N; i++) {
		/* clear RTC update interrupt */
		rtc_readb(RTC_INTR);

		/* wait for a second (until next update cycle interrupt) */
		while ((icu->irr.dev & ICU_DEV_RTC) == 0)
			;

		new_ticks = hrtime();
		elapsed[i] = new_ticks - old_ticks;
		old_ticks = new_ticks;
	}

	qsort(elapsed, N, sizeof(Time), compare);

	PRINTF("elapsed ticks:");
	for (i = 0; i < N; i++)
		PRINTF(" %d", (int)elapsed[i]);
	PRINTF("\n");

	sum = 0;
	for (i = 1; i < N-1; i++)
		sum += elapsed[i];
	ticks_per_sec = sum / (N-2);
#else
	ticks_per_sec = 125*1000000;
#endif

	PRINTF("ticks per second=%d\n", (int)ticks_per_sec);
}


#ifdef	NUCLEUS
void
inside_rtc_init(void)
{
	kprintf("in-nucleus RTC driver\n");

	/* verify that we are running in kernel mode with interrupts disabled*/ 
        if (!check_psw(0,0)) { 
		kprintf("rtc: invalid processor status: %08lx\n", get_psw()); 
		task_exit(1); 
        } 

#else	/* !NUCLEUS */

/* rtc initialization thread */
int main()
{
	printf("RTC driver is active\n");
	
	/* verify that we are running in user mode with interrupts enabled */ 
        if (!check_psw(1,1)) { 
		printf("rtc: invalid processor status: %08lx\n", get_psw()); 
		task_exit(1); 
        } 
#endif

	/* set clock to 24hr mode, binary mode, update cycle interrupts (once per second) */
#ifndef Cobalt
	rtc_mode = (rtc_readb(RTC_STATUSB) & RTCSB_SQWE) | RTCSB_BINARY | RTCSB_24HR | RTCSB_UIE;
	rtc_writeb(RTC_STATUSB, rtc_mode);
#endif

	measure_ticks();

	if (portal_create_pair("rtc", sys_rtc_read, sys_rtc_write) < 0)
		PANIC("portal_create_pair for rtc_read() failed\n");
	
	if (portal_create(SYS_SEC2TICKS, "smtiii", 0, sys_sec2ticks, 0) < 0)
		PANIC("portal_create for sec2ticks() failed\n");

	if (portal_create(SYS_NVRAM_GET, "smtiii", 0, sys_nvram_get, 0) < 0)
		PANIC("portal_create for nvram_get() failed\n");

	if (portal_create(SYS_NVRAM_SET, "smtiii", 0, sys_nvram_set, 0) < 0)
		PANIC("portal_create for nvram_set() failed\n");

	if (portal_create(SYS_TIME, "smtiii", 0, sys_time, 0) < 0)
		PANIC("portal_create for time() failed\n");

#ifndef Cobalt
#ifdef	NUCLEUS
	if (intr_direct_call(HW_INTR_RTC, rtc_handler) < 0)
		kpanic("intr_callback failed:");
#else
	if ((rtc_sem = intr_define(HW_INTR_RTC)) < 0)
		panic("intr_define failed:");

	if (thr_spawn(rtc_handler, 0) < 0)
		panic("failed to spawn RTC interrupt handler:");
#endif
#endif

#ifndef NUCLEUS

	/*
	 * return to initialization code.
	 * cannot just "return", since the startup code (crt0.S) calls
	 * exit when main routine terminates.
	 */
	call_portal(SYS_RTN_RPC);
	return(1);
#endif
}

#endif	/* generate code for a separate component or inside the nucleus */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -