2007-10-24 20:34:09 +04:00
/*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
* Based on linux / arch / mips / kernel / cevt - r4k . c ,
* linux / arch / mips / jmr3927 / rbhma3100 / setup . c
*
* Copyright 2001 MontaVista Software Inc .
* Copyright ( C ) 2000 - 2001 Toshiba Corporation
* Copyright ( C ) 2007 MIPS Technologies , Inc .
* Copyright ( C ) 2007 Ralf Baechle < ralf @ linux - mips . org >
*/
# include <linux/init.h>
# include <linux/interrupt.h>
2010-10-07 17:08:54 +04:00
# include <linux/irq.h>
2007-10-24 20:34:09 +04:00
# include <asm/time.h>
# include <asm/txx9tmr.h>
# define TCR_BASE (TXx9_TMTCR_CCDE | TXx9_TMTCR_CRE | TXx9_TMTCR_TMODE_ITVL)
# define TIMER_CCD 0 /* 1/2 */
# define TIMER_CLK(imclk) ((imclk) / (2 << TIMER_CCD))
2009-04-23 19:10:36 +04:00
struct txx9_clocksource {
struct clocksource cs ;
struct txx9_tmr_reg __iomem * tmrptr ;
} ;
2007-10-24 20:34:09 +04:00
2009-04-21 23:24:00 +04:00
static cycle_t txx9_cs_read ( struct clocksource * cs )
2007-10-24 20:34:09 +04:00
{
2009-04-23 19:10:36 +04:00
struct txx9_clocksource * txx9_cs =
container_of ( cs , struct txx9_clocksource , cs ) ;
return __raw_readl ( & txx9_cs - > tmrptr - > trr ) ;
2007-10-24 20:34:09 +04:00
}
/* Use 1 bit smaller width to use full bits in that width */
# define TXX9_CLOCKSOURCE_BITS (TXX9_TIMER_BITS - 1)
2009-04-23 19:10:36 +04:00
static struct txx9_clocksource txx9_clocksource = {
. cs = {
. name = " TXx9 " ,
. rating = 200 ,
. read = txx9_cs_read ,
. mask = CLOCKSOURCE_MASK ( TXX9_CLOCKSOURCE_BITS ) ,
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
} ,
2007-10-24 20:34:09 +04:00
} ;
void __init txx9_clocksource_init ( unsigned long baseaddr ,
unsigned int imbusclk )
{
struct txx9_tmr_reg __iomem * tmrptr ;
2010-04-27 07:23:11 +04:00
clocksource_register_hz ( & txx9_clocksource . cs , TIMER_CLK ( imbusclk ) ) ;
2007-10-24 20:34:09 +04:00
tmrptr = ioremap ( baseaddr , sizeof ( struct txx9_tmr_reg ) ) ;
__raw_writel ( TCR_BASE , & tmrptr - > tcr ) ;
__raw_writel ( 0 , & tmrptr - > tisr ) ;
__raw_writel ( TIMER_CCD , & tmrptr - > ccdr ) ;
__raw_writel ( TXx9_TMITMR_TZCE , & tmrptr - > itmr ) ;
__raw_writel ( 1 < < TXX9_CLOCKSOURCE_BITS , & tmrptr - > cpra ) ;
__raw_writel ( TCR_BASE | TXx9_TMTCR_TCE , & tmrptr - > tcr ) ;
2009-04-23 19:10:36 +04:00
txx9_clocksource . tmrptr = tmrptr ;
2007-10-24 20:34:09 +04:00
}
2009-04-23 19:10:36 +04:00
struct txx9_clock_event_device {
struct clock_event_device cd ;
struct txx9_tmr_reg __iomem * tmrptr ;
} ;
2007-10-24 20:34:09 +04:00
static void txx9tmr_stop_and_clear ( struct txx9_tmr_reg __iomem * tmrptr )
{
/* stop and reset counter */
__raw_writel ( TCR_BASE , & tmrptr - > tcr ) ;
/* clear pending interrupt */
__raw_writel ( 0 , & tmrptr - > tisr ) ;
}
static void txx9tmr_set_mode ( enum clock_event_mode mode ,
struct clock_event_device * evt )
{
2009-04-23 19:10:36 +04:00
struct txx9_clock_event_device * txx9_cd =
container_of ( evt , struct txx9_clock_event_device , cd ) ;
struct txx9_tmr_reg __iomem * tmrptr = txx9_cd - > tmrptr ;
2007-10-24 20:34:09 +04:00
txx9tmr_stop_and_clear ( tmrptr ) ;
switch ( mode ) {
case CLOCK_EVT_MODE_PERIODIC :
__raw_writel ( TXx9_TMITMR_TIIE | TXx9_TMITMR_TZCE ,
& tmrptr - > itmr ) ;
/* start timer */
__raw_writel ( ( ( u64 ) ( NSEC_PER_SEC / HZ ) * evt - > mult ) > >
evt - > shift ,
& tmrptr - > cpra ) ;
__raw_writel ( TCR_BASE | TXx9_TMTCR_TCE , & tmrptr - > tcr ) ;
break ;
case CLOCK_EVT_MODE_SHUTDOWN :
case CLOCK_EVT_MODE_UNUSED :
__raw_writel ( 0 , & tmrptr - > itmr ) ;
break ;
case CLOCK_EVT_MODE_ONESHOT :
__raw_writel ( TXx9_TMITMR_TIIE , & tmrptr - > itmr ) ;
break ;
case CLOCK_EVT_MODE_RESUME :
__raw_writel ( TIMER_CCD , & tmrptr - > ccdr ) ;
__raw_writel ( 0 , & tmrptr - > itmr ) ;
break ;
}
}
static int txx9tmr_set_next_event ( unsigned long delta ,
struct clock_event_device * evt )
{
2009-04-23 19:10:36 +04:00
struct txx9_clock_event_device * txx9_cd =
container_of ( evt , struct txx9_clock_event_device , cd ) ;
struct txx9_tmr_reg __iomem * tmrptr = txx9_cd - > tmrptr ;
2007-10-24 20:34:09 +04:00
txx9tmr_stop_and_clear ( tmrptr ) ;
/* start timer */
__raw_writel ( delta , & tmrptr - > cpra ) ;
__raw_writel ( TCR_BASE | TXx9_TMTCR_TCE , & tmrptr - > tcr ) ;
return 0 ;
}
2009-04-23 19:10:36 +04:00
static struct txx9_clock_event_device txx9_clock_event_device = {
. cd = {
. name = " TXx9 " ,
. features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT ,
. rating = 200 ,
. set_mode = txx9tmr_set_mode ,
. set_next_event = txx9tmr_set_next_event ,
} ,
2007-10-24 20:34:09 +04:00
} ;
static irqreturn_t txx9tmr_interrupt ( int irq , void * dev_id )
{
2009-04-23 19:10:36 +04:00
struct txx9_clock_event_device * txx9_cd = dev_id ;
struct clock_event_device * cd = & txx9_cd - > cd ;
struct txx9_tmr_reg __iomem * tmrptr = txx9_cd - > tmrptr ;
2007-10-24 20:34:09 +04:00
__raw_writel ( 0 , & tmrptr - > tisr ) ; /* ack interrupt */
cd - > event_handler ( cd ) ;
return IRQ_HANDLED ;
}
static struct irqaction txx9tmr_irq = {
. handler = txx9tmr_interrupt ,
2009-10-10 19:26:35 +04:00
. flags = IRQF_DISABLED | IRQF_PERCPU | IRQF_TIMER ,
2007-10-24 20:34:09 +04:00
. name = " txx9tmr " ,
2009-04-23 19:10:36 +04:00
. dev_id = & txx9_clock_event_device ,
2007-10-24 20:34:09 +04:00
} ;
void __init txx9_clockevent_init ( unsigned long baseaddr , int irq ,
unsigned int imbusclk )
{
2009-04-23 19:10:36 +04:00
struct clock_event_device * cd = & txx9_clock_event_device . cd ;
2007-10-24 20:34:09 +04:00
struct txx9_tmr_reg __iomem * tmrptr ;
tmrptr = ioremap ( baseaddr , sizeof ( struct txx9_tmr_reg ) ) ;
txx9tmr_stop_and_clear ( tmrptr ) ;
__raw_writel ( TIMER_CCD , & tmrptr - > ccdr ) ;
__raw_writel ( 0 , & tmrptr - > itmr ) ;
2009-04-23 19:10:36 +04:00
txx9_clock_event_device . tmrptr = tmrptr ;
2007-10-24 20:34:09 +04:00
clockevent_set_clock ( cd , TIMER_CLK ( imbusclk ) ) ;
cd - > max_delta_ns =
clockevent_delta2ns ( 0xffffffff > > ( 32 - TXX9_TIMER_BITS ) , cd ) ;
cd - > min_delta_ns = clockevent_delta2ns ( 0xf , cd ) ;
cd - > irq = irq ;
2008-12-13 13:50:26 +03:00
cd - > cpumask = cpumask_of ( 0 ) ,
2007-10-24 20:34:09 +04:00
clockevents_register_device ( cd ) ;
setup_irq ( irq , & txx9tmr_irq ) ;
printk ( KERN_INFO " TXx9: clockevent device at 0x%lx, irq %d \n " ,
baseaddr , irq ) ;
}
void __init txx9_tmr_init ( unsigned long baseaddr )
{
struct txx9_tmr_reg __iomem * tmrptr ;
tmrptr = ioremap ( baseaddr , sizeof ( struct txx9_tmr_reg ) ) ;
2008-06-24 18:26:38 +04:00
/* Start once to make CounterResetEnable effective */
__raw_writel ( TXx9_TMTCR_CRE | TXx9_TMTCR_TCE , & tmrptr - > tcr ) ;
/* Stop and reset the counter */
2007-10-24 20:34:09 +04:00
__raw_writel ( TXx9_TMTCR_CRE , & tmrptr - > tcr ) ;
__raw_writel ( 0 , & tmrptr - > tisr ) ;
__raw_writel ( 0xffffffff , & tmrptr - > cpra ) ;
__raw_writel ( 0 , & tmrptr - > itmr ) ;
__raw_writel ( 0 , & tmrptr - > ccdr ) ;
__raw_writel ( 0 , & tmrptr - > pgmr ) ;
iounmap ( tmrptr ) ;
}