2010-12-23 15:11:21 +03:00
/*
2012-10-06 10:55:42 +04:00
* arch / arm / mach - vt8500 / timer . c
2010-12-23 15:11:21 +03:00
*
2012-08-03 13:00:06 +04:00
* Copyright ( C ) 2012 Tony Prisk < linux @ prisktech . co . nz >
2010-12-23 15:11:21 +03:00
* Copyright ( C ) 2010 Alexey Charkov < alchark @ gmail . com >
*
* 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
*/
2012-08-03 13:00:06 +04:00
/*
* This file is copied and modified from the original timer . c provided by
* Alexey Charkov . Minor changes have been made for Device Tree Support .
*/
2010-12-23 15:11:21 +03:00
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/interrupt.h>
# include <linux/clocksource.h>
# include <linux/clockchips.h>
# include <linux/delay.h>
# include <asm/mach/time.h>
2012-08-03 13:00:06 +04:00
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_irq.h>
2010-12-23 15:11:21 +03:00
# define VT8500_TIMER_OFFSET 0x0100
2012-08-03 13:00:06 +04:00
# define VT8500_TIMER_HZ 3000000
2010-12-23 15:11:21 +03:00
# define TIMER_MATCH_VAL 0x0000
# define TIMER_COUNT_VAL 0x0010
# define TIMER_STATUS_VAL 0x0014
# define TIMER_IER_VAL 0x001c /* interrupt enable */
# define TIMER_CTRL_VAL 0x0020
# define TIMER_AS_VAL 0x0024 /* access status */
# define TIMER_COUNT_R_ACTIVE (1 << 5) /* not ready for read */
# define TIMER_COUNT_W_ACTIVE (1 << 4) /* not ready for write */
# define TIMER_MATCH_W_ACTIVE (1 << 0) /* not ready for write */
# define msecs_to_loops(t) (loops_per_jiffy / 1000 * HZ * t)
static void __iomem * regbase ;
static cycle_t vt8500_timer_read ( struct clocksource * cs )
{
int loops = msecs_to_loops ( 10 ) ;
writel ( 3 , regbase + TIMER_CTRL_VAL ) ;
while ( ( readl ( ( regbase + TIMER_AS_VAL ) ) & TIMER_COUNT_R_ACTIVE )
& & - - loops )
cpu_relax ( ) ;
return readl ( regbase + TIMER_COUNT_VAL ) ;
}
2012-08-03 13:00:06 +04:00
static struct clocksource clocksource = {
2010-12-23 15:11:21 +03:00
. name = " vt8500_timer " ,
. rating = 200 ,
. read = vt8500_timer_read ,
. mask = CLOCKSOURCE_MASK ( 32 ) ,
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
} ;
static int vt8500_timer_set_next_event ( unsigned long cycles ,
struct clock_event_device * evt )
{
int loops = msecs_to_loops ( 10 ) ;
cycle_t alarm = clocksource . read ( & clocksource ) + cycles ;
while ( ( readl ( regbase + TIMER_AS_VAL ) & TIMER_MATCH_W_ACTIVE )
& & - - loops )
cpu_relax ( ) ;
writel ( ( unsigned long ) alarm , regbase + TIMER_MATCH_VAL ) ;
if ( ( signed ) ( alarm - clocksource . read ( & clocksource ) ) < = 16 )
return - ETIME ;
writel ( 1 , regbase + TIMER_IER_VAL ) ;
return 0 ;
}
static void vt8500_timer_set_mode ( enum clock_event_mode mode ,
struct clock_event_device * evt )
{
switch ( mode ) {
case CLOCK_EVT_MODE_RESUME :
case CLOCK_EVT_MODE_PERIODIC :
break ;
case CLOCK_EVT_MODE_ONESHOT :
case CLOCK_EVT_MODE_UNUSED :
case CLOCK_EVT_MODE_SHUTDOWN :
writel ( readl ( regbase + TIMER_CTRL_VAL ) | 1 ,
regbase + TIMER_CTRL_VAL ) ;
writel ( 0 , regbase + TIMER_IER_VAL ) ;
break ;
}
}
2012-08-03 13:00:06 +04:00
static struct clock_event_device clockevent = {
2010-12-23 15:11:21 +03:00
. name = " vt8500_timer " ,
. features = CLOCK_EVT_FEAT_ONESHOT ,
. rating = 200 ,
. set_next_event = vt8500_timer_set_next_event ,
. set_mode = vt8500_timer_set_mode ,
} ;
static irqreturn_t vt8500_timer_interrupt ( int irq , void * dev_id )
{
struct clock_event_device * evt = dev_id ;
writel ( 0xf , regbase + TIMER_STATUS_VAL ) ;
evt - > event_handler ( evt ) ;
return IRQ_HANDLED ;
}
2012-08-03 13:00:06 +04:00
static struct irqaction irq = {
2010-12-23 15:11:21 +03:00
. name = " vt8500_timer " ,
2013-12-09 13:38:50 +04:00
. flags = IRQF_TIMER | IRQF_IRQPOLL ,
2010-12-23 15:11:21 +03:00
. handler = vt8500_timer_interrupt ,
. dev_id = & clockevent ,
} ;
2013-02-07 00:40:22 +04:00
static void __init vt8500_timer_init ( struct device_node * np )
2010-12-23 15:11:21 +03:00
{
2012-08-03 13:00:06 +04:00
int timer_irq ;
regbase = of_iomap ( np , 0 ) ;
if ( ! regbase ) {
pr_err ( " %s: Missing iobase description in Device Tree \n " ,
__func__ ) ;
return ;
}
timer_irq = irq_of_parse_and_map ( np , 0 ) ;
if ( ! timer_irq ) {
pr_err ( " %s: Missing irq description in Device Tree \n " ,
__func__ ) ;
return ;
}
2010-12-23 15:11:21 +03:00
writel ( 1 , regbase + TIMER_CTRL_VAL ) ;
writel ( 0xf , regbase + TIMER_STATUS_VAL ) ;
writel ( ~ 0 , regbase + TIMER_MATCH_VAL ) ;
if ( clocksource_register_hz ( & clocksource , VT8500_TIMER_HZ ) )
2012-08-03 13:00:06 +04:00
pr_err ( " %s: vt8500_timer_init: clocksource_register failed for %s \n " ,
__func__ , clocksource . name ) ;
2010-12-23 15:11:21 +03:00
clockevent . cpumask = cpumask_of ( 0 ) ;
2012-08-03 13:00:06 +04:00
if ( setup_irq ( timer_irq , & irq ) )
pr_err ( " %s: setup_irq failed for %s \n " , __func__ ,
clockevent . name ) ;
2013-01-12 15:50:05 +04:00
clockevents_config_and_register ( & clockevent , VT8500_TIMER_HZ ,
4 , 0xf0000000 ) ;
2010-12-23 15:11:21 +03:00
}
2013-03-19 18:38:50 +04:00
CLOCKSOURCE_OF_DECLARE ( vt8500 , " via,vt8500-timer " , vt8500_timer_init ) ;