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

📄 at91rm9200_time.c

📁 linux 内核源代码
💻 C
字号:
/* * linux/arch/arm/mach-at91/at91rm9200_time.c * *  Copyright (C) 2003 SAN People *  Copyright (C) 2003 ATMEL * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */#include <linux/kernel.h>#include <linux/interrupt.h>#include <linux/irq.h>#include <linux/clockchips.h>#include <asm/mach/time.h>#include <asm/arch/at91_st.h>static unsigned long last_crtr;static u32 irqmask;static struct clock_event_device clkevt;/* * The ST_CRTR is updated asynchronously to the master clock ... but * the updates as seen by the CPU don't seem to be strictly monotonic. * Waiting until we read the same value twice avoids glitching. */static inline unsigned long read_CRTR(void){	unsigned long x1, x2;	x1 = at91_sys_read(AT91_ST_CRTR);	do {		x2 = at91_sys_read(AT91_ST_CRTR);		if (x1 == x2)			break;		x1 = x2;	} while (1);	return x1;}/* * IRQ handler for the timer. */static irqreturn_t at91rm9200_timer_interrupt(int irq, void *dev_id){	u32	sr = at91_sys_read(AT91_ST_SR) & irqmask;	/* simulate "oneshot" timer with alarm */	if (sr & AT91_ST_ALMS) {		clkevt.event_handler(&clkevt);		return IRQ_HANDLED;	}	/* periodic mode should handle delayed ticks */	if (sr & AT91_ST_PITS) {		u32	crtr = read_CRTR();		while (((crtr - last_crtr) & AT91_ST_CRTV) >= LATCH) {			last_crtr += LATCH;			clkevt.event_handler(&clkevt);		}		return IRQ_HANDLED;	}	/* this irq is shared ... */	return IRQ_NONE;}static struct irqaction at91rm9200_timer_irq = {	.name		= "at91_tick",	.flags		= IRQF_SHARED | IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL,	.handler	= at91rm9200_timer_interrupt};static cycle_t read_clk32k(void){	return read_CRTR();}static struct clocksource clk32k = {	.name		= "32k_counter",	.rating		= 150,	.read		= read_clk32k,	.mask		= CLOCKSOURCE_MASK(20),	.shift		= 10,	.flags		= CLOCK_SOURCE_IS_CONTINUOUS,};static voidclkevt32k_mode(enum clock_event_mode mode, struct clock_event_device *dev){	/* Disable and flush pending timer interrupts */	at91_sys_write(AT91_ST_IDR, AT91_ST_PITS | AT91_ST_ALMS);	(void) at91_sys_read(AT91_ST_SR);	last_crtr = read_CRTR();	switch (mode) {	case CLOCK_EVT_MODE_PERIODIC:		/* PIT for periodic irqs; fixed rate of 1/HZ */		irqmask = AT91_ST_PITS;		at91_sys_write(AT91_ST_PIMR, LATCH);		break;	case CLOCK_EVT_MODE_ONESHOT:		/* ALM for oneshot irqs, set by next_event()		 * before 32 seconds have passed		 */		irqmask = AT91_ST_ALMS;		at91_sys_write(AT91_ST_RTAR, last_crtr);		break;	case CLOCK_EVT_MODE_SHUTDOWN:	case CLOCK_EVT_MODE_UNUSED:	case CLOCK_EVT_MODE_RESUME:		irqmask = 0;		break;	}	at91_sys_write(AT91_ST_IER, irqmask);}static intclkevt32k_next_event(unsigned long delta, struct clock_event_device *dev){	unsigned long	flags;	u32		alm;	int		status = 0;	BUG_ON(delta < 2);	/* Use "raw" primitives so we behave correctly on RT kernels. */	raw_local_irq_save(flags);	/* The alarm IRQ uses absolute time (now+delta), not the relative	 * time (delta) in our calling convention.  Like all clockevents	 * using such "match" hardware, we have a race to defend against.	 *	 * Our defense here is to have set up the clockevent device so the	 * delta is at least two.  That way we never end up writing RTAR	 * with the value then held in CRTR ... which would mean the match	 * wouldn't trigger until 32 seconds later, after CRTR wraps.	 */	alm = read_CRTR();	/* Cancel any pending alarm; flush any pending IRQ */	at91_sys_write(AT91_ST_RTAR, alm);	(void) at91_sys_read(AT91_ST_SR);	/* Schedule alarm by writing RTAR. */	alm += delta;	at91_sys_write(AT91_ST_RTAR, alm);	raw_local_irq_restore(flags);	return status;}static struct clock_event_device clkevt = {	.name		= "at91_tick",	.features	= CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT,	.shift		= 32,	.rating		= 150,	.cpumask	= CPU_MASK_CPU0,	.set_next_event	= clkevt32k_next_event,	.set_mode	= clkevt32k_mode,};/* * ST (system timer) module supports both clockevents and clocksource. */void __init at91rm9200_timer_init(void){	/* Disable all timer interrupts, and clear any pending ones */	at91_sys_write(AT91_ST_IDR,		AT91_ST_PITS | AT91_ST_WDOVF | AT91_ST_RTTINC | AT91_ST_ALMS);	(void) at91_sys_read(AT91_ST_SR);	/* Make IRQs happen for the system timer */	setup_irq(AT91_ID_SYS, &at91rm9200_timer_irq);	/* The 32KiHz "Slow Clock" (tick every 30517.58 nanoseconds) is used	 * directly for the clocksource and all clockevents, after adjusting	 * its prescaler from the 1 Hz default.	 */	at91_sys_write(AT91_ST_RTMR, 1);	/* Setup timer clockevent, with minimum of two ticks (important!!) */	clkevt.mult = div_sc(AT91_SLOW_CLOCK, NSEC_PER_SEC, clkevt.shift);	clkevt.max_delta_ns = clockevent_delta2ns(AT91_ST_ALMV, &clkevt);	clkevt.min_delta_ns = clockevent_delta2ns(2, &clkevt) + 1;	clkevt.cpumask = cpumask_of_cpu(0);	clockevents_register_device(&clkevt);	/* register clocksource */	clk32k.mult = clocksource_hz2mult(AT91_SLOW_CLOCK, clk32k.shift);	clocksource_register(&clk32k);}struct sys_timer at91rm9200_timer = {	.init		= at91rm9200_timer_init,};

⌨️ 快捷键说明

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