2010-04-01 15:30:49 +04:00
/*
* arch / arm / plat - spear / time . c
*
2011-02-16 09:40:32 +03:00
* Copyright ( C ) 2010 ST Microelectronics
2014-04-19 02:07:16 +04:00
* Shiraz Hashim < shiraz . linux . kernel @ gmail . com >
2010-04-01 15:30:49 +04:00
*
* 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/clk.h>
# include <linux/clockchips.h>
# include <linux/clocksource.h>
# include <linux/err.h>
# include <linux/init.h>
# include <linux/interrupt.h>
2012-04-11 21:30:11 +04:00
# include <linux/ioport.h>
2010-04-01 15:30:49 +04:00
# include <linux/io.h>
# include <linux/kernel.h>
2012-04-21 11:45:37 +04:00
# include <linux/of_irq.h>
# include <linux/of_address.h>
2010-04-01 15:30:49 +04:00
# include <linux/time.h>
# include <linux/irq.h>
# include <asm/mach/time.h>
2012-12-02 18:49:04 +04:00
# include "generic.h"
2010-04-01 15:30:49 +04:00
/*
* We would use TIMER0 and TIMER1 as clockevent and clocksource .
* Timer0 and Timer1 both belong to same gpt block in cpu subbsystem . Further
* they share same functional clock . Any change in one ' s functional clock will
* also affect other timer .
*/
# define CLKEVT 0 /* gpt0, channel0 as clockevent */
# define CLKSRC 1 /* gpt0, channel1 as clocksource */
/* Register offsets, x is channel number */
# define CR(x) ((x) * 0x80 + 0x80)
# define IR(x) ((x) * 0x80 + 0x84)
# define LOAD(x) ((x) * 0x80 + 0x88)
# define COUNT(x) ((x) * 0x80 + 0x8C)
/* Reg bit definitions */
# define CTRL_INT_ENABLE 0x0100
# define CTRL_ENABLE 0x0020
# define CTRL_ONE_SHOT 0x0010
# define CTRL_PRESCALER1 0x0
# define CTRL_PRESCALER2 0x1
# define CTRL_PRESCALER4 0x2
# define CTRL_PRESCALER8 0x3
# define CTRL_PRESCALER16 0x4
# define CTRL_PRESCALER32 0x5
# define CTRL_PRESCALER64 0x6
# define CTRL_PRESCALER128 0x7
# define CTRL_PRESCALER256 0x8
# define INT_STATUS 0x1
2010-07-19 23:55:46 +04:00
/*
* Minimum clocksource / clockevent timer range in seconds
*/
# define SPEAR_MIN_RANGE 4
2010-04-01 15:30:49 +04:00
static __iomem void * gpt_base ;
static struct clk * gpt_clk ;
static int clockevent_next_event ( unsigned long evt ,
struct clock_event_device * clk_event_dev ) ;
2014-04-09 18:01:12 +04:00
static void __init spear_clocksource_init ( void )
2010-04-01 15:30:49 +04:00
{
u32 tick_rate ;
u16 val ;
/* program the prescaler (/256)*/
writew ( CTRL_PRESCALER256 , gpt_base + CR ( CLKSRC ) ) ;
/* find out actual clock driving Timer */
tick_rate = clk_get_rate ( gpt_clk ) ;
tick_rate > > = CTRL_PRESCALER256 ;
writew ( 0xFFFF , gpt_base + LOAD ( CLKSRC ) ) ;
val = readw ( gpt_base + CR ( CLKSRC ) ) ;
val & = ~ CTRL_ONE_SHOT ; /* autoreload mode */
val | = CTRL_ENABLE ;
writew ( val , gpt_base + CR ( CLKSRC ) ) ;
/* register the clocksource */
2011-05-08 20:10:14 +04:00
clocksource_mmio_init ( gpt_base + COUNT ( CLKSRC ) , " tmr1 " , tick_rate ,
200 , 16 , clocksource_mmio_readw_up ) ;
2010-04-01 15:30:49 +04:00
}
2015-02-27 11:09:52 +03:00
static inline void timer_shutdown ( struct clock_event_device * evt )
{
u16 val = readw ( gpt_base + CR ( CLKEVT ) ) ;
2010-04-01 15:30:49 +04:00
2015-02-27 11:09:52 +03:00
/* stop the timer */
val & = ~ CTRL_ENABLE ;
writew ( val , gpt_base + CR ( CLKEVT ) ) ;
}
static int spear_shutdown ( struct clock_event_device * evt )
{
timer_shutdown ( evt ) ;
return 0 ;
}
static int spear_set_oneshot ( struct clock_event_device * evt )
{
u16 val ;
/* stop the timer */
timer_shutdown ( evt ) ;
val = readw ( gpt_base + CR ( CLKEVT ) ) ;
val | = CTRL_ONE_SHOT ;
writew ( val , gpt_base + CR ( CLKEVT ) ) ;
return 0 ;
}
static int spear_set_periodic ( struct clock_event_device * evt )
2010-04-01 15:30:49 +04:00
{
u32 period ;
u16 val ;
/* stop the timer */
2015-02-27 11:09:52 +03:00
timer_shutdown ( evt ) ;
period = clk_get_rate ( gpt_clk ) / HZ ;
period > > = CTRL_PRESCALER16 ;
writew ( period , gpt_base + LOAD ( CLKEVT ) ) ;
2010-04-01 15:30:49 +04:00
val = readw ( gpt_base + CR ( CLKEVT ) ) ;
2015-02-27 11:09:52 +03:00
val & = ~ CTRL_ONE_SHOT ;
val | = CTRL_ENABLE | CTRL_INT_ENABLE ;
2010-04-01 15:30:49 +04:00
writew ( val , gpt_base + CR ( CLKEVT ) ) ;
2015-02-27 11:09:52 +03:00
return 0 ;
2010-04-01 15:30:49 +04:00
}
2015-02-27 11:09:52 +03:00
static struct clock_event_device clkevt = {
. name = " tmr0 " ,
. features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT ,
. set_state_shutdown = spear_shutdown ,
. set_state_periodic = spear_set_periodic ,
. set_state_oneshot = spear_set_oneshot ,
. tick_resume = spear_shutdown ,
. set_next_event = clockevent_next_event ,
. shift = 0 , /* to be computed */
} ;
2010-04-01 15:30:49 +04:00
static int clockevent_next_event ( unsigned long cycles ,
struct clock_event_device * clk_event_dev )
{
2012-02-25 01:50:50 +04:00
u16 val = readw ( gpt_base + CR ( CLKEVT ) ) ;
if ( val & CTRL_ENABLE )
writew ( val & ~ CTRL_ENABLE , gpt_base + CR ( CLKEVT ) ) ;
2010-04-01 15:30:49 +04:00
writew ( cycles , gpt_base + LOAD ( CLKEVT ) ) ;
val | = CTRL_ENABLE | CTRL_INT_ENABLE ;
writew ( val , gpt_base + CR ( CLKEVT ) ) ;
return 0 ;
}
static irqreturn_t spear_timer_interrupt ( int irq , void * dev_id )
{
struct clock_event_device * evt = & clkevt ;
writew ( INT_STATUS , gpt_base + IR ( CLKEVT ) ) ;
evt - > event_handler ( evt ) ;
return IRQ_HANDLED ;
}
static struct irqaction spear_timer_irq = {
. name = " timer " ,
2014-03-05 01:09:18 +04:00
. flags = IRQF_TIMER ,
2010-04-01 15:30:49 +04:00
. handler = spear_timer_interrupt
} ;
2012-04-11 21:30:11 +04:00
static void __init spear_clockevent_init ( int irq )
2010-04-01 15:30:49 +04:00
{
u32 tick_rate ;
/* program the prescaler */
writew ( CTRL_PRESCALER16 , gpt_base + CR ( CLKEVT ) ) ;
tick_rate = clk_get_rate ( gpt_clk ) ;
tick_rate > > = CTRL_PRESCALER16 ;
clkevt . cpumask = cpumask_of ( 0 ) ;
2013-01-12 15:50:05 +04:00
clockevents_config_and_register ( & clkevt , tick_rate , 3 , 0xfff0 ) ;
2010-04-01 15:30:49 +04:00
2012-04-11 21:30:11 +04:00
setup_irq ( irq , & spear_timer_irq ) ;
2010-04-01 15:30:49 +04:00
}
2015-07-28 01:27:52 +03:00
static const struct of_device_id const timer_of_match [ ] __initconst = {
2012-04-21 11:45:37 +04:00
{ . compatible = " st,spear-timer " , } ,
{ } ,
} ;
void __init spear_setup_of_timer ( void )
2010-04-01 15:30:49 +04:00
{
2012-04-21 11:45:37 +04:00
struct device_node * np ;
int irq , ret ;
np = of_find_matching_node ( NULL , timer_of_match ) ;
if ( ! np ) {
pr_err ( " %s: No timer passed via DT \n " , __func__ ) ;
return ;
}
2010-04-01 15:30:49 +04:00
2012-04-21 11:45:37 +04:00
irq = irq_of_parse_and_map ( np , 0 ) ;
if ( ! irq ) {
pr_err ( " %s: No irq passed for timer via DT \n " , __func__ ) ;
2010-04-01 15:30:49 +04:00
return ;
}
2012-04-21 11:45:37 +04:00
gpt_base = of_iomap ( np , 0 ) ;
2010-04-01 15:30:49 +04:00
if ( ! gpt_base ) {
2012-04-21 11:45:37 +04:00
pr_err ( " %s: of iomap failed \n " , __func__ ) ;
return ;
2010-04-01 15:30:49 +04:00
}
gpt_clk = clk_get_sys ( " gpt0 " , NULL ) ;
if ( ! gpt_clk ) {
pr_err ( " %s:couldn't get clk for gpt \n " , __func__ ) ;
goto err_iomap ;
}
2012-04-16 12:26:18 +04:00
ret = clk_prepare_enable ( gpt_clk ) ;
2011-02-16 09:40:32 +03:00
if ( ret < 0 ) {
2012-04-16 12:26:18 +04:00
pr_err ( " %s:couldn't prepare-enable gpt clock \n " , __func__ ) ;
goto err_prepare_enable_clk ;
2010-04-01 15:30:49 +04:00
}
2012-04-11 21:30:11 +04:00
spear_clockevent_init ( irq ) ;
2010-04-01 15:30:49 +04:00
spear_clocksource_init ( ) ;
return ;
2012-04-16 12:26:18 +04:00
err_prepare_enable_clk :
2011-02-16 09:40:32 +03:00
clk_put ( gpt_clk ) ;
2010-04-01 15:30:49 +04:00
err_iomap :
iounmap ( gpt_base ) ;
}