2007-04-30 22:37:19 +04:00
/*
* DaVinci timer subsystem
*
* Author : Kevin Hilman , MontaVista Software , Inc . < source @ mvista . com >
*
* 2007 ( c ) MontaVista Software , Inc . This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed " as is " without any warranty of any kind , whether express
* or implied .
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/interrupt.h>
# include <linux/clocksource.h>
# include <linux/clockchips.h>
# include <linux/spinlock.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2009-04-14 16:04:16 +04:00
# include <linux/clk.h>
# include <linux/err.h>
# include <linux/device.h>
2009-04-30 03:23:59 +04:00
# include <linux/platform_device.h>
2007-04-30 22:37:19 +04:00
2008-08-05 19:14:15 +04:00
# include <mach/hardware.h>
2007-04-30 22:37:19 +04:00
# include <asm/system.h>
# include <asm/irq.h>
# include <asm/mach/irq.h>
# include <asm/mach/time.h>
# include <asm/errno.h>
2008-08-05 19:14:15 +04:00
# include <mach/io.h>
2009-04-14 16:04:16 +04:00
# include <mach/cputype.h>
2009-04-15 23:40:11 +04:00
# include <mach/time.h>
2009-04-14 16:04:16 +04:00
# include "clock.h"
2007-04-30 22:37:19 +04:00
static struct clock_event_device clockevent_davinci ;
2009-04-14 16:06:37 +04:00
static unsigned int davinci_clock_tick_rate ;
2007-04-30 22:37:19 +04:00
/*
* This driver configures the 2 64 - bit count - up timers as 4 independent
* 32 - bit count - up timers used as follows :
*/
2009-04-15 23:40:11 +04:00
enum {
TID_CLOCKEVENT ,
TID_CLOCKSOURCE ,
} ;
2007-04-30 22:37:19 +04:00
/* Timer register offsets */
# define PID12 0x0
# define TIM12 0x10
# define TIM34 0x14
# define PRD12 0x18
# define PRD34 0x1c
# define TCR 0x20
# define TGCR 0x24
# define WDTCR 0x28
/* Timer register bitfields */
# define TCR_ENAMODE_DISABLE 0x0
# define TCR_ENAMODE_ONESHOT 0x1
# define TCR_ENAMODE_PERIODIC 0x2
# define TCR_ENAMODE_MASK 0x3
# define TGCR_TIMMODE_SHIFT 2
# define TGCR_TIMMODE_64BIT_GP 0x0
# define TGCR_TIMMODE_32BIT_UNCHAINED 0x1
# define TGCR_TIMMODE_64BIT_WDOG 0x2
# define TGCR_TIMMODE_32BIT_CHAINED 0x3
# define TGCR_TIM12RS_SHIFT 0
# define TGCR_TIM34RS_SHIFT 1
# define TGCR_RESET 0x0
# define TGCR_UNRESET 0x1
# define TGCR_RESET_MASK 0x3
# define WDTCR_WDEN_SHIFT 14
# define WDTCR_WDEN_DISABLE 0x0
# define WDTCR_WDEN_ENABLE 0x1
# define WDTCR_WDKEY_SHIFT 16
# define WDTCR_WDKEY_SEQ0 0xa5c6
# define WDTCR_WDKEY_SEQ1 0xda7e
struct timer_s {
char * name ;
unsigned int id ;
unsigned long period ;
unsigned long opts ;
2009-04-14 16:04:16 +04:00
void __iomem * base ;
unsigned long tim_off ;
unsigned long prd_off ;
2007-04-30 22:37:19 +04:00
unsigned long enamode_shift ;
struct irqaction irqaction ;
} ;
static struct timer_s timers [ ] ;
/* values for 'opts' field of struct timer_s */
# define TIMER_OPTS_DISABLED 0x00
# define TIMER_OPTS_ONESHOT 0x01
# define TIMER_OPTS_PERIODIC 0x02
2009-04-15 23:40:11 +04:00
static char * id_to_name [ ] = {
[ T0_BOT ] = " timer0_0 " ,
[ T0_TOP ] = " timer0_1 " ,
[ T1_BOT ] = " timer1_0 " ,
[ T1_TOP ] = " timer1_1 " ,
} ;
2007-04-30 22:37:19 +04:00
static int timer32_config ( struct timer_s * t )
{
2009-04-14 16:04:16 +04:00
u32 tcr = __raw_readl ( t - > base + TCR ) ;
2007-04-30 22:37:19 +04:00
/* disable timer */
tcr & = ~ ( TCR_ENAMODE_MASK < < t - > enamode_shift ) ;
2009-04-14 16:04:16 +04:00
__raw_writel ( tcr , t - > base + TCR ) ;
2007-04-30 22:37:19 +04:00
/* reset counter to zero, set new period */
2009-04-14 16:04:16 +04:00
__raw_writel ( 0 , t - > base + t - > tim_off ) ;
__raw_writel ( t - > period , t - > base + t - > prd_off ) ;
2007-04-30 22:37:19 +04:00
/* Set enable mode */
if ( t - > opts & TIMER_OPTS_ONESHOT ) {
tcr | = TCR_ENAMODE_ONESHOT < < t - > enamode_shift ;
} else if ( t - > opts & TIMER_OPTS_PERIODIC ) {
tcr | = TCR_ENAMODE_PERIODIC < < t - > enamode_shift ;
}
2009-04-14 16:04:16 +04:00
__raw_writel ( tcr , t - > base + TCR ) ;
2007-04-30 22:37:19 +04:00
return 0 ;
}
static inline u32 timer32_read ( struct timer_s * t )
{
2009-04-14 16:04:16 +04:00
return __raw_readl ( t - > base + t - > tim_off ) ;
2007-04-30 22:37:19 +04:00
}
static irqreturn_t timer_interrupt ( int irq , void * dev_id )
{
struct clock_event_device * evt = & clockevent_davinci ;
evt - > event_handler ( evt ) ;
return IRQ_HANDLED ;
}
/* called when 32-bit counter wraps */
static irqreturn_t freerun_interrupt ( int irq , void * dev_id )
{
return IRQ_HANDLED ;
}
static struct timer_s timers [ ] = {
[ TID_CLOCKEVENT ] = {
. name = " clockevent " ,
. opts = TIMER_OPTS_DISABLED ,
. irqaction = {
. flags = IRQF_DISABLED | IRQF_TIMER ,
. handler = timer_interrupt ,
}
} ,
[ TID_CLOCKSOURCE ] = {
. name = " free-run counter " ,
. period = ~ 0 ,
. opts = TIMER_OPTS_PERIODIC ,
. irqaction = {
. flags = IRQF_DISABLED | IRQF_TIMER ,
. handler = freerun_interrupt ,
}
} ,
} ;
static void __init timer_init ( void )
{
2009-04-15 23:40:11 +04:00
struct davinci_soc_info * soc_info = & davinci_soc_info ;
struct davinci_timer_instance * dtip = soc_info - > timer_info - > timers ;
2007-04-30 22:37:19 +04:00
int i ;
/* Global init of each 64-bit timer as a whole */
for ( i = 0 ; i < 2 ; i + + ) {
2009-04-14 16:04:16 +04:00
u32 tgcr ;
2009-04-15 23:40:11 +04:00
void __iomem * base = dtip [ i ] . base ;
2007-04-30 22:37:19 +04:00
/* Disabled, Internal clock source */
2009-04-14 16:04:16 +04:00
__raw_writel ( 0 , base + TCR ) ;
2007-04-30 22:37:19 +04:00
/* reset both timers, no pre-scaler for timer34 */
tgcr = 0 ;
2009-04-14 16:04:16 +04:00
__raw_writel ( tgcr , base + TGCR ) ;
2007-04-30 22:37:19 +04:00
/* Set both timers to unchained 32-bit */
tgcr = TGCR_TIMMODE_32BIT_UNCHAINED < < TGCR_TIMMODE_SHIFT ;
2009-04-14 16:04:16 +04:00
__raw_writel ( tgcr , base + TGCR ) ;
2007-04-30 22:37:19 +04:00
/* Unreset timers */
tgcr | = ( TGCR_UNRESET < < TGCR_TIM12RS_SHIFT ) |
( TGCR_UNRESET < < TGCR_TIM34RS_SHIFT ) ;
2009-04-14 16:04:16 +04:00
__raw_writel ( tgcr , base + TGCR ) ;
2007-04-30 22:37:19 +04:00
/* Init both counters to zero */
2009-04-14 16:04:16 +04:00
__raw_writel ( 0 , base + TIM12 ) ;
__raw_writel ( 0 , base + TIM34 ) ;
2007-04-30 22:37:19 +04:00
}
/* Init of each timer as a 32-bit timer */
for ( i = 0 ; i < ARRAY_SIZE ( timers ) ; i + + ) {
struct timer_s * t = & timers [ i ] ;
2009-04-15 23:40:11 +04:00
int timer = ID_TO_TIMER ( t - > id ) ;
u32 irq ;
t - > base = dtip [ timer ] . base ;
if ( IS_TIMER_BOT ( t - > id ) ) {
t - > enamode_shift = 6 ;
t - > tim_off = TIM12 ;
t - > prd_off = PRD12 ;
irq = dtip [ timer ] . bottom_irq ;
} else {
t - > enamode_shift = 22 ;
t - > tim_off = TIM34 ;
t - > prd_off = PRD34 ;
irq = dtip [ timer ] . top_irq ;
2007-04-30 22:37:19 +04:00
}
2009-04-15 23:40:11 +04:00
/* Register interrupt */
t - > irqaction . name = t - > name ;
t - > irqaction . dev_id = ( void * ) t ;
if ( t - > irqaction . handler ! = NULL )
setup_irq ( irq , & t - > irqaction ) ;
timer32_config ( & timers [ i ] ) ;
2007-04-30 22:37:19 +04:00
}
}
/*
* clocksource
*/
2009-04-21 23:24:00 +04:00
static cycle_t read_cycles ( struct clocksource * cs )
2007-04-30 22:37:19 +04:00
{
struct timer_s * t = & timers [ TID_CLOCKSOURCE ] ;
return ( cycles_t ) timer32_read ( t ) ;
}
static struct clocksource clocksource_davinci = {
. rating = 300 ,
. read = read_cycles ,
. mask = CLOCKSOURCE_MASK ( 32 ) ,
. shift = 24 ,
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
} ;
/*
* clockevent
*/
static int davinci_set_next_event ( unsigned long cycles ,
struct clock_event_device * evt )
{
struct timer_s * t = & timers [ TID_CLOCKEVENT ] ;
t - > period = cycles ;
timer32_config ( t ) ;
return 0 ;
}
static void davinci_set_mode ( enum clock_event_mode mode ,
struct clock_event_device * evt )
{
struct timer_s * t = & timers [ TID_CLOCKEVENT ] ;
switch ( mode ) {
case CLOCK_EVT_MODE_PERIODIC :
2009-04-14 16:06:37 +04:00
t - > period = davinci_clock_tick_rate / ( HZ ) ;
2007-04-30 22:37:19 +04:00
t - > opts = TIMER_OPTS_PERIODIC ;
timer32_config ( t ) ;
break ;
case CLOCK_EVT_MODE_ONESHOT :
t - > opts = TIMER_OPTS_ONESHOT ;
break ;
case CLOCK_EVT_MODE_UNUSED :
case CLOCK_EVT_MODE_SHUTDOWN :
t - > opts = TIMER_OPTS_DISABLED ;
break ;
2007-07-21 15:37:34 +04:00
case CLOCK_EVT_MODE_RESUME :
break ;
2007-04-30 22:37:19 +04:00
}
}
static struct clock_event_device clockevent_davinci = {
. features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT ,
. shift = 32 ,
. set_next_event = davinci_set_next_event ,
. set_mode = davinci_set_mode ,
} ;
static void __init davinci_timer_init ( void )
{
2009-04-14 16:06:37 +04:00
struct clk * timer_clk ;
2009-04-15 23:40:11 +04:00
struct davinci_soc_info * soc_info = & davinci_soc_info ;
2009-04-14 16:06:37 +04:00
2007-04-30 22:37:19 +04:00
static char err [ ] __initdata = KERN_ERR
" %s: can't register clocksource! \n " ;
2009-04-15 23:40:11 +04:00
timers [ TID_CLOCKEVENT ] . id = soc_info - > timer_info - > clockevent_id ;
timers [ TID_CLOCKSOURCE ] . id = soc_info - > timer_info - > clocksource_id ;
2007-04-30 22:37:19 +04:00
/* init timer hw */
timer_init ( ) ;
2009-04-14 16:06:37 +04:00
timer_clk = clk_get ( NULL , " timer0 " ) ;
BUG_ON ( IS_ERR ( timer_clk ) ) ;
clk_enable ( timer_clk ) ;
davinci_clock_tick_rate = clk_get_rate ( timer_clk ) ;
2007-04-30 22:37:19 +04:00
/* setup clocksource */
2009-04-15 23:40:11 +04:00
clocksource_davinci . name = id_to_name [ timers [ TID_CLOCKSOURCE ] . id ] ;
2007-04-30 22:37:19 +04:00
clocksource_davinci . mult =
2009-04-14 16:06:37 +04:00
clocksource_khz2mult ( davinci_clock_tick_rate / 1000 ,
2007-04-30 22:37:19 +04:00
clocksource_davinci . shift ) ;
if ( clocksource_register ( & clocksource_davinci ) )
printk ( err , clocksource_davinci . name ) ;
/* setup clockevent */
2009-04-15 23:40:11 +04:00
clockevent_davinci . name = id_to_name [ timers [ TID_CLOCKEVENT ] . id ] ;
2009-04-14 16:06:37 +04:00
clockevent_davinci . mult = div_sc ( davinci_clock_tick_rate , NSEC_PER_SEC ,
2007-04-30 22:37:19 +04:00
clockevent_davinci . shift ) ;
clockevent_davinci . max_delta_ns =
clockevent_delta2ns ( 0xfffffffe , & clockevent_davinci ) ;
clockevent_davinci . min_delta_ns =
clockevent_delta2ns ( 1 , & clockevent_davinci ) ;
2008-12-13 13:50:26 +03:00
clockevent_davinci . cpumask = cpumask_of ( 0 ) ;
2007-04-30 22:37:19 +04:00
clockevents_register_device ( & clockevent_davinci ) ;
}
struct sys_timer davinci_timer = {
. init = davinci_timer_init ,
} ;
/* reset board using watchdog timer */
2009-04-30 03:23:59 +04:00
void davinci_watchdog_reset ( void )
{
2009-04-14 16:04:16 +04:00
u32 tgcr , wdtcr ;
2009-04-15 23:40:21 +04:00
struct davinci_soc_info * soc_info = & davinci_soc_info ;
void __iomem * base = soc_info - > wdt_base ;
2009-04-14 16:06:37 +04:00
struct clk * wd_clk ;
2009-04-30 03:23:59 +04:00
wd_clk = clk_get ( & davinci_wdt_device . dev , NULL ) ;
2009-04-14 16:06:37 +04:00
if ( WARN_ON ( IS_ERR ( wd_clk ) ) )
return ;
clk_enable ( wd_clk ) ;
2007-04-30 22:37:19 +04:00
/* disable, internal clock source */
2009-04-14 16:04:16 +04:00
__raw_writel ( 0 , base + TCR ) ;
2007-04-30 22:37:19 +04:00
/* reset timer, set mode to 64-bit watchdog, and unreset */
tgcr = 0 ;
2009-04-14 16:04:16 +04:00
__raw_writel ( tgcr , base + TCR ) ;
2007-04-30 22:37:19 +04:00
tgcr = TGCR_TIMMODE_64BIT_WDOG < < TGCR_TIMMODE_SHIFT ;
tgcr | = ( TGCR_UNRESET < < TGCR_TIM12RS_SHIFT ) |
( TGCR_UNRESET < < TGCR_TIM34RS_SHIFT ) ;
2009-04-14 16:04:16 +04:00
__raw_writel ( tgcr , base + TCR ) ;
2007-04-30 22:37:19 +04:00
/* clear counter and period regs */
2009-04-14 16:04:16 +04:00
__raw_writel ( 0 , base + TIM12 ) ;
__raw_writel ( 0 , base + TIM34 ) ;
__raw_writel ( 0 , base + PRD12 ) ;
__raw_writel ( 0 , base + PRD34 ) ;
2007-04-30 22:37:19 +04:00
/* enable */
2009-04-14 16:04:16 +04:00
wdtcr = __raw_readl ( base + WDTCR ) ;
2007-04-30 22:37:19 +04:00
wdtcr | = WDTCR_WDEN_ENABLE < < WDTCR_WDEN_SHIFT ;
2009-04-14 16:04:16 +04:00
__raw_writel ( wdtcr , base + WDTCR ) ;
2007-04-30 22:37:19 +04:00
/* put watchdog in pre-active state */
wdtcr = ( WDTCR_WDKEY_SEQ0 < < WDTCR_WDKEY_SHIFT ) |
( WDTCR_WDEN_ENABLE < < WDTCR_WDEN_SHIFT ) ;
2009-04-14 16:04:16 +04:00
__raw_writel ( wdtcr , base + WDTCR ) ;
2007-04-30 22:37:19 +04:00
/* put watchdog in active state */
wdtcr = ( WDTCR_WDKEY_SEQ1 < < WDTCR_WDKEY_SHIFT ) |
( WDTCR_WDEN_ENABLE < < WDTCR_WDEN_SHIFT ) ;
2009-04-14 16:04:16 +04:00
__raw_writel ( wdtcr , base + WDTCR ) ;
2007-04-30 22:37:19 +04:00
/* write an invalid value to the WDKEY field to trigger
* a watchdog reset */
wdtcr = 0x00004000 ;
2009-04-14 16:04:16 +04:00
__raw_writel ( wdtcr , base + WDTCR ) ;
2007-04-30 22:37:19 +04:00
}