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

📄 time.c

📁 linux-2.4.29操作系统的源码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* $Id: time.c,v 1.41.2.2 2002/03/03 04:08:10 davem Exp $ * time.c: UltraSparc timer and TOD clock support. * * Copyright (C) 1997 David S. Miller (davem@caip.rutgers.edu) * Copyright (C) 1998 Eddie C. Dost   (ecd@skynet.be) * * Based largely on code which is: * * Copyright (C) 1996 Thomas K. Dyas (tdyas@eden.rutgers.edu) */#include <linux/config.h>#include <linux/errno.h>#include <linux/sched.h>#include <linux/kernel.h>#include <linux/param.h>#include <linux/string.h>#include <linux/mm.h>#include <linux/interrupt.h>#include <linux/timex.h>#include <linux/init.h>#include <linux/ioport.h>#include <linux/mc146818rtc.h>#include <linux/delay.h>#include <linux/timer.h>#include <asm/oplib.h>#include <asm/mostek.h>#include <asm/timer.h>#include <asm/irq.h>#include <asm/io.h>#include <asm/sbus.h>#include <asm/fhc.h>#include <asm/pbm.h>#include <asm/ebus.h>#include <asm/isa.h>#include <asm/starfire.h>#include <asm/smp.h>extern rwlock_t xtime_lock;extern unsigned long wall_jiffies;spinlock_t mostek_lock = SPIN_LOCK_UNLOCKED;spinlock_t rtc_lock = SPIN_LOCK_UNLOCKED;unsigned long mstk48t02_regs = 0UL;#ifdef CONFIG_PCIunsigned long ds1287_regs = 0UL;#endifstatic unsigned long mstk48t08_regs = 0UL;static unsigned long mstk48t59_regs = 0UL;static int set_rtc_mmss(unsigned long);struct sparc64_tick_ops *tick_ops;#define TICK_PRIV_BIT	(1UL << 63)static void tick_disable_protection(void){	/* Set things up so user can access tick register for profiling	 * purposes.  Also workaround BB_ERRATA_1 by doing a dummy	 * read back of %tick after writing it.	 */	__asm__ __volatile__(	"	ba,pt	%%xcc, 1f\n"	"	 nop\n"	"	.align	64\n"	"1:	rd	%%tick, %%g2\n"	"	add	%%g2, 6, %%g2\n"	"	andn	%%g2, %0, %%g2\n"	"	wrpr	%%g2, 0, %%tick\n"	"	rdpr	%%tick, %%g0"	: /* no outputs */	: "r" (TICK_PRIV_BIT)	: "g2");}static void tick_init_tick(unsigned long offset){	tick_disable_protection();	__asm__ __volatile__(	"	rd	%%tick, %%g1\n"	"	andn	%%g1, %1, %%g1\n"	"	ba,pt	%%xcc, 1f\n"	"	 add	%%g1, %0, %%g1\n"	"	.align	64\n"	"1:	wr	%%g1, 0x0, %%tick_cmpr\n"	"	rd	%%tick_cmpr, %%g0"	: /* no outputs */	: "r" (offset), "r" (TICK_PRIV_BIT)	: "g1");}static unsigned long tick_get_tick(void){	unsigned long ret;	__asm__ __volatile__("rd	%%tick, %0\n\t"			     "mov	%0, %0"			     : "=r" (ret));	return ret & ~TICK_PRIV_BIT;}static unsigned long tick_get_compare(void){	unsigned long ret;	__asm__ __volatile__("rd	%%tick_cmpr, %0\n\t"			     "mov	%0, %0"			     : "=r" (ret));	return ret;}static unsigned long tick_add_compare(unsigned long adj){	unsigned long new_compare;	/* Workaround for Spitfire Errata (#54 I think??), I discovered	 * this via Sun BugID 4008234, mentioned in Solaris-2.5.1 patch	 * number 103640.	 *	 * On Blackbird writes to %tick_cmpr can fail, the	 * workaround seems to be to execute the wr instruction	 * at the start of an I-cache line, and perform a dummy	 * read back from %tick_cmpr right after writing to it. -DaveM	 */	__asm__ __volatile__("rd	%%tick_cmpr, %0\n\t"			     "ba,pt	%%xcc, 1f\n\t"			     " add	%0, %1, %0\n\t"			     ".align	64\n"			     "1:\n\t"			     "wr	%0, 0, %%tick_cmpr\n\t"			     "rd	%%tick_cmpr, %%g0"			     : "=&r" (new_compare)			     : "r" (adj));	return new_compare;}static unsigned long tick_add_tick(unsigned long adj, unsigned long offset){	unsigned long new_tick, tmp;	/* Also need to handle Blackbird bug here too. */	__asm__ __volatile__("rd	%%tick, %0\n\t"			     "add	%0, %2, %0\n\t"			     "wrpr	%0, 0, %%tick\n\t"			     "andn	%0, %4, %1\n\t"			     "ba,pt	%%xcc, 1f\n\t"			     " add	%1, %3, %1\n\t"			     ".align	64\n"			     "1:\n\t"			     "wr	%1, 0, %%tick_cmpr\n\t"			     "rd	%%tick_cmpr, %%g0"			     : "=&r" (new_tick), "=&r" (tmp)			     : "r" (adj), "r" (offset), "r" (TICK_PRIV_BIT));	return new_tick;}static struct sparc64_tick_ops tick_operations = {	.init_tick	=	tick_init_tick,	.get_tick	=	tick_get_tick,	.get_compare	=	tick_get_compare,	.add_tick	=	tick_add_tick,	.add_compare	=	tick_add_compare,	.softint_mask	=	1UL << 0,};static void stick_init_tick(unsigned long offset){	tick_disable_protection();	/* Let the user get at STICK too. */	__asm__ __volatile__(	"	rd	%%asr24, %%g2\n"	"	andn	%%g2, %0, %%g2\n"	"	wr	%%g2, 0, %%asr24"	: /* no outputs */	: "r" (TICK_PRIV_BIT)	: "g1", "g2");	__asm__ __volatile__(	"	rd	%%asr24, %%g1\n"	"	andn	%%g1, %1, %%g1\n"	"	add	%%g1, %0, %%g1\n"	"	wr	%%g1, 0x0, %%asr25"	: /* no outputs */	: "r" (offset), "r" (TICK_PRIV_BIT)	: "g1");}static unsigned long stick_get_tick(void){	unsigned long ret;	__asm__ __volatile__("rd	%%asr24, %0"			     : "=r" (ret));	return ret & ~TICK_PRIV_BIT;}static unsigned long stick_get_compare(void){	unsigned long ret;	__asm__ __volatile__("rd	%%asr25, %0"			     : "=r" (ret));	return ret;}static unsigned long stick_add_tick(unsigned long adj, unsigned long offset){	unsigned long new_tick, tmp;	__asm__ __volatile__("rd	%%asr24, %0\n\t"			     "add	%0, %2, %0\n\t"			     "wr	%0, 0, %%asr24\n\t"			     "andn	%0, %4, %1\n\t"			     "add	%1, %3, %1\n\t"			     "wr	%1, 0, %%asr25"			     : "=&r" (new_tick), "=&r" (tmp)			     : "r" (adj), "r" (offset), "r" (TICK_PRIV_BIT));	return new_tick;}static unsigned long stick_add_compare(unsigned long adj){	unsigned long new_compare;	__asm__ __volatile__("rd	%%asr25, %0\n\t"			     "add	%0, %1, %0\n\t"			     "wr	%0, 0, %%asr25"			     : "=&r" (new_compare)			     : "r" (adj));	return new_compare;}static struct sparc64_tick_ops stick_operations = {	.init_tick	=	stick_init_tick,	.get_tick	=	stick_get_tick,	.get_compare	=	stick_get_compare,	.add_tick	=	stick_add_tick,	.add_compare	=	stick_add_compare,	.softint_mask	=	1UL << 16,};/* On Hummingbird the STICK/STICK_CMPR register is implemented * in I/O space.  There are two 64-bit registers each, the * first holds the low 32-bits of the value and the second holds * the high 32-bits. * * Since STICK is constantly updating, we have to access it carefully. * * The sequence we use to read is: * 1) read low * 2) read high * 3) read low again, if it rolled over increment high by 1 * * Writing STICK safely is also tricky: * 1) write low to zero * 2) write high * 3) write low */#define HBIRD_STICKCMP_ADDR	0x1fe0000f060UL#define HBIRD_STICK_ADDR	0x1fe0000f070ULstatic unsigned long __hbird_read_stick(void){	unsigned long ret, tmp1, tmp2, tmp3;	unsigned long addr = HBIRD_STICK_ADDR;	__asm__ __volatile__("ldxa	[%1] %5, %2\n\t"			     "add	%1, 0x8, %1\n\t"			     "ldxa	[%1] %5, %3\n\t"			     "sub	%1, 0x8, %1\n\t"			     "ldxa	[%1] %5, %4\n\t"			     "cmp	%4, %2\n\t"			     "blu,a,pn	%%xcc, 1f\n\t"			     " add	%3, 1, %3\n"			     "1:\n\t"			     "sllx	%3, 32, %3\n\t"			     "or	%3, %4, %0\n\t"			     : "=&r" (ret), "=&r" (addr),			       "=&r" (tmp1), "=&r" (tmp2), "=&r" (tmp3)			     : "i" (ASI_PHYS_BYPASS_EC_E), "1" (addr));	return ret;}static unsigned long __hbird_read_compare(void){	unsigned long low, high;	unsigned long addr = HBIRD_STICKCMP_ADDR;	__asm__ __volatile__("ldxa	[%2] %3, %0\n\t"			     "add	%2, 0x8, %2\n\t"			     "ldxa	[%2] %3, %1"			     : "=&r" (low), "=&r" (high), "=&r" (addr)			     : "i" (ASI_PHYS_BYPASS_EC_E), "2" (addr));	return (high << 32UL) | low;}static void __hbird_write_stick(unsigned long val){	unsigned long low = (val & 0xffffffffUL);	unsigned long high = (val >> 32UL);	unsigned long addr = HBIRD_STICK_ADDR;	__asm__ __volatile__("stxa	%%g0, [%0] %4\n\t"			     "add	%0, 0x8, %0\n\t"			     "stxa	%3, [%0] %4\n\t"			     "sub	%0, 0x8, %0\n\t"			     "stxa	%2, [%0] %4"			     : "=&r" (addr)			     : "0" (addr), "r" (low), "r" (high),			       "i" (ASI_PHYS_BYPASS_EC_E));}static void __hbird_write_compare(unsigned long val){	unsigned long low = (val & 0xffffffffUL);	unsigned long high = (val >> 32UL);	unsigned long addr = HBIRD_STICKCMP_ADDR + 0x8UL;	__asm__ __volatile__("stxa	%3, [%0] %4\n\t"			     "sub	%0, 0x8, %0\n\t"			     "stxa	%2, [%0] %4"			     : "=&r" (addr)			     : "0" (addr), "r" (low), "r" (high),			       "i" (ASI_PHYS_BYPASS_EC_E));}static void hbtick_init_tick(unsigned long offset){	unsigned long val;	tick_disable_protection();	/* XXX This seems to be necessary to 'jumpstart' Hummingbird	 * XXX into actually sending STICK interrupts.  I think because	 * XXX of how we store %tick_cmpr in head.S this somehow resets the	 * XXX {TICK + STICK} interrupt mux.  -DaveM	 */	__hbird_write_stick(__hbird_read_stick());	val = __hbird_read_stick() & ~TICK_PRIV_BIT;	__hbird_write_compare(val + offset);}static unsigned long hbtick_get_tick(void){	return __hbird_read_stick() & ~TICK_PRIV_BIT;}static unsigned long hbtick_get_compare(void){	return __hbird_read_compare();}static unsigned long hbtick_add_tick(unsigned long adj, unsigned long offset){	unsigned long val;	val = __hbird_read_stick() + adj;	__hbird_write_stick(val);	val &= ~TICK_PRIV_BIT;	__hbird_write_compare(val + offset);	return val;}static unsigned long hbtick_add_compare(unsigned long adj){	unsigned long val = __hbird_read_compare() + adj;	val &= ~TICK_PRIV_BIT;	__hbird_write_compare(val);	return val;}static struct sparc64_tick_ops hbtick_operations = {	.init_tick	=	hbtick_init_tick,	.get_tick	=	hbtick_get_tick,	.get_compare	=	hbtick_get_compare,	.add_tick	=	hbtick_add_tick,	.add_compare	=	hbtick_add_compare,	.softint_mask	=	1UL << 0,};/* timer_interrupt() needs to keep up the real-time clock, * as well as call the "do_timer()" routine every clocktick * * NOTE: On SUN5 systems the ticker interrupt comes in using 2 *       interrupts, one at level14 and one with softint bit 0. */unsigned long timer_tick_offset;unsigned long timer_tick_compare;unsigned long timer_ticks_per_usec_quotient;static __inline__ void timer_check_rtc(void){	/* last time the cmos clock got updated */	static long last_rtc_update;	/* Determine when to update the Mostek clock. */	if ((time_status & STA_UNSYNC) == 0 &&	    xtime.tv_sec > last_rtc_update + 660 &&	    xtime.tv_usec >= 500000 - ((unsigned) tick) / 2 &&	    xtime.tv_usec <= 500000 + ((unsigned) tick) / 2) {		if (set_rtc_mmss(xtime.tv_sec) == 0)			last_rtc_update = xtime.tv_sec;		else			last_rtc_update = xtime.tv_sec - 600;			/* do it again in 60 s */	}}void sparc64_do_profile(unsigned long pc, unsigned long o7){	if (prof_buffer && current->pid) {		extern int _stext;		extern int rwlock_impl_begin, rwlock_impl_end;		extern int atomic_impl_begin, atomic_impl_end;		extern int __bitops_begin, __bitops_end;		if ((pc >= (unsigned long) &atomic_impl_begin &&		     pc < (unsigned long) &atomic_impl_end) ||		    (pc >= (unsigned long) &rwlock_impl_begin &&		     pc < (unsigned long) &rwlock_impl_end) ||		    (pc >= (unsigned long) &__bitops_begin &&		     pc < (unsigned long) &__bitops_end))			pc = o7;		pc -= (unsigned long) &_stext;		pc >>= prof_shift;		if(pc >= prof_len)			pc = prof_len - 1;		atomic_inc((atomic_t *)&prof_buffer[pc]);	}}static void timer_interrupt(int irq, void *dev_id, struct pt_regs * regs){	unsigned long ticks, pstate;	write_lock(&xtime_lock);	do {#ifndef CONFIG_SMP		if ((regs->tstate & TSTATE_PRIV) != 0)			sparc64_do_profile(regs->tpc, regs->u_regs[UREG_RETPC]);#endif		do_timer(regs);		/* Guarentee that the following sequences execute		 * uninterrupted.		 */		__asm__ __volatile__("rdpr	%%pstate, %0\n\t"				     "wrpr	%0, %1, %%pstate"				     : "=r" (pstate)				     : "i" (PSTATE_IE));		timer_tick_compare = tick_ops->add_compare(timer_tick_offset);		ticks = tick_ops->get_tick();		/* Restore PSTATE_IE. */		__asm__ __volatile__("wrpr	%0, 0x0, %%pstate"				     : /* no outputs */				     : "r" (pstate));	} while (time_after_eq(ticks, timer_tick_compare));	timer_check_rtc();	write_unlock(&xtime_lock);}#ifdef CONFIG_SMPvoid timer_tick_interrupt(struct pt_regs *regs){	write_lock(&xtime_lock);	do_timer(regs);	/*	 * Only keep timer_tick_offset uptodate, but don't set TICK_CMPR.	 */	timer_tick_compare = tick_ops->get_compare() + timer_tick_offset;	timer_check_rtc();	write_unlock(&xtime_lock);}#endif/* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */static void __init kick_start_clock(void){	unsigned long regs = mstk48t02_regs;	u8 sec, tmp;	int i, count;	prom_printf("CLOCK: Clock was stopped. Kick start ");	spin_lock_irq(&mostek_lock);	/* Turn on the kick start bit to start the oscillator. */	tmp = mostek_read(regs + MOSTEK_CREG);	tmp |= MSTK_CREG_WRITE;	mostek_write(regs + MOSTEK_CREG, tmp);	tmp = mostek_read(regs + MOSTEK_SEC);	tmp &= ~MSTK_STOP;	mostek_write(regs + MOSTEK_SEC, tmp);	tmp = mostek_read(regs + MOSTEK_HOUR);	tmp |= MSTK_KICK_START;	mostek_write(regs + MOSTEK_HOUR, tmp);	tmp = mostek_read(regs + MOSTEK_CREG);	tmp &= ~MSTK_CREG_WRITE;	mostek_write(regs + MOSTEK_CREG, tmp);	spin_unlock_irq(&mostek_lock);	/* Delay to allow the clock oscillator to start. */	sec = MSTK_REG_SEC(regs);	for (i = 0; i < 3; i++) {		while (sec == MSTK_REG_SEC(regs))			for (count = 0; count < 100000; count++)				/* nothing */ ;		prom_printf(".");		sec = MSTK_REG_SEC(regs);	}	prom_printf("\n");	spin_lock_irq(&mostek_lock);	/* Turn off kick start and set a "valid" time and date. */	tmp = mostek_read(regs + MOSTEK_CREG);	tmp |= MSTK_CREG_WRITE;	mostek_write(regs + MOSTEK_CREG, tmp);	tmp = mostek_read(regs + MOSTEK_HOUR);	tmp &= ~MSTK_KICK_START;	mostek_write(regs + MOSTEK_HOUR, tmp);	MSTK_SET_REG_SEC(regs,0);	MSTK_SET_REG_MIN(regs,0);	MSTK_SET_REG_HOUR(regs,0);	MSTK_SET_REG_DOW(regs,5);	MSTK_SET_REG_DOM(regs,1);	MSTK_SET_REG_MONTH(regs,8);	MSTK_SET_REG_YEAR(regs,1996 - MSTK_YEAR_ZERO);	tmp = mostek_read(regs + MOSTEK_CREG);	tmp &= ~MSTK_CREG_WRITE;	mostek_write(regs + MOSTEK_CREG, tmp);	spin_unlock_irq(&mostek_lock);	/* Ensure the kick start bit is off. If it isn't, turn it off. */	while (mostek_read(regs + MOSTEK_HOUR) & MSTK_KICK_START) {		prom_printf("CLOCK: Kick start still on!\n");		spin_lock_irq(&mostek_lock);		tmp = mostek_read(regs + MOSTEK_CREG);		tmp |= MSTK_CREG_WRITE;		mostek_write(regs + MOSTEK_CREG, tmp);		tmp = mostek_read(regs + MOSTEK_HOUR);		tmp &= ~MSTK_KICK_START;		mostek_write(regs + MOSTEK_HOUR, tmp);

⌨️ 快捷键说明

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