blob: 766a7068619ce90dfd22a9f2e13a4867bce29520 [file] [log] [blame]
/*
* Copyright (C) 2004, 2007-2010, 2011-2012 Synopsys, Inc. (www.synopsys.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* "Jiffies" based Time keeping with architecture specific
* -TOD
* -Tick processing
*
* Both of these are now done by generic code.
* Please refer to arch/arc/kernel/time.c whic has the new cool stuff
* This file is there just in case some body needs our good old lean-thin
* timer infrastructure
*
* Mar 2009: Rajeshwar Ranga
* Added Timer1 as clock source.
*
* Mar 2009: Vineetg
* -forked off time.c into time.c (new) and time-jiff.c (old)
*
* Jan 22 2008: Vineet Gupta
* Removed the redundant scheduler_tick() from timer_handler()
* because update_process_times() was already calling it
* This shaved off about 25% cycles off of Timer ISR
*
* Dec 28 2007: Rajeshwar Ranga
* Split the Timer Interrupt Handler for Boot CPU and Others
* Boot CPU will do the global time keeping as well as scheduling
* ALL other CPU(s) onyl tick the scheduler
*
* Oct 30 2007: Simon Spooner
* Implemented the arch specific gettimeofday routines
*
* Amit Shah, Rahul Trivedi, Sameer Dhavale: Codito Technologies 2004
*/
#include <linux/spinlock.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/interrupt.h>
#include <linux/time.h>
#include <linux/init.h>
#include <linux/timex.h>
#include <linux/profile.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/arcregs.h>
#include <asm/event-log.h>
#include <linux/clocksource.h>
/* ARC700 has two 32bit independent prog Timers: TIMER0 and TIMER1
* Each can programmed to go from @count to @limit and optionally
* interrupt when that happens
*/
/******************************************************************
* Hardware Interface routines to program the ARC TIMERs
******************************************************************/
/*
* Arm the timer to interrupt after @limit cycles
*/
void arc_timer0_setup_event(unsigned int limit)
{
/* setup start and end markers */
write_new_aux_reg(ARC_REG_TIMER0_LIMIT, limit);
write_new_aux_reg(ARC_REG_TIMER0_CNT, 0); // start from 0
/* IE: Interrupt on count = limit,
* NH: Count cycles only when CPU running (NOT Halted)
*/
write_new_aux_reg(ARC_REG_TIMER0_CTRL, TIMER_CTRL_IE|TIMER_CTRL_NH);
}
/*
* Acknowledge the interrupt
*/
void arc_timer0_ack_event(void)
{
/* Ack the interrupt by writing to CTRL reg.
Any write will cause intr to be ack, however we only
set the bits which sh be 1
*/
write_new_aux_reg(ARC_REG_TIMER0_CTRL, TIMER_CTRL_IE | TIMER_CTRL_NH);
}
/*
*/
void arc_timer1_setup_free_flow(unsigned int limit)
{
/* although for free flowing case, limit would alway be max 32 bits
* still we've kept the interface open, just in case ...
*/
write_new_aux_reg(ARC_REG_TIMER1_LIMIT, limit); // caller specifies
write_new_aux_reg(ARC_REG_TIMER1_CNT, 0); // start from 0
write_new_aux_reg(ARC_REG_TIMER1_CTRL, TIMER_CTRL_NH);
}
/******************************************************************
* Old way of implementing gettimeofday backend
* Now handled by architecture independent Generic TOD subsystem
******************************************************************/
#ifndef CONFIG_GENERIC_TIME
int do_settimeofday(struct timespec *tv)
{
time_t wtm_sec, sec = tv->tv_sec;
long wtm_nsec, nsec = tv->tv_nsec;
if ((unsigned long)tv->tv_nsec >= NSEC_PER_SEC)
return -EINVAL;
write_seqlock_irq(&xtime_lock);
{
wtm_sec = wall_to_monotonic.tv_sec + (xtime.tv_sec - sec);
wtm_nsec = wall_to_monotonic.tv_nsec + (xtime.tv_nsec - nsec);
set_normalized_timespec(&xtime, sec, nsec);
set_normalized_timespec(&wall_to_monotonic, wtm_sec, wtm_nsec);
time_adjust = 0; /* stop active adjtime() */
time_status |= STA_UNSYNC;
time_maxerror = NTP_PHASE_LIMIT;
time_esterror = NTP_PHASE_LIMIT;
}
write_sequnlock_irq(&xtime_lock);
clock_was_set();
return 0;
}
EXPORT_SYMBOL(do_settimeofday);
void do_gettimeofday(struct timeval *tv)
{
unsigned long flags;
unsigned long seq;
unsigned long usec, sec;
do {
seq = read_seqbegin_irqsave(&xtime_lock, flags);
sec = xtime.tv_sec;
usec = xtime.tv_nsec / 1000;
} while (read_seqretry_irqrestore(&xtime_lock, seq, flags));
while (unlikely(usec >= USEC_PER_SEC)) {
usec -= USEC_PER_SEC;
++sec;
}
tv->tv_sec = sec;
tv->tv_usec = usec;
}
EXPORT_SYMBOL(do_gettimeofday);
#else
/********** Clock Source Device *********/
static cycle_t cycle_read_t1(void)
{
return read_new_aux_reg(ARC_REG_TIMER1_CNT);
}
static struct clocksource clocksource_t1 = {
.name = "ARC Timer1",
.rating = 300,
.read = cycle_read_t1,
.mask = CLOCKSOURCE_MASK(32),
.flags = CLOCK_SOURCE_IS_CONTINUOUS,
};
void __init arc_clocksource_init(void)
{
u64 temp;
u32 shift;
struct clocksource *cs = &clocksource_t1;
/* Find a shift value */
for (shift = 32; shift > 0; shift--) {
temp = (u64) NSEC_PER_SEC << shift;
do_div(temp, CONFIG_ARC700_CLK);
if ((temp >> 32) == 0)
break;
}
cs->shift = shift;
cs->mult = (u32) temp;
arc_timer1_setup_free_flow(0xFFFFFFFF);
clocksource_register(&clocksource_t1);
}
#endif
/**************************************************************
* Old way of counting ticks and deciding what to do each tick
* Now handled by architecture independent code
************************************************************/
irqreturn_t timer_handler(int irq, void *dev_id);
static struct irqaction arc_timer_irq = {
.name = "ARC Timer0",
.flags = 0,
.handler = timer_handler,
};
void board_setup_timer(void)
{
setup_irq(TIMER0_INT, &arc_timer_irq);
/* Setup TIMER0 as the Linux heartbeat, ticking HZ times a sec */
arc_timer0_setup_event(CONFIG_ARC700_CLK / HZ);
#ifdef CONFIG_ARC_DBG_EVENT_TIMELINE
/* TIMER1 to provide free flowing cycle timestamping for event logger */
arc_timer1_setup_free_flow(0xEFFFFFFF);
#endif
}
irqreturn_t timer_handler(int irq, void *dev_id)
{
arc_timer0_ack_event();
/* only Boot CPU, ID = 0, does wall time keeping */
if (!smp_processor_id()) {
write_seqlock(&xtime_lock);
do_timer(1);
write_sequnlock(&xtime_lock);
}
update_process_times(user_mode(get_irq_regs()));
#ifdef CONFIG_PROFILING
profile_tick(CPU_PROFILING);
#endif
return IRQ_HANDLED;
}
void __init time_init(void)
{
extern unsigned long clk_speed;
if (clk_speed != CONFIG_ARC700_CLK)
panic("CONFIG_ARC700_CLK doesn't match corresp boot param\n");
xtime.tv_sec = 0;
xtime.tv_nsec = 0;
set_normalized_timespec(&wall_to_monotonic, -xtime.tv_sec, -xtime.tv_nsec);
#ifdef CONFIG_GENERIC_TIME
arc_clocksource_init();
#endif
board_setup_timer();
}