2006-09-19 02:18:16 +04:00
/*
* arch / arm / plat - iop / time . c
*
* Timer code for IOP32x and IOP33x based systems
*
* Author : Deepak Saxena < dsaxena @ mvista . com >
*
* Copyright 2002 - 2003 MontaVista Software Inc .
*
* 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 .
*/
# include <linux/kernel.h>
# include <linux/interrupt.h>
# include <linux/time.h>
# include <linux/init.h>
# include <linux/timex.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2009-10-29 21:46:54 +03:00
# include <linux/clocksource.h>
2009-10-29 21:46:54 +03:00
# include <linux/clockchips.h>
2011-08-01 00:17:29 +04:00
# include <linux/export.h>
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2006-09-19 02:18:16 +04:00
# include <asm/irq.h>
2010-12-16 00:52:10 +03:00
# include <asm/sched_clock.h>
2006-09-19 02:18:16 +04:00
# include <asm/uaccess.h>
# include <asm/mach/irq.h>
# include <asm/mach/time.h>
2008-08-05 19:14:15 +04:00
# include <mach/time.h>
2006-09-19 02:18:16 +04:00
2010-06-02 12:08:55 +04:00
/*
* Minimum clocksource / clockevent timer range in seconds
*/
# define IOP_MIN_RANGE 4
2009-10-29 21:46:54 +03:00
/*
* IOP clocksource ( free - running timer 1 ) .
*/
2010-12-04 08:20:52 +03:00
static cycle_t notrace iop_clocksource_read ( struct clocksource * unused )
2009-10-29 21:46:54 +03:00
{
return 0xffffffffu - read_tcr1 ( ) ;
}
static struct clocksource iop_clocksource = {
. name = " iop_timer1 " ,
. rating = 300 ,
. read = iop_clocksource_read ,
. mask = CLOCKSOURCE_MASK ( 32 ) ,
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
} ;
2009-10-29 21:46:56 +03:00
/*
* IOP sched_clock ( ) implementation via its clocksource .
*/
2011-12-15 15:19:23 +04:00
static u32 notrace iop_read_sched_clock ( void )
2009-10-29 21:46:56 +03:00
{
2011-12-15 15:19:23 +04:00
return 0xffffffffu - read_tcr1 ( ) ;
2009-10-29 21:46:56 +03:00
}
2009-10-29 21:46:54 +03:00
/*
* IOP clockevents ( interrupting timer 0 ) .
*/
static int iop_set_next_event ( unsigned long delta ,
struct clock_event_device * unused )
{
u32 tmr = IOP_TMR_PRIVILEGED | IOP_TMR_RATIO_1_1 ;
BUG_ON ( delta = = 0 ) ;
write_tmr0 ( tmr & ~ ( IOP_TMR_EN | IOP_TMR_RELOAD ) ) ;
write_tcr0 ( delta ) ;
write_tmr0 ( ( tmr & ~ IOP_TMR_RELOAD ) | IOP_TMR_EN ) ;
return 0 ;
}
2006-09-19 02:18:16 +04:00
static unsigned long ticks_per_jiffy ;
2009-10-29 21:46:54 +03:00
static void iop_set_mode ( enum clock_event_mode mode ,
struct clock_event_device * unused )
{
u32 tmr = read_tmr0 ( ) ;
switch ( mode ) {
case CLOCK_EVT_MODE_PERIODIC :
write_tmr0 ( tmr & ~ IOP_TMR_EN ) ;
write_tcr0 ( ticks_per_jiffy - 1 ) ;
2010-12-19 18:43:34 +03:00
write_trr0 ( ticks_per_jiffy - 1 ) ;
2009-10-29 21:46:54 +03:00
tmr | = ( IOP_TMR_RELOAD | IOP_TMR_EN ) ;
break ;
case CLOCK_EVT_MODE_ONESHOT :
/* ->set_next_event sets period and enables timer */
tmr & = ~ ( IOP_TMR_RELOAD | IOP_TMR_EN ) ;
break ;
case CLOCK_EVT_MODE_RESUME :
tmr | = IOP_TMR_EN ;
break ;
case CLOCK_EVT_MODE_SHUTDOWN :
case CLOCK_EVT_MODE_UNUSED :
default :
tmr & = ~ IOP_TMR_EN ;
break ;
}
write_tmr0 ( tmr ) ;
}
static struct clock_event_device iop_clockevent = {
. name = " iop_timer0 " ,
. features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT ,
. rating = 300 ,
. set_next_event = iop_set_next_event ,
. set_mode = iop_set_mode ,
} ;
2006-09-19 02:18:16 +04:00
static irqreturn_t
2007-02-13 19:13:34 +03:00
iop_timer_interrupt ( int irq , void * dev_id )
2006-09-19 02:18:16 +04:00
{
2009-10-29 21:46:54 +03:00
struct clock_event_device * evt = dev_id ;
2006-09-19 02:18:16 +04:00
2009-10-29 21:46:54 +03:00
write_tisr ( 1 ) ;
evt - > event_handler ( evt ) ;
2006-09-19 02:18:16 +04:00
return IRQ_HANDLED ;
}
2007-02-13 19:13:34 +03:00
static struct irqaction iop_timer_irq = {
. name = " IOP Timer Tick " ,
. handler = iop_timer_interrupt ,
2007-05-08 11:35:39 +04:00
. flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL ,
2009-10-29 21:46:54 +03:00
. dev_id = & iop_clockevent ,
2006-09-19 02:18:16 +04:00
} ;
2007-07-20 05:07:26 +04:00
static unsigned long iop_tick_rate ;
unsigned long get_iop_tick_rate ( void )
{
return iop_tick_rate ;
}
EXPORT_SYMBOL ( get_iop_tick_rate ) ;
2007-02-13 19:13:34 +03:00
void __init iop_init_time ( unsigned long tick_rate )
2006-09-19 02:18:16 +04:00
{
u32 timer_ctl ;
2011-12-15 15:19:23 +04:00
setup_sched_clock ( iop_read_sched_clock , 32 , tick_rate ) ;
2010-12-16 00:52:10 +03:00
2009-08-02 12:46:45 +04:00
ticks_per_jiffy = DIV_ROUND_CLOSEST ( tick_rate , HZ ) ;
2007-07-20 05:07:26 +04:00
iop_tick_rate = tick_rate ;
2006-09-19 02:18:16 +04:00
2007-02-13 19:13:34 +03:00
timer_ctl = IOP_TMR_EN | IOP_TMR_PRIVILEGED |
IOP_TMR_RELOAD | IOP_TMR_RATIO_1_1 ;
2006-09-19 02:18:16 +04:00
/*
2009-10-29 21:46:54 +03:00
* Set up interrupting clockevent timer 0.
2006-09-19 02:18:16 +04:00
*/
2009-10-29 21:46:54 +03:00
write_tmr0 ( timer_ctl & ~ IOP_TMR_EN ) ;
2010-12-19 18:43:34 +03:00
write_tisr ( 1 ) ;
2009-10-29 21:46:54 +03:00
setup_irq ( IRQ_IOP_TIMER0 , & iop_timer_irq ) ;
2010-06-02 12:08:55 +04:00
clockevents_calc_mult_shift ( & iop_clockevent ,
tick_rate , IOP_MIN_RANGE ) ;
2009-10-29 21:46:54 +03:00
iop_clockevent . max_delta_ns =
clockevent_delta2ns ( 0xfffffffe , & iop_clockevent ) ;
iop_clockevent . min_delta_ns =
clockevent_delta2ns ( 0xf , & iop_clockevent ) ;
iop_clockevent . cpumask = cpumask_of ( 0 ) ;
clockevents_register_device ( & iop_clockevent ) ;
2009-10-29 21:46:54 +03:00
/*
* Set up free - running clocksource timer 1.
*/
2007-02-13 19:13:34 +03:00
write_trr1 ( 0xffffffff ) ;
2009-10-29 21:46:54 +03:00
write_tcr1 ( 0xffffffff ) ;
2007-02-13 19:13:34 +03:00
write_tmr1 ( timer_ctl ) ;
2010-12-13 16:20:23 +03:00
clocksource_register_hz ( & iop_clocksource , tick_rate ) ;
2006-09-19 02:18:16 +04:00
}