2007-05-12 00:01:28 +04:00
/*
* arch / arm / mach - ks8695 / time . c
*
* Copyright ( C ) 2006 Ben Dooks < ben @ simtec . co . uk >
* Copyright ( C ) 2006 Simtec Electronics
*
* 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 .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/kernel.h>
# include <linux/sched.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2012-08-29 22:27:22 +04:00
# include <linux/clockchips.h>
2007-05-12 00:01:28 +04:00
# include <asm/mach/time.h>
2012-03-28 21:30:01 +04:00
# include <asm/system_misc.h>
2007-05-12 00:01:28 +04:00
2008-08-05 19:14:15 +04:00
# include <mach/regs-irq.h>
2007-05-12 00:01:28 +04:00
# include "generic.h"
2012-08-29 22:26:42 +04:00
# define KS8695_TMR_OFFSET (0xF0000 + 0xE400)
# define KS8695_TMR_VA (KS8695_IO_VA + KS8695_TMR_OFFSET)
# define KS8695_TMR_PA (KS8695_IO_PA + KS8695_TMR_OFFSET)
/*
* Timer registers
*/
# define KS8695_TMCON (0x00) /* Timer Control Register */
# define KS8695_T1TC (0x04) /* Timer 1 Timeout Count Register */
# define KS8695_T0TC (0x08) /* Timer 0 Timeout Count Register */
# define KS8695_T1PD (0x0C) /* Timer 1 Pulse Count Register */
# define KS8695_T0PD (0x10) /* Timer 0 Pulse Count Register */
/* Timer Control Register */
# define TMCON_T1EN (1 << 1) /* Timer 1 Enable */
# define TMCON_T0EN (1 << 0) /* Timer 0 Enable */
/* Timer0 Timeout Counter Register */
# define T0TC_WATCHDOG (0xff) /* Enable watchdog mode */
2012-08-29 22:27:22 +04:00
static void ks8695_set_mode ( enum clock_event_mode mode ,
struct clock_event_device * evt )
2007-05-12 00:01:28 +04:00
{
2012-08-29 22:27:22 +04:00
u32 tmcon ;
2007-05-12 00:01:28 +04:00
2012-08-29 22:27:22 +04:00
if ( mode = = CLOCK_EVT_FEAT_PERIODIC ) {
u32 rate = DIV_ROUND_CLOSEST ( KS8695_CLOCK_RATE , HZ ) ;
u32 half = DIV_ROUND_CLOSEST ( rate , 2 ) ;
/* Disable timer 1 */
tmcon = readl_relaxed ( KS8695_TMR_VA + KS8695_TMCON ) ;
tmcon & = ~ TMCON_T1EN ;
writel_relaxed ( tmcon , KS8695_TMR_VA + KS8695_TMCON ) ;
/* Both registers need to count down */
writel_relaxed ( half , KS8695_TMR_VA + KS8695_T1TC ) ;
writel_relaxed ( half , KS8695_TMR_VA + KS8695_T1PD ) ;
/* Re-enable timer1 */
tmcon | = TMCON_T1EN ;
writel_relaxed ( tmcon , KS8695_TMR_VA + KS8695_TMCON ) ;
}
}
static int ks8695_set_next_event ( unsigned long cycles ,
struct clock_event_device * evt )
{
u32 half = DIV_ROUND_CLOSEST ( cycles , 2 ) ;
u32 tmcon ;
/* Disable timer 1 */
tmcon = readl_relaxed ( KS8695_TMR_VA + KS8695_TMCON ) ;
tmcon & = ~ TMCON_T1EN ;
writel_relaxed ( tmcon , KS8695_TMR_VA + KS8695_TMCON ) ;
/* Both registers need to count down */
writel_relaxed ( half , KS8695_TMR_VA + KS8695_T1TC ) ;
writel_relaxed ( half , KS8695_TMR_VA + KS8695_T1PD ) ;
/* Re-enable timer1 */
tmcon | = TMCON_T1EN ;
writel_relaxed ( tmcon , KS8695_TMR_VA + KS8695_TMCON ) ;
return 0 ;
2007-05-12 00:01:28 +04:00
}
2012-08-29 22:27:22 +04:00
static struct clock_event_device clockevent_ks8695 = {
. name = " ks8695_t1tc " ,
. rating = 300 , /* Reasonably fast and accurate clock event */
. features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC ,
. set_next_event = ks8695_set_next_event ,
. set_mode = ks8695_set_mode ,
} ;
2007-05-12 00:01:28 +04:00
/*
* IRQ handler for the timer .
*/
static irqreturn_t ks8695_timer_interrupt ( int irq , void * dev_id )
{
2012-08-29 22:27:22 +04:00
struct clock_event_device * evt = & clockevent_ks8695 ;
evt - > event_handler ( evt ) ;
2007-05-12 00:01:28 +04:00
return IRQ_HANDLED ;
}
static struct irqaction ks8695_timer_irq = {
. name = " ks8695_tick " ,
2014-03-05 01:04:50 +04:00
. flags = IRQF_TIMER ,
2007-05-12 00:01:28 +04:00
. handler = ks8695_timer_interrupt ,
} ;
static void ks8695_timer_setup ( void )
{
unsigned long tmcon ;
2012-08-29 22:27:22 +04:00
/* Disable timer 0 and 1 */
2012-08-29 22:27:02 +04:00
tmcon = readl_relaxed ( KS8695_TMR_VA + KS8695_TMCON ) ;
2012-08-29 22:27:22 +04:00
tmcon & = ~ TMCON_T0EN ;
tmcon & = ~ TMCON_T1EN ;
writel_relaxed ( tmcon , KS8695_TMR_VA + KS8695_TMCON ) ;
2007-05-12 00:01:28 +04:00
2012-08-29 22:27:22 +04:00
/*
* Use timer 1 to fire IRQs on the timeline , minimum 2 cycles
* ( one on each counter ) maximum 2 * 2 ^ 32 , but the API will only
* accept up to a 32 bit full word ( 0xFFFFFFFFU ) .
*/
clockevents_config_and_register ( & clockevent_ks8695 ,
KS8695_CLOCK_RATE , 2 ,
0xFFFFFFFFU ) ;
2007-05-12 00:01:28 +04:00
}
2012-11-08 23:40:59 +04:00
void __init ks8695_timer_init ( void )
2007-05-12 00:01:28 +04:00
{
ks8695_timer_setup ( ) ;
/* Enable timer interrupts */
setup_irq ( KS8695_IRQ_TIMER1 , & ks8695_timer_irq ) ;
}
2013-07-09 03:01:40 +04:00
void ks8695_restart ( enum reboot_mode reboot_mode , const char * cmd )
2011-11-11 19:30:47 +04:00
{
unsigned int reg ;
2013-07-09 03:01:40 +04:00
if ( reboot_mode = = REBOOT_SOFT )
2011-11-11 19:30:47 +04:00
soft_restart ( 0 ) ;
/* disable timer0 */
2012-08-29 22:27:02 +04:00
reg = readl_relaxed ( KS8695_TMR_VA + KS8695_TMCON ) ;
writel_relaxed ( reg & ~ TMCON_T0EN , KS8695_TMR_VA + KS8695_TMCON ) ;
2011-11-11 19:30:47 +04:00
/* enable watchdog mode */
2012-08-29 22:27:02 +04:00
writel_relaxed ( ( 10 < < 8 ) | T0TC_WATCHDOG , KS8695_TMR_VA + KS8695_T0TC ) ;
2011-11-11 19:30:47 +04:00
/* re-enable timer0 */
2012-08-29 22:27:02 +04:00
writel_relaxed ( reg | TMCON_T0EN , KS8695_TMR_VA + KS8695_TMCON ) ;
2011-11-11 19:30:47 +04:00
}