2010-01-14 13:30:16 +00:00
/*
2010-10-06 16:18:08 +01:00
* linux / arch / arm / common / timer - sp . c
2010-01-14 13:30:16 +00:00
*
* Copyright ( C ) 1999 - 2003 ARM Limited
* Copyright ( C ) 2000 Deep Blue Solutions Ltd
*
* 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
*/
2011-05-12 13:31:48 +01:00
# include <linux/clk.h>
2010-01-14 13:30:16 +00:00
# include <linux/clocksource.h>
# include <linux/clockchips.h>
2011-05-12 13:31:48 +01:00
# include <linux/err.h>
2010-01-14 13:30:16 +00:00
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/io.h>
2011-12-12 15:29:08 -06:00
# include <asm/sched_clock.h>
2010-01-14 13:30:16 +00:00
# include <asm/hardware/arm_timer.h>
2011-05-12 13:31:48 +01:00
static long __init sp804_get_clock_rate ( const char * name )
{
struct clk * clk ;
long rate ;
int err ;
clk = clk_get_sys ( " sp804 " , name ) ;
if ( IS_ERR ( clk ) ) {
pr_err ( " sp804: %s clock not found: %d \n " , name ,
( int ) PTR_ERR ( clk ) ) ;
return PTR_ERR ( clk ) ;
}
2011-09-22 11:38:40 +01:00
err = clk_prepare ( clk ) ;
if ( err ) {
pr_err ( " sp804: %s clock failed to prepare: %d \n " , name , err ) ;
clk_put ( clk ) ;
return err ;
}
2011-05-12 13:31:48 +01:00
err = clk_enable ( clk ) ;
if ( err ) {
pr_err ( " sp804: %s clock failed to enable: %d \n " , name , err ) ;
2011-09-22 11:38:40 +01:00
clk_unprepare ( clk ) ;
2011-05-12 13:31:48 +01:00
clk_put ( clk ) ;
return err ;
}
rate = clk_get_rate ( clk ) ;
if ( rate < 0 ) {
pr_err ( " sp804: %s clock failed to get rate: %ld \n " , name , rate ) ;
clk_disable ( clk ) ;
2011-09-22 11:38:40 +01:00
clk_unprepare ( clk ) ;
2011-05-12 13:31:48 +01:00
clk_put ( clk ) ;
}
return rate ;
}
2011-12-12 15:29:08 -06:00
static void __iomem * sched_clock_base ;
static u32 sp804_read ( void )
{
return ~ readl_relaxed ( sched_clock_base + TIMER_VALUE ) ;
}
void __init __sp804_clocksource_and_sched_clock_init ( void __iomem * base ,
const char * name ,
int use_sched_clock )
2010-01-14 13:30:16 +00:00
{
2011-05-12 13:31:48 +01:00
long rate = sp804_get_clock_rate ( name ) ;
if ( rate < 0 )
return ;
2010-01-14 13:30:16 +00:00
/* setup timer 0 as free-running clocksource */
2011-05-08 15:33:30 +01:00
writel ( 0 , base + TIMER_CTRL ) ;
writel ( 0xffffffff , base + TIMER_LOAD ) ;
writel ( 0xffffffff , base + TIMER_VALUE ) ;
2010-01-14 13:30:16 +00:00
writel ( TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC ,
2011-05-08 15:33:30 +01:00
base + TIMER_CTRL ) ;
2010-01-14 13:30:16 +00:00
2011-05-12 12:08:23 +01:00
clocksource_mmio_init ( base + TIMER_VALUE , name ,
2011-05-12 13:31:48 +01:00
rate , 200 , 32 , clocksource_mmio_readl_down ) ;
2011-12-12 15:29:08 -06:00
if ( use_sched_clock ) {
sched_clock_base = base ;
setup_sched_clock ( sp804_read , 32 , rate ) ;
}
2010-01-14 13:30:16 +00:00
}
static void __iomem * clkevt_base ;
2011-05-12 15:45:16 +01:00
static unsigned long clkevt_reload ;
2010-01-14 13:30:16 +00:00
/*
* IRQ handler for the timer
*/
static irqreturn_t sp804_timer_interrupt ( int irq , void * dev_id )
{
struct clock_event_device * evt = dev_id ;
/* clear the interrupt */
writel ( 1 , clkevt_base + TIMER_INTCLR ) ;
evt - > event_handler ( evt ) ;
return IRQ_HANDLED ;
}
static void sp804_set_mode ( enum clock_event_mode mode ,
struct clock_event_device * evt )
{
unsigned long ctrl = TIMER_CTRL_32BIT | TIMER_CTRL_IE ;
writel ( ctrl , clkevt_base + TIMER_CTRL ) ;
switch ( mode ) {
case CLOCK_EVT_MODE_PERIODIC :
2011-05-12 15:45:16 +01:00
writel ( clkevt_reload , clkevt_base + TIMER_LOAD ) ;
2010-01-14 13:30:16 +00:00
ctrl | = TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE ;
break ;
case CLOCK_EVT_MODE_ONESHOT :
/* period set, and timer enabled in 'next_event' hook */
ctrl | = TIMER_CTRL_ONESHOT ;
break ;
case CLOCK_EVT_MODE_UNUSED :
case CLOCK_EVT_MODE_SHUTDOWN :
default :
break ;
}
writel ( ctrl , clkevt_base + TIMER_CTRL ) ;
}
static int sp804_set_next_event ( unsigned long next ,
struct clock_event_device * evt )
{
unsigned long ctrl = readl ( clkevt_base + TIMER_CTRL ) ;
writel ( next , clkevt_base + TIMER_LOAD ) ;
writel ( ctrl | TIMER_CTRL_ENABLE , clkevt_base + TIMER_CTRL ) ;
return 0 ;
}
static struct clock_event_device sp804_clockevent = {
. features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT ,
. set_mode = sp804_set_mode ,
. set_next_event = sp804_set_next_event ,
. rating = 300 ,
. cpumask = cpu_all_mask ,
} ;
static struct irqaction sp804_timer_irq = {
. name = " timer " ,
. flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL ,
. handler = sp804_timer_interrupt ,
. dev_id = & sp804_clockevent ,
} ;
2011-05-12 15:31:13 +01:00
void __init sp804_clockevents_init ( void __iomem * base , unsigned int irq ,
const char * name )
2010-01-14 13:30:16 +00:00
{
struct clock_event_device * evt = & sp804_clockevent ;
2011-05-12 15:45:16 +01:00
long rate = sp804_get_clock_rate ( name ) ;
if ( rate < 0 )
return ;
2010-01-14 13:30:16 +00:00
clkevt_base = base ;
2011-05-12 15:45:16 +01:00
clkevt_reload = DIV_ROUND_CLOSEST ( rate , HZ ) ;
2011-05-12 15:31:13 +01:00
evt - > name = name ;
evt - > irq = irq ;
2010-01-14 13:30:16 +00:00
2011-05-12 15:31:13 +01:00
setup_irq ( irq , & sp804_timer_irq ) ;
2011-12-21 13:25:34 +01:00
clockevents_config_and_register ( evt , rate , 0xf , 0xffffffff ) ;
2010-01-14 13:30:16 +00:00
}