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>
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>
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/mach/irq.h>
# include <asm/mach/time.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 */
2009-04-15 23:41:54 +04:00
# define PID12 0x0
# define TIM12 0x10
# define TIM34 0x14
# define PRD12 0x18
# define PRD34 0x1c
# define TCR 0x20
# define TGCR 0x24
# define WDTCR 0x28
/* Offsets of the 8 compare registers */
# define CMP12_0 0x60
# define CMP12_1 0x64
# define CMP12_2 0x68
# define CMP12_3 0x6c
# define CMP12_4 0x70
# define CMP12_5 0x74
# define CMP12_6 0x78
# define CMP12_7 0x7c
2007-04-30 22:37:19 +04:00
/* 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-15 23:41:54 +04:00
unsigned long flags ;
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 */
2009-04-15 23:41:54 +04:00
# define TIMER_OPTS_DISABLED 0x01
# define TIMER_OPTS_ONESHOT 0x02
# define TIMER_OPTS_PERIODIC 0x04
# define TIMER_OPTS_STATE_MASK 0x07
# define TIMER_OPTS_USE_COMPARE 0x80000000
# define USING_COMPARE(t) ((t)->opts & TIMER_OPTS_USE_COMPARE)
2007-04-30 22:37:19 +04:00
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-15 23:41:54 +04:00
u32 tcr ;
2009-04-15 23:42:06 +04:00
struct davinci_soc_info * soc_info = & davinci_soc_info ;
2009-04-15 23:41:54 +04:00
if ( USING_COMPARE ( t ) ) {
struct davinci_timer_instance * dtip =
soc_info - > timer_info - > timers ;
int event_timer = ID_TO_TIMER ( timers [ TID_CLOCKEVENT ] . id ) ;
/*
* Next interrupt should be the current time reg value plus
* the new period ( using 32 - bit unsigned addition / wrapping
* to 0 on overflow ) . This assumes that the clocksource
* is setup to count to 2 ^ 32 - 1 before wrapping around to 0.
*/
__raw_writel ( __raw_readl ( t - > base + t - > tim_off ) + t - > period ,
t - > base + dtip [ event_timer ] . cmp_off ) ;
} else {
tcr = __raw_readl ( t - > base + TCR ) ;
/* disable timer */
tcr & = ~ ( TCR_ENAMODE_MASK < < t - > enamode_shift ) ;
__raw_writel ( tcr , t - > base + TCR ) ;
/* reset counter to zero, set new period */
__raw_writel ( 0 , t - > base + t - > tim_off ) ;
__raw_writel ( t - > period , t - > base + t - > prd_off ) ;
/* 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 ;
__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 ;
2010-05-08 01:06:35 +04:00
void __iomem * base [ 2 ] ;
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 ;
2010-05-08 01:06:35 +04:00
base [ i ] = ioremap ( dtip [ i ] . base , SZ_4K ) ;
if ( WARN_ON ( ! base [ i ] ) )
continue ;
2007-04-30 22:37:19 +04:00
/* Disabled, Internal clock source */
2010-05-08 01:06:35 +04:00
__raw_writel ( 0 , base [ i ] + TCR ) ;
2007-04-30 22:37:19 +04:00
/* reset both timers, no pre-scaler for timer34 */
tgcr = 0 ;
2010-05-08 01:06:35 +04:00
__raw_writel ( tgcr , base [ i ] + TGCR ) ;
2007-04-30 22:37:19 +04:00
/* Set both timers to unchained 32-bit */
tgcr = TGCR_TIMMODE_32BIT_UNCHAINED < < TGCR_TIMMODE_SHIFT ;
2010-05-08 01:06:35 +04:00
__raw_writel ( tgcr , base [ i ] + TGCR ) ;
2007-04-30 22:37:19 +04:00
/* Unreset timers */
tgcr | = ( TGCR_UNRESET < < TGCR_TIM12RS_SHIFT ) |
( TGCR_UNRESET < < TGCR_TIM34RS_SHIFT ) ;
2010-05-08 01:06:35 +04:00
__raw_writel ( tgcr , base [ i ] + TGCR ) ;
2007-04-30 22:37:19 +04:00
/* Init both counters to zero */
2010-05-08 01:06:35 +04:00
__raw_writel ( 0 , base [ i ] + TIM12 ) ;
__raw_writel ( 0 , base [ i ] + 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 ;
2010-05-08 01:06:35 +04:00
t - > base = base [ timer ] ;
if ( ! t - > base )
continue ;
2009-04-15 23:40:11 +04:00
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 ;
2009-04-15 23:41:54 +04:00
if ( t - > irqaction . handler ! = NULL ) {
irq = USING_COMPARE ( t ) ? dtip [ i ] . cmp_irq : irq ;
2009-04-15 23:40:11 +04:00
setup_irq ( irq , & t - > irqaction ) ;
2009-04-15 23:41:54 +04:00
}
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 ) ;
}
2010-10-06 12:38:55 +04:00
/*
* Kernel assumes that sched_clock can be called early but may not have
* things ready yet .
*/
static cycle_t read_dummy ( struct clocksource * cs )
{
return 0 ;
}
2007-04-30 22:37:19 +04:00
static struct clocksource clocksource_davinci = {
. rating = 300 ,
2010-10-06 12:38:55 +04:00
. read = read_dummy ,
2007-04-30 22:37:19 +04:00
. mask = CLOCKSOURCE_MASK ( 32 ) ,
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
} ;
2010-10-06 12:38:55 +04:00
/*
* Overwrite weak default sched_clock with something more precise
*/
unsigned long long notrace sched_clock ( void )
{
const cycle_t cyc = clocksource_davinci . read ( & clocksource_davinci ) ;
return clocksource_cyc2ns ( cyc , clocksource_davinci . mult ,
clocksource_davinci . shift ) ;
}
2007-04-30 22:37:19 +04:00
/*
* 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 ) ;
2009-04-15 23:41:54 +04:00
t - > opts & = ~ TIMER_OPTS_STATE_MASK ;
t - > opts | = TIMER_OPTS_PERIODIC ;
2007-04-30 22:37:19 +04:00
timer32_config ( t ) ;
break ;
case CLOCK_EVT_MODE_ONESHOT :
2009-04-15 23:41:54 +04:00
t - > opts & = ~ TIMER_OPTS_STATE_MASK ;
t - > opts | = TIMER_OPTS_ONESHOT ;
2007-04-30 22:37:19 +04:00
break ;
case CLOCK_EVT_MODE_UNUSED :
case CLOCK_EVT_MODE_SHUTDOWN :
2009-04-15 23:41:54 +04:00
t - > opts & = ~ TIMER_OPTS_STATE_MASK ;
t - > opts | = TIMER_OPTS_DISABLED ;
2007-04-30 22:37:19 +04:00
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-15 23:41:54 +04:00
unsigned int clockevent_id ;
unsigned int clocksource_id ;
2007-04-30 22:37:19 +04:00
static char err [ ] __initdata = KERN_ERR
" %s: can't register clocksource! \n " ;
2010-03-12 01:57:35 +03:00
int i ;
2007-04-30 22:37:19 +04:00
2009-04-15 23:41:54 +04:00
clockevent_id = soc_info - > timer_info - > clockevent_id ;
clocksource_id = soc_info - > timer_info - > clocksource_id ;
timers [ TID_CLOCKEVENT ] . id = clockevent_id ;
timers [ TID_CLOCKSOURCE ] . id = clocksource_id ;
/*
* If using same timer for both clock events & clocksource ,
* a compare register must be used to generate an event interrupt .
* This is equivalent to a oneshot timer only ( not periodic ) .
*/
if ( clockevent_id = = clocksource_id ) {
struct davinci_timer_instance * dtip =
soc_info - > timer_info - > timers ;
int event_timer = ID_TO_TIMER ( clockevent_id ) ;
/* Only bottom timers can use compare regs */
if ( IS_TIMER_TOP ( clockevent_id ) )
pr_warning ( " davinci_timer_init: Invalid use "
" of system timers. Results unpredictable. \n " ) ;
else if ( ( dtip [ event_timer ] . cmp_off = = 0 )
| | ( dtip [ event_timer ] . cmp_irq = = 0 ) )
pr_warning ( " davinci_timer_init: Invalid timer instance "
" setup. Results unpredictable. \n " ) ;
else {
timers [ TID_CLOCKEVENT ] . opts | = TIMER_OPTS_USE_COMPARE ;
clockevent_davinci . features = CLOCK_EVT_FEAT_ONESHOT ;
}
}
2009-04-15 23:40:11 +04:00
2009-04-14 16:06:37 +04:00
timer_clk = clk_get ( NULL , " timer0 " ) ;
BUG_ON ( IS_ERR ( timer_clk ) ) ;
clk_enable ( timer_clk ) ;
2010-03-26 00:43:45 +03:00
/* init timer hw */
timer_init ( ) ;
2009-04-14 16:06:37 +04:00
davinci_clock_tick_rate = clk_get_rate ( timer_clk ) ;
2007-04-30 22:37:19 +04:00
/* setup clocksource */
2010-10-06 12:38:55 +04:00
clocksource_davinci . read = read_cycles ;
2009-04-15 23:41:54 +04:00
clocksource_davinci . name = id_to_name [ clocksource_id ] ;
2010-12-13 16:17:12 +03:00
if ( clocksource_register_hz ( & clocksource_davinci ,
davinci_clock_tick_rate ) )
2007-04-30 22:37:19 +04:00
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 ) ;
2009-04-15 23:41:54 +04:00
clockevent_davinci . min_delta_ns = 50000 ; /* 50 usec */
2007-04-30 22:37:19 +04:00
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 ) ;
2010-03-12 01:57:35 +03:00
for ( i = 0 ; i < ARRAY_SIZE ( timers ) ; i + + )
timer32_config ( & timers [ i ] ) ;
2007-04-30 22:37:19 +04:00
}
struct sys_timer davinci_timer = {
. init = davinci_timer_init ,
} ;
/* reset board using watchdog timer */
2010-05-02 02:38:28 +04:00
void davinci_watchdog_reset ( struct platform_device * pdev )
2009-04-30 03:23:59 +04:00
{
2009-04-14 16:04:16 +04:00
u32 tgcr , wdtcr ;
2010-05-02 02:38:28 +04:00
void __iomem * base ;
2009-04-14 16:06:37 +04:00
struct clk * wd_clk ;
2010-05-02 02:38:28 +04:00
base = ioremap ( pdev - > resource [ 0 ] . start , SZ_4K ) ;
if ( WARN_ON ( ! base ) )
return ;
2009-06-03 23:24:50 +04:00
wd_clk = clk_get ( & pdev - > 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-06-01 22:41:54 +04:00
__raw_writel ( tgcr , base + TGCR ) ;
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-06-01 22:41:54 +04:00
__raw_writel ( tgcr , base + TGCR ) ;
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
/* put watchdog in pre-active state */
2009-06-01 22:41:54 +04:00
wdtcr = __raw_readl ( base + WDTCR ) ;
2007-04-30 22:37:19 +04:00
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
}