2007-02-16 15:36:55 +01:00
/*
2008-02-15 08:41:06 +01:00
* arch / arm / mach - ns9xxx / time - ns9360 . c
2007-02-16 15:36:55 +01:00
*
2008-02-15 08:41:06 +01:00
* Copyright ( C ) 2006 , 2007 by Digi International Inc .
2007-02-16 15:36:55 +01:00
* All rights reserved .
*
* 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 .
*/
# include <linux/jiffies.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
2007-09-30 20:35:48 +01:00
# include <linux/stringify.h>
# include <linux/clocksource.h>
2007-09-30 20:36:00 +01:00
# include <linux/clockchips.h>
2007-09-30 20:35:48 +01:00
2008-08-05 16:14:15 +01:00
# include <mach/processor-ns9360.h>
# include <mach/regs-sys-ns9360.h>
# include <mach/irqs.h>
# include <mach/system.h>
2007-02-16 15:36:55 +01:00
# include "generic.h"
2007-09-30 20:36:00 +01:00
# define TIMER_CLOCKSOURCE 0
# define TIMER_CLOCKEVENT 1
static u32 latch ;
2007-02-16 15:36:55 +01:00
2009-04-21 12:24:00 -07:00
static cycle_t ns9360_clocksource_read ( struct clocksource * cs )
2007-09-30 20:36:00 +01:00
{
2007-09-30 20:36:33 +01:00
return __raw_readl ( SYS_TR ( TIMER_CLOCKSOURCE ) ) ;
2007-09-30 20:36:00 +01:00
}
2008-02-15 08:41:06 +01:00
static struct clocksource ns9360_clocksource = {
. name = " ns9360-timer " __stringify ( TIMER_CLOCKSOURCE ) ,
2007-09-30 20:36:00 +01:00
. rating = 300 ,
2008-02-15 08:41:06 +01:00
. read = ns9360_clocksource_read ,
2007-09-30 20:36:00 +01:00
. mask = CLOCKSOURCE_MASK ( 32 ) ,
. shift = 20 ,
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
} ;
2008-02-15 08:41:06 +01:00
static void ns9360_clockevent_setmode ( enum clock_event_mode mode ,
2007-09-30 20:36:00 +01:00
struct clock_event_device * clk )
{
2007-09-30 20:36:33 +01:00
u32 tc = __raw_readl ( SYS_TC ( TIMER_CLOCKEVENT ) ) ;
2007-09-30 20:36:00 +01:00
2008-02-15 08:41:06 +01:00
switch ( mode ) {
2007-09-30 20:36:00 +01:00
case CLOCK_EVT_MODE_PERIODIC :
2007-09-30 20:36:33 +01:00
__raw_writel ( latch , SYS_TRC ( TIMER_CLOCKEVENT ) ) ;
2007-09-30 20:36:00 +01:00
REGSET ( tc , SYS_TCx , REN , EN ) ;
REGSET ( tc , SYS_TCx , INTS , EN ) ;
REGSET ( tc , SYS_TCx , TEN , EN ) ;
break ;
case CLOCK_EVT_MODE_ONESHOT :
REGSET ( tc , SYS_TCx , REN , DIS ) ;
REGSET ( tc , SYS_TCx , INTS , EN ) ;
/* fall through */
case CLOCK_EVT_MODE_UNUSED :
case CLOCK_EVT_MODE_SHUTDOWN :
case CLOCK_EVT_MODE_RESUME :
default :
REGSET ( tc , SYS_TCx , TEN , DIS ) ;
break ;
}
2007-09-30 20:36:33 +01:00
__raw_writel ( tc , SYS_TC ( TIMER_CLOCKEVENT ) ) ;
2007-09-30 20:36:00 +01:00
}
2008-02-15 08:41:06 +01:00
static int ns9360_clockevent_setnextevent ( unsigned long evt ,
2007-09-30 20:36:00 +01:00
struct clock_event_device * clk )
{
2007-09-30 20:36:33 +01:00
u32 tc = __raw_readl ( SYS_TC ( TIMER_CLOCKEVENT ) ) ;
2007-09-30 20:36:00 +01:00
if ( REGGET ( tc , SYS_TCx , TEN ) ) {
REGSET ( tc , SYS_TCx , TEN , DIS ) ;
2007-09-30 20:36:33 +01:00
__raw_writel ( tc , SYS_TC ( TIMER_CLOCKEVENT ) ) ;
2007-09-30 20:36:00 +01:00
}
REGSET ( tc , SYS_TCx , TEN , EN ) ;
2007-09-30 20:36:33 +01:00
__raw_writel ( evt , SYS_TRC ( TIMER_CLOCKEVENT ) ) ;
2007-09-30 20:36:00 +01:00
2007-09-30 20:36:33 +01:00
__raw_writel ( tc , SYS_TC ( TIMER_CLOCKEVENT ) ) ;
2007-09-30 20:36:00 +01:00
return 0 ;
}
2008-02-15 08:41:06 +01:00
static struct clock_event_device ns9360_clockevent_device = {
. name = " ns9360-timer " __stringify ( TIMER_CLOCKEVENT ) ,
2007-09-30 20:36:00 +01:00
. shift = 20 ,
. features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT ,
2008-02-15 08:41:06 +01:00
. set_mode = ns9360_clockevent_setmode ,
. set_next_event = ns9360_clockevent_setnextevent ,
2007-09-30 20:36:00 +01:00
} ;
2008-02-15 08:41:06 +01:00
static irqreturn_t ns9360_clockevent_handler ( int irq , void * dev_id )
2007-02-16 15:36:55 +01:00
{
2008-02-15 08:41:06 +01:00
int timerno = irq - IRQ_NS9360_TIMER0 ;
2007-08-07 21:08:21 +01:00
u32 tc ;
2008-02-15 08:41:06 +01:00
struct clock_event_device * evt = & ns9360_clockevent_device ;
2007-02-16 15:36:55 +01:00
2007-08-07 21:08:21 +01:00
/* clear irq */
2007-09-30 20:36:33 +01:00
tc = __raw_readl ( SYS_TC ( timerno ) ) ;
2007-08-07 21:08:21 +01:00
if ( REGGET ( tc , SYS_TCx , REN ) = = SYS_TCx_REN_DIS ) {
REGSET ( tc , SYS_TCx , TEN , DIS ) ;
2007-09-30 20:36:33 +01:00
__raw_writel ( tc , SYS_TC ( timerno ) ) ;
2007-08-07 21:08:21 +01:00
}
REGSET ( tc , SYS_TCx , INTC , SET ) ;
2007-09-30 20:36:33 +01:00
__raw_writel ( tc , SYS_TC ( timerno ) ) ;
2007-08-07 21:08:21 +01:00
REGSET ( tc , SYS_TCx , INTC , UNSET ) ;
2007-09-30 20:36:33 +01:00
__raw_writel ( tc , SYS_TC ( timerno ) ) ;
2007-08-07 21:08:21 +01:00
2007-09-30 20:36:00 +01:00
evt - > event_handler ( evt ) ;
2007-02-16 15:36:55 +01:00
2007-09-30 20:36:00 +01:00
return IRQ_HANDLED ;
2007-09-30 20:35:48 +01:00
}
2008-02-15 08:41:06 +01:00
static struct irqaction ns9360_clockevent_action = {
. name = " ns9360-timer " __stringify ( TIMER_CLOCKEVENT ) ,
2007-09-30 20:36:00 +01:00
. flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL ,
2008-02-15 08:41:06 +01:00
. handler = ns9360_clockevent_handler ,
2007-09-30 20:35:48 +01:00
} ;
2008-02-15 08:41:06 +01:00
static void __init ns9360_timer_init ( void )
2007-02-16 15:36:55 +01:00
{
int tc ;
2007-09-30 20:36:33 +01:00
tc = __raw_readl ( SYS_TC ( TIMER_CLOCKSOURCE ) ) ;
2007-09-30 20:35:48 +01:00
if ( REGGET ( tc , SYS_TCx , TEN ) ) {
REGSET ( tc , SYS_TCx , TEN , DIS ) ;
2007-09-30 20:36:33 +01:00
__raw_writel ( tc , SYS_TC ( TIMER_CLOCKSOURCE ) ) ;
2007-09-30 20:35:48 +01:00
}
2007-09-30 20:36:33 +01:00
__raw_writel ( 0 , SYS_TRC ( TIMER_CLOCKSOURCE ) ) ;
2007-09-30 20:35:48 +01:00
REGSET ( tc , SYS_TCx , TEN , EN ) ;
REGSET ( tc , SYS_TCx , TDBG , STOP ) ;
REGSET ( tc , SYS_TCx , TLCS , CPU ) ;
REGSET ( tc , SYS_TCx , TM , IEE ) ;
REGSET ( tc , SYS_TCx , INTS , DIS ) ;
REGSET ( tc , SYS_TCx , UDS , UP ) ;
REGSET ( tc , SYS_TCx , TSZ , 32 ) ;
REGSET ( tc , SYS_TCx , REN , EN ) ;
2007-09-30 20:36:33 +01:00
__raw_writel ( tc , SYS_TC ( TIMER_CLOCKSOURCE ) ) ;
2007-09-30 20:35:48 +01:00
2008-02-15 08:41:06 +01:00
ns9360_clocksource . mult = clocksource_hz2mult ( ns9360_cpuclock ( ) ,
ns9360_clocksource . shift ) ;
2007-09-30 20:35:48 +01:00
2008-02-15 08:41:06 +01:00
clocksource_register ( & ns9360_clocksource ) ;
2007-09-30 20:36:00 +01:00
2008-02-15 08:41:06 +01:00
latch = SH_DIV ( ns9360_cpuclock ( ) , HZ , 0 ) ;
2007-09-30 20:36:00 +01:00
2007-09-30 20:36:33 +01:00
tc = __raw_readl ( SYS_TC ( TIMER_CLOCKEVENT ) ) ;
2007-09-30 20:36:00 +01:00
REGSET ( tc , SYS_TCx , TEN , DIS ) ;
REGSET ( tc , SYS_TCx , TDBG , STOP ) ;
REGSET ( tc , SYS_TCx , TLCS , CPU ) ;
REGSET ( tc , SYS_TCx , TM , IEE ) ;
REGSET ( tc , SYS_TCx , INTS , DIS ) ;
REGSET ( tc , SYS_TCx , UDS , DOWN ) ;
REGSET ( tc , SYS_TCx , TSZ , 32 ) ;
REGSET ( tc , SYS_TCx , REN , EN ) ;
2007-09-30 20:36:33 +01:00
__raw_writel ( tc , SYS_TC ( TIMER_CLOCKEVENT ) ) ;
2007-09-30 20:36:00 +01:00
2008-02-15 08:41:06 +01:00
ns9360_clockevent_device . mult = div_sc ( ns9360_cpuclock ( ) ,
NSEC_PER_SEC , ns9360_clockevent_device . shift ) ;
ns9360_clockevent_device . max_delta_ns =
clockevent_delta2ns ( - 1 , & ns9360_clockevent_device ) ;
ns9360_clockevent_device . min_delta_ns =
clockevent_delta2ns ( 1 , & ns9360_clockevent_device ) ;
2007-09-30 20:36:00 +01:00
2008-12-13 21:20:26 +10:30
ns9360_clockevent_device . cpumask = cpumask_of ( 0 ) ;
2008-02-15 08:41:06 +01:00
clockevents_register_device ( & ns9360_clockevent_device ) ;
2007-09-30 20:36:00 +01:00
2008-02-15 08:41:06 +01:00
setup_irq ( IRQ_NS9360_TIMER0 + TIMER_CLOCKEVENT ,
& ns9360_clockevent_action ) ;
2007-02-16 15:36:55 +01:00
}
2008-02-15 08:41:06 +01:00
struct sys_timer ns9360_timer = {
. init = ns9360_timer_init ,
2007-02-16 15:36:55 +01:00
} ;