2006-12-01 09:04:47 +01:00
/*
2008-04-16 20:43:49 +01:00
* at91sam926x_time . c - Periodic Interval Timer ( PIT ) for at91sam926x
2006-12-01 09:04:47 +01:00
*
* Copyright ( C ) 2005 - 2006 M . Amine SAYA , ATMEL Rousset , France
* Revision 2005 M . Nicolas Diremdjian , ATMEL Rousset , France
2008-04-16 20:43:49 +01:00
* Converted to ClockSource / ClockEvents by David Brownell .
2006-12-01 09:04:47 +01:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/kernel.h>
2008-04-16 20:43:49 +01:00
# include <linux/clk.h>
# include <linux/clockchips.h>
2012-02-27 11:19:34 +01:00
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_irq.h>
2006-12-01 09:04:47 +01:00
# include <asm/mach/time.h>
2012-10-30 08:09:09 +08:00
# define AT91_PIT_MR 0x00 /* Mode Register */
# define AT91_PIT_PITIEN (1 << 25) /* Timer Interrupt Enable */
# define AT91_PIT_PITEN (1 << 24) /* Timer Enabled */
# define AT91_PIT_PIV (0xfffff) /* Periodic Interval Value */
# define AT91_PIT_SR 0x04 /* Status Register */
# define AT91_PIT_PITS (1 << 0) /* Timer Status */
# define AT91_PIT_PIVR 0x08 /* Periodic Interval Value Register */
# define AT91_PIT_PIIR 0x0c /* Periodic Interval Image Register */
# define AT91_PIT_PICNT (0xfff << 20) /* Interval Counter */
# define AT91_PIT_CPIV (0xfffff) /* Inverval Value */
2006-12-01 09:04:47 +01:00
# define PIT_CPIV(x) ((x) & AT91_PIT_CPIV)
# define PIT_PICNT(x) (((x) & AT91_PIT_PICNT) >> 20)
2008-04-16 20:43:49 +01:00
static u32 pit_cycle ; /* write-once */
static u32 pit_cnt ; /* access only w/system irq blocked */
2011-09-18 22:29:50 +08:00
static void __iomem * pit_base_addr __read_mostly ;
2008-04-16 20:43:49 +01:00
2011-09-18 22:29:50 +08:00
static inline unsigned int pit_read ( unsigned int reg_offset )
{
return __raw_readl ( pit_base_addr + reg_offset ) ;
}
static inline void pit_write ( unsigned int reg_offset , unsigned long value )
{
__raw_writel ( value , pit_base_addr + reg_offset ) ;
}
2008-04-16 20:43:49 +01:00
2006-12-01 09:04:47 +01:00
/*
2008-04-16 20:43:49 +01:00
* Clocksource : just a monotonic counter of MCK / 16 cycles .
* We don ' t care whether or not PIT irqs are enabled .
2006-12-01 09:04:47 +01:00
*/
2009-04-21 12:24:00 -07:00
static cycle_t read_pit_clk ( struct clocksource * cs )
2006-12-01 09:04:47 +01:00
{
2008-04-16 20:43:49 +01:00
unsigned long flags ;
u32 elapsed ;
u32 t ;
raw_local_irq_save ( flags ) ;
elapsed = pit_cnt ;
2011-09-18 22:29:50 +08:00
t = pit_read ( AT91_PIT_PIIR ) ;
2008-04-16 20:43:49 +01:00
raw_local_irq_restore ( flags ) ;
elapsed + = PIT_PICNT ( t ) * pit_cycle ;
elapsed + = PIT_CPIV ( t ) ;
return elapsed ;
}
static struct clocksource pit_clk = {
. name = " pit " ,
. rating = 175 ,
. read = read_pit_clk ,
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
} ;
2006-12-01 09:04:47 +01:00
2008-04-16 20:43:49 +01:00
/*
* Clockevent device : interrupts every 1 / HZ ( = = pit_cycles * MCK / 16 )
*/
static void
pit_clkevt_mode ( enum clock_event_mode mode , struct clock_event_device * dev )
{
switch ( mode ) {
case CLOCK_EVT_MODE_PERIODIC :
2009-09-21 09:30:09 +02:00
/* update clocksource counter */
2011-09-18 22:29:50 +08:00
pit_cnt + = pit_cycle * PIT_PICNT ( pit_read ( AT91_PIT_PIVR ) ) ;
pit_write ( AT91_PIT_MR , ( pit_cycle - 1 ) | AT91_PIT_PITEN
2008-04-16 20:43:49 +01:00
| AT91_PIT_PITIEN ) ;
break ;
case CLOCK_EVT_MODE_ONESHOT :
BUG ( ) ;
/* FALLTHROUGH */
case CLOCK_EVT_MODE_SHUTDOWN :
case CLOCK_EVT_MODE_UNUSED :
/* disable irq, leaving the clocksource active */
2011-09-18 22:29:50 +08:00
pit_write ( AT91_PIT_MR , ( pit_cycle - 1 ) | AT91_PIT_PITEN ) ;
2008-04-16 20:43:49 +01:00
break ;
case CLOCK_EVT_MODE_RESUME :
break ;
}
2006-12-01 09:04:47 +01:00
}
2012-11-07 16:32:41 -07:00
static void at91sam926x_pit_suspend ( struct clock_event_device * cedev )
{
/* Disable timer */
pit_write ( AT91_PIT_MR , 0 ) ;
}
static void at91sam926x_pit_reset ( void )
{
/* Disable timer and irqs */
pit_write ( AT91_PIT_MR , 0 ) ;
/* Clear any pending interrupts, wait for PIT to stop counting */
while ( PIT_CPIV ( pit_read ( AT91_PIT_PIVR ) ) ! = 0 )
cpu_relax ( ) ;
/* Start PIT but don't enable IRQ */
pit_write ( AT91_PIT_MR , ( pit_cycle - 1 ) | AT91_PIT_PITEN ) ;
}
static void at91sam926x_pit_resume ( struct clock_event_device * cedev )
{
at91sam926x_pit_reset ( ) ;
}
2008-04-16 20:43:49 +01:00
static struct clock_event_device pit_clkevt = {
. name = " pit " ,
. features = CLOCK_EVT_FEAT_PERIODIC ,
. shift = 32 ,
. rating = 100 ,
. set_mode = pit_clkevt_mode ,
2012-11-07 16:32:41 -07:00
. suspend = at91sam926x_pit_suspend ,
. resume = at91sam926x_pit_resume ,
2008-04-16 20:43:49 +01:00
} ;
2006-12-01 09:04:47 +01:00
/*
* IRQ handler for the timer .
*/
2008-04-16 20:43:49 +01:00
static irqreturn_t at91sam926x_pit_interrupt ( int irq , void * dev_id )
2006-12-01 09:04:47 +01:00
{
2009-09-21 09:30:09 +02:00
/*
* irqs should be disabled here , but as the irq is shared they are only
* guaranteed to be off if the timer irq is registered first .
*/
WARN_ON_ONCE ( ! irqs_disabled ( ) ) ;
2006-12-01 09:04:47 +01:00
2008-04-16 20:43:49 +01:00
/* The PIT interrupt may be disabled, and is shared */
if ( ( pit_clkevt . mode = = CLOCK_EVT_MODE_PERIODIC )
2011-09-18 22:29:50 +08:00
& & ( pit_read ( AT91_PIT_SR ) & AT91_PIT_PITS ) ) {
2008-04-16 20:43:49 +01:00
unsigned nr_ticks ;
/* Get number of ticks performed before irq, and ack it */
2011-09-18 22:29:50 +08:00
nr_ticks = PIT_PICNT ( pit_read ( AT91_PIT_PIVR ) ) ;
2006-12-01 09:04:47 +01:00
do {
2008-04-16 20:43:49 +01:00
pit_cnt + = pit_cycle ;
pit_clkevt . event_handler ( & pit_clkevt ) ;
2006-12-01 09:04:47 +01:00
nr_ticks - - ;
} while ( nr_ticks ) ;
return IRQ_HANDLED ;
2008-04-16 20:43:49 +01:00
}
return IRQ_NONE ;
2006-12-01 09:04:47 +01:00
}
2008-04-16 20:43:49 +01:00
static struct irqaction at91sam926x_pit_irq = {
2006-12-01 09:04:47 +01:00
. name = " at91_tick " ,
2007-05-08 00:35:39 -07:00
. flags = IRQF_SHARED | IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL ,
2012-02-27 11:19:34 +01:00
. handler = at91sam926x_pit_interrupt ,
2012-06-21 14:47:27 +02:00
. irq = NR_IRQS_LEGACY + AT91_ID_SYS ,
2006-12-01 09:04:47 +01:00
} ;
2012-02-27 11:19:34 +01:00
# ifdef CONFIG_OF
static struct of_device_id pit_timer_ids [ ] = {
{ . compatible = " atmel,at91sam9260-pit " } ,
{ /* sentinel */ }
} ;
static int __init of_at91sam926x_pit_init ( void )
{
struct device_node * np ;
int ret ;
np = of_find_matching_node ( NULL , pit_timer_ids ) ;
if ( ! np )
goto err ;
pit_base_addr = of_iomap ( np , 0 ) ;
if ( ! pit_base_addr )
goto node_err ;
/* Get the interrupts property */
ret = irq_of_parse_and_map ( np , 0 ) ;
2012-02-17 11:54:29 +01:00
if ( ! ret ) {
pr_crit ( " AT91: PIT: Unable to get IRQ from DT \n " ) ;
2012-02-27 11:19:34 +01:00
goto ioremap_err ;
2012-02-17 11:54:29 +01:00
}
2012-02-27 11:19:34 +01:00
at91sam926x_pit_irq . irq = ret ;
of_node_put ( np ) ;
return 0 ;
ioremap_err :
iounmap ( pit_base_addr ) ;
node_err :
of_node_put ( np ) ;
err :
return - EINVAL ;
}
# else
static int __init of_at91sam926x_pit_init ( void )
{
return - EINVAL ;
}
# endif
2006-12-01 09:04:47 +01:00
/*
2008-04-16 20:43:49 +01:00
* Set up both clocksource and clockevent support .
2006-12-01 09:04:47 +01:00
*/
2012-11-08 12:40:59 -07:00
void __init at91sam926x_pit_init ( void )
2006-12-01 09:04:47 +01:00
{
2008-04-16 20:43:49 +01:00
unsigned long pit_rate ;
unsigned bits ;
2012-02-17 11:54:29 +01:00
int ret ;
2008-04-16 20:43:49 +01:00
2012-02-27 11:19:34 +01:00
/* For device tree enabled device: initialize here */
of_at91sam926x_pit_init ( ) ;
2008-04-16 20:43:49 +01:00
/*
* Use our actual MCK to figure out how many MCK / 16 ticks per
* 1 / HZ period ( instead of a compile - time constant LATCH ) .
*/
pit_rate = clk_get_rate ( clk_get ( NULL , " mck " ) ) / 16 ;
pit_cycle = ( pit_rate + HZ / 2 ) / HZ ;
WARN_ON ( ( ( pit_cycle - 1 ) & ~ AT91_PIT_PIV ) ! = 0 ) ;
2006-12-01 09:04:47 +01:00
2008-04-16 20:43:49 +01:00
/* Initialize and enable the timer */
at91sam926x_pit_reset ( ) ;
/*
* Register clocksource . The high order bits of PIV are unused ,
* so this isn ' t a 32 - bit counter unless we get clockevent irqs .
*/
bits = 12 /* PICNT */ + ilog2 ( pit_cycle ) /* PIV */ ;
pit_clk . mask = CLOCKSOURCE_MASK ( bits ) ;
2010-12-13 13:14:55 +00:00
clocksource_register_hz ( & pit_clk , pit_rate ) ;
2008-04-16 20:43:49 +01:00
/* Set up irq handler */
2012-02-17 11:54:29 +01:00
ret = setup_irq ( at91sam926x_pit_irq . irq , & at91sam926x_pit_irq ) ;
if ( ret )
pr_crit ( " AT91: PIT: Unable to setup IRQ \n " ) ;
2008-04-16 20:43:49 +01:00
/* Set up and register clockevents */
pit_clkevt . mult = div_sc ( pit_rate , NSEC_PER_SEC , pit_clkevt . shift ) ;
2008-12-13 21:20:26 +10:30
pit_clkevt . cpumask = cpumask_of ( 0 ) ;
2008-04-16 20:43:49 +01:00
clockevents_register_device ( & pit_clkevt ) ;
2006-12-01 09:04:47 +01:00
}
2011-09-18 22:29:50 +08:00
void __init at91sam926x_ioremap_pit ( u32 addr )
{
2012-02-27 11:19:34 +01:00
# if defined(CONFIG_OF)
struct device_node * np =
of_find_matching_node ( NULL , pit_timer_ids ) ;
if ( np ) {
of_node_put ( np ) ;
return ;
}
# endif
2011-09-18 22:29:50 +08:00
pit_base_addr = ioremap ( addr , 16 ) ;
if ( ! pit_base_addr )
panic ( " Impossible to ioremap PIT \n " ) ;
2006-12-01 09:04:47 +01:00
}