diff options
Diffstat (limited to 'arch/blackfin/kernel/time.c')
-rw-r--r-- | arch/blackfin/kernel/time.c | 162 |
1 files changed, 91 insertions, 71 deletions
diff --git a/arch/blackfin/kernel/time.c b/arch/blackfin/kernel/time.c index eb23523..172b4c58 100644 --- a/arch/blackfin/kernel/time.c +++ b/arch/blackfin/kernel/time.c @@ -1,32 +1,11 @@ /* - * File: arch/blackfin/kernel/time.c - * Based on: none - original work - * Author: + * arch/blackfin/kernel/time.c * - * Created: - * Description: This file contains the bfin-specific time handling details. - * Most of the stuff is located in the machine specific files. - * FIXME: (This file is subject for removal) + * This file contains the Blackfin-specific time handling details. + * Most of the stuff is located in the machine specific files. * - * Modified: - * Copyright 2004-2008 Analog Devices Inc. - * - * Bugs: Enter bugs at http://blackfin.uclinux.org/ - * - * 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, see the file COPYING, or write - * to the Free Software Foundation, Inc., - * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * Copyright 2004-2008 Analog Devices Inc. + * Licensed under the GPL-2 or later. */ #include <linux/module.h> @@ -34,23 +13,43 @@ #include <linux/interrupt.h> #include <linux/time.h> #include <linux/irq.h> +#include <linux/delay.h> #include <asm/blackfin.h> #include <asm/time.h> +#include <asm/gptimers.h> /* This is an NTP setting */ #define TICK_SIZE (tick_nsec / 1000) -static void time_sched_init(irq_handler_t timer_routine); -static unsigned long gettimeoffset(void); - static struct irqaction bfin_timer_irq = { - .name = "BFIN Timer Tick", + .name = "Blackfin Timer Tick", +#ifdef CONFIG_IRQ_PER_CPU + .flags = IRQF_DISABLED | IRQF_PERCPU, +#else .flags = IRQF_DISABLED +#endif }; -static void -time_sched_init(irq_handler_t timer_routine) +#if defined(CONFIG_TICK_SOURCE_SYSTMR0) || defined(CONFIG_IPIPE) +void __init setup_system_timer0(void) +{ + /* Power down the core timer, just to play safe. */ + bfin_write_TCNTL(0); + + disable_gptimers(TIMER0bit); + set_gptimer_status(0, TIMER_STATUS_TRUN0); + while (get_gptimer_status(0) & TIMER_STATUS_TRUN0) + udelay(10); + + set_gptimer_config(0, 0x59); /* IRQ enable, periodic, PWM_OUT, SCLKed, OUT PAD disabled */ + set_gptimer_period(TIMER0_id, get_sclk() / HZ); + set_gptimer_pwidth(TIMER0_id, 1); + SSYNC(); + enable_gptimers(TIMER0bit); +} +#else +void __init setup_core_timer(void) { u32 tcount; @@ -58,10 +57,8 @@ time_sched_init(irq_handler_t timer_routine) bfin_write_TCNTL(1); CSYNC(); - /* - * the TSCALE prescaler counter. - */ - bfin_write_TSCALE((TIME_SCALE - 1)); + /* the TSCALE prescaler counter */ + bfin_write_TSCALE(TIME_SCALE - 1); tcount = ((get_cclk() / (HZ * TIME_SCALE)) - 1); bfin_write_TPERIOD(tcount); @@ -71,35 +68,52 @@ time_sched_init(irq_handler_t timer_routine) CSYNC(); bfin_write_TCNTL(7); +} +#endif - bfin_timer_irq.handler = (irq_handler_t)timer_routine; - /* call setup_irq instead of request_irq because request_irq calls - * kmalloc which has not been initialized yet - */ +static void __init +time_sched_init(irqreturn_t(*timer_routine) (int, void *)) +{ +#if defined(CONFIG_TICK_SOURCE_SYSTMR0) || defined(CONFIG_IPIPE) + setup_system_timer0(); + bfin_timer_irq.handler = timer_routine; + setup_irq(IRQ_TIMER0, &bfin_timer_irq); +#else + setup_core_timer(); + bfin_timer_irq.handler = timer_routine; setup_irq(IRQ_CORETMR, &bfin_timer_irq); +#endif } /* * Should return useconds since last timer tick */ +#ifndef CONFIG_GENERIC_TIME static unsigned long gettimeoffset(void) { unsigned long offset; unsigned long clocks_per_jiffy; +#if defined(CONFIG_TICK_SOURCE_SYSTMR0) || defined(CONFIG_IPIPE) + clocks_per_jiffy = bfin_read_TIMER0_PERIOD(); + offset = bfin_read_TIMER0_COUNTER() / \ + (((clocks_per_jiffy + 1) * HZ) / USEC_PER_SEC); + + if ((get_gptimer_status(0) & TIMER_STATUS_TIMIL0) && offset < (100000 / HZ / 2)) + offset += (USEC_PER_SEC / HZ); +#else clocks_per_jiffy = bfin_read_TPERIOD(); - offset = - (clocks_per_jiffy - - bfin_read_TCOUNT()) / (((clocks_per_jiffy + 1) * HZ) / - USEC_PER_SEC); + offset = (clocks_per_jiffy - bfin_read_TCOUNT()) / \ + (((clocks_per_jiffy + 1) * HZ) / USEC_PER_SEC); /* Check if we just wrapped the counters and maybe missed a tick */ if ((bfin_read_ILAT() & (1 << IRQ_CORETMR)) - && (offset < (100000 / HZ / 2))) + && (offset < (100000 / HZ / 2))) offset += (USEC_PER_SEC / HZ); - +#endif return offset; } +#endif static inline int set_rtc_mmss(unsigned long nowtime) { @@ -111,43 +125,49 @@ static inline int set_rtc_mmss(unsigned long nowtime) * as well as call the "do_timer()" routine every clocktick */ #ifdef CONFIG_CORE_TIMER_IRQ_L1 -irqreturn_t timer_interrupt(int irq, void *dummy)__attribute__((l1_text)); +__attribute__((l1_text)) #endif - irqreturn_t timer_interrupt(int irq, void *dummy) { /* last time the cmos clock got updated */ static long last_rtc_update; write_seqlock(&xtime_lock); - - do_timer(1); - - profile_tick(CPU_PROFILING); - - /* - * If we have an externally synchronized Linux clock, then update - * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be - * called as close as possible to 500 ms before the new second starts. - */ - - if (ntp_synced() && - xtime.tv_sec > last_rtc_update + 660 && - (xtime.tv_nsec / NSEC_PER_USEC) >= - 500000 - ((unsigned)TICK_SIZE) / 2 - && (xtime.tv_nsec / NSEC_PER_USEC) <= - 500000 + ((unsigned)TICK_SIZE) / 2) { - if (set_rtc_mmss(xtime.tv_sec) == 0) - last_rtc_update = xtime.tv_sec; - else - /* Do it again in 60s. */ - last_rtc_update = xtime.tv_sec - 600; +#if defined(CONFIG_TICK_SOURCE_SYSTMR0) && !defined(CONFIG_IPIPE) +/* FIXME: Here TIMIL0 is not set when IPIPE enabled, why? */ + if (get_gptimer_status(0) & TIMER_STATUS_TIMIL0) { +#endif + do_timer(1); + + /* + * If we have an externally synchronized Linux clock, then update + * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be + * called as close as possible to 500 ms before the new second starts. + */ + if (ntp_synced() && + xtime.tv_sec > last_rtc_update + 660 && + (xtime.tv_nsec / NSEC_PER_USEC) >= + 500000 - ((unsigned)TICK_SIZE) / 2 + && (xtime.tv_nsec / NSEC_PER_USEC) <= + 500000 + ((unsigned)TICK_SIZE) / 2) { + if (set_rtc_mmss(xtime.tv_sec) == 0) + last_rtc_update = xtime.tv_sec; + else + /* Do it again in 60s. */ + last_rtc_update = xtime.tv_sec - 600; + } +#if defined(CONFIG_TICK_SOURCE_SYSTMR0) && !defined(CONFIG_IPIPE) + set_gptimer_status(0, TIMER_STATUS_TIMIL0); } +#endif write_sequnlock(&xtime_lock); -#ifndef CONFIG_SMP +#ifdef CONFIG_IPIPE + update_root_process_times(get_irq_regs()); +#else update_process_times(user_mode(get_irq_regs())); #endif + profile_tick(CPU_PROFILING); return IRQ_HANDLED; } |