time.c

来自「linux 内核源代码」· C语言 代码 · 共 1,713 行 · 第 1/3 页

C
1,713
字号
/* $Id: time.c,v 1.42 2002/01/23 14:33:55 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/errno.h>#include <linux/module.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/time.h>#include <linux/timex.h>#include <linux/init.h>#include <linux/ioport.h>#include <linux/mc146818rtc.h>#include <linux/delay.h>#include <linux/profile.h>#include <linux/bcd.h>#include <linux/jiffies.h>#include <linux/cpufreq.h>#include <linux/percpu.h>#include <linux/miscdevice.h>#include <linux/rtc.h>#include <linux/kernel_stat.h>#include <linux/clockchips.h>#include <linux/clocksource.h>#include <asm/oplib.h>#include <asm/mostek.h>#include <asm/timer.h>#include <asm/irq.h>#include <asm/io.h>#include <asm/prom.h>#include <asm/of_device.h>#include <asm/starfire.h>#include <asm/smp.h>#include <asm/sections.h>#include <asm/cpudata.h>#include <asm/uaccess.h>#include <asm/irq_regs.h>DEFINE_SPINLOCK(mostek_lock);DEFINE_SPINLOCK(rtc_lock);void __iomem *mstk48t02_regs = NULL;#ifdef CONFIG_PCIunsigned long ds1287_regs = 0UL;static void __iomem *bq4802_regs;#endifstatic void __iomem *mstk48t08_regs;static void __iomem *mstk48t59_regs;static int set_rtc_mmss(unsigned long);#define TICK_PRIV_BIT	(1UL << 63)#define TICKCMP_IRQ_BIT	(1UL << 63)#ifdef CONFIG_SMPunsigned long profile_pc(struct pt_regs *regs){	unsigned long pc = instruction_pointer(regs);	if (in_lock_functions(pc))		return regs->u_regs[UREG_RETPC];	return pc;}EXPORT_SYMBOL(profile_pc);#endifstatic 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_disable_irq(void){	__asm__ __volatile__(	"	ba,pt	%%xcc, 1f\n"	"	 nop\n"	"	.align	64\n"	"1:	wr	%0, 0x0, %%tick_cmpr\n"	"	rd	%%tick_cmpr, %%g0"	: /* no outputs */	: "r" (TICKCMP_IRQ_BIT));}static void tick_init_tick(void){	tick_disable_protection();	tick_disable_irq();}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 int tick_add_compare(unsigned long adj){	unsigned long orig_tick, new_tick, new_compare;	__asm__ __volatile__("rd	%%tick, %0"			     : "=r" (orig_tick));	orig_tick &= ~TICKCMP_IRQ_BIT;	/* 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__("ba,pt	%%xcc, 1f\n\t"			     " add	%1, %2, %0\n\t"			     ".align	64\n"			     "1:\n\t"			     "wr	%0, 0, %%tick_cmpr\n\t"			     "rd	%%tick_cmpr, %%g0\n\t"			     : "=r" (new_compare)			     : "r" (orig_tick), "r" (adj));	__asm__ __volatile__("rd	%%tick, %0"			     : "=r" (new_tick));	new_tick &= ~TICKCMP_IRQ_BIT;	return ((long)(new_tick - (orig_tick+adj))) > 0L;}static unsigned long tick_add_tick(unsigned long adj){	unsigned long new_tick;	/* Also need to handle Blackbird bug here too. */	__asm__ __volatile__("rd	%%tick, %0\n\t"			     "add	%0, %1, %0\n\t"			     "wrpr	%0, 0, %%tick\n\t"			     : "=&r" (new_tick)			     : "r" (adj));	return new_tick;}static struct sparc64_tick_ops tick_operations __read_mostly = {	.name		=	"tick",	.init_tick	=	tick_init_tick,	.disable_irq	=	tick_disable_irq,	.get_tick	=	tick_get_tick,	.add_tick	=	tick_add_tick,	.add_compare	=	tick_add_compare,	.softint_mask	=	1UL << 0,};struct sparc64_tick_ops *tick_ops __read_mostly = &tick_operations;static void stick_disable_irq(void){	__asm__ __volatile__(	"wr	%0, 0x0, %%asr25"	: /* no outputs */	: "r" (TICKCMP_IRQ_BIT));}static void stick_init_tick(void){	/* Writes to the %tick and %stick register are not	 * allowed on sun4v.  The Hypervisor controls that	 * bit, per-strand.	 */	if (tlb_type != hypervisor) {		tick_disable_protection();		tick_disable_irq();		/* 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");	}	stick_disable_irq();}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_add_tick(unsigned long adj){	unsigned long new_tick;	__asm__ __volatile__("rd	%%asr24, %0\n\t"			     "add	%0, %1, %0\n\t"			     "wr	%0, 0, %%asr24\n\t"			     : "=&r" (new_tick)			     : "r" (adj));	return new_tick;}static int stick_add_compare(unsigned long adj){	unsigned long orig_tick, new_tick;	__asm__ __volatile__("rd	%%asr24, %0"			     : "=r" (orig_tick));	orig_tick &= ~TICKCMP_IRQ_BIT;	__asm__ __volatile__("wr	%0, 0, %%asr25"			     : /* no outputs */			     : "r" (orig_tick + adj));	__asm__ __volatile__("rd	%%asr24, %0"			     : "=r" (new_tick));	new_tick &= ~TICKCMP_IRQ_BIT;	return ((long)(new_tick - (orig_tick+adj))) > 0L;}static struct sparc64_tick_ops stick_operations __read_mostly = {	.name		=	"stick",	.init_tick	=	stick_init_tick,	.disable_irq	=	stick_disable_irq,	.get_tick	=	stick_get_tick,	.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 high * 2) read low * 3) read high again, if it rolled re-read both low and high again. * * 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+8;	__asm__ __volatile__("ldxa	[%1] %5, %2\n"			     "1:\n\t"			     "sub	%1, 0x8, %1\n\t"			     "ldxa	[%1] %5, %3\n\t"			     "add	%1, 0x8, %1\n\t"			     "ldxa	[%1] %5, %4\n\t"			     "cmp	%4, %2\n\t"			     "bne,a,pn	%%xcc, 1b\n\t"			     " mov	%4, %2\n\t"			     "sllx	%4, 32, %4\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 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_disable_irq(void){	__hbird_write_compare(TICKCMP_IRQ_BIT);}static void hbtick_init_tick(void){	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());	hbtick_disable_irq();}static unsigned long hbtick_get_tick(void){	return __hbird_read_stick() & ~TICK_PRIV_BIT;}static unsigned long hbtick_add_tick(unsigned long adj){	unsigned long val;	val = __hbird_read_stick() + adj;	__hbird_write_stick(val);	return val;}static int hbtick_add_compare(unsigned long adj){	unsigned long val = __hbird_read_stick();	unsigned long val2;	val &= ~TICKCMP_IRQ_BIT;	val += adj;	__hbird_write_compare(val);	val2 = __hbird_read_stick() & ~TICKCMP_IRQ_BIT;	return ((long)(val2 - val)) > 0L;}static struct sparc64_tick_ops hbtick_operations __read_mostly = {	.name		=	"hbtick",	.init_tick	=	hbtick_init_tick,	.disable_irq	=	hbtick_disable_irq,	.get_tick	=	hbtick_get_tick,	.add_tick	=	hbtick_add_tick,	.add_compare	=	hbtick_add_compare,	.softint_mask	=	1UL << 0,};static unsigned long timer_ticks_per_nsec_quotient __read_mostly;int update_persistent_clock(struct timespec now){	return set_rtc_mmss(now.tv_sec);}/* Kick start a stopped clock (procedure from the Sun NVRAM/hostid FAQ). */static void __init kick_start_clock(void){	void __iomem *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);		tmp = mostek_read(regs + MOSTEK_CREG);		tmp &= ~MSTK_CREG_WRITE;		mostek_write(regs + MOSTEK_CREG, tmp);		spin_unlock_irq(&mostek_lock);	}	prom_printf("CLOCK: Kick start procedure successful.\n");}/* Return nonzero if the clock chip battery is low. */static int __init has_low_battery(void){	void __iomem *regs = mstk48t02_regs;	u8 data1, data2;	spin_lock_irq(&mostek_lock);	data1 = mostek_read(regs + MOSTEK_EEPROM);	/* Read some data. */	mostek_write(regs + MOSTEK_EEPROM, ~data1);	/* Write back the complement. */	data2 = mostek_read(regs + MOSTEK_EEPROM);	/* Read back the complement. */	mostek_write(regs + MOSTEK_EEPROM, data1);	/* Restore original value. */	spin_unlock_irq(&mostek_lock);	return (data1 == data2);	/* Was the write blocked? */}/* Probe for the real time clock chip. */static void __init set_system_time(void){	unsigned int year, mon, day, hour, min, sec;	void __iomem *mregs = mstk48t02_regs;#ifdef CONFIG_PCI	unsigned long dregs = ds1287_regs;	void __iomem *bregs = bq4802_regs;#else	unsigned long dregs = 0UL;	void __iomem *bregs = 0UL;#endif	u8 tmp;	if (!mregs && !dregs && !bregs) {		prom_printf("Something wrong, clock regs not mapped yet.\n");		prom_halt();	}			if (mregs) {		spin_lock_irq(&mostek_lock);		/* Traditional Mostek chip. */		tmp = mostek_read(mregs + MOSTEK_CREG);		tmp |= MSTK_CREG_READ;		mostek_write(mregs + MOSTEK_CREG, tmp);		sec = MSTK_REG_SEC(mregs);		min = MSTK_REG_MIN(mregs);		hour = MSTK_REG_HOUR(mregs);		day = MSTK_REG_DOM(mregs);		mon = MSTK_REG_MONTH(mregs);		year = MSTK_CVT_YEAR( MSTK_REG_YEAR(mregs) );	} else if (bregs) {		unsigned char val = readb(bregs + 0x0e);		unsigned int century;		/* BQ4802 RTC chip. */		writeb(val | 0x08, bregs + 0x0e);		sec  = readb(bregs + 0x00);		min  = readb(bregs + 0x02);		hour = readb(bregs + 0x04);		day  = readb(bregs + 0x06);		mon  = readb(bregs + 0x09);		year = readb(bregs + 0x0a);		century = readb(bregs + 0x0f);		writeb(val, bregs + 0x0e);		BCD_TO_BIN(sec);		BCD_TO_BIN(min);		BCD_TO_BIN(hour);		BCD_TO_BIN(day);		BCD_TO_BIN(mon);		BCD_TO_BIN(year);		BCD_TO_BIN(century);		year += (century * 100);	} else {

⌨️ 快捷键说明

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