2006-01-09 20:05:41 +03:00
/*
2007-02-05 13:42:07 +03:00
* linux / arch / arm / mach - at91 / at91rm9200_time . c
2006-01-09 20:05:41 +03:00
*
* Copyright ( C ) 2003 SAN People
* Copyright ( C ) 2003 ATMEL
*
* 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
*/
2007-07-31 04:41:26 +04:00
# include <linux/kernel.h>
2006-01-09 20:05:41 +03:00
# include <linux/interrupt.h>
2006-07-02 02:01:50 +04:00
# include <linux/irq.h>
2015-08-16 12:23:44 +03:00
# include <linux/clk.h>
2007-07-31 04:41:26 +04:00
# include <linux/clockchips.h>
2012-04-04 21:15:15 +04:00
# include <linux/export.h>
2015-03-12 15:07:32 +03:00
# include <linux/mfd/syscon.h>
# include <linux/mfd/syscon/atmel-st.h>
2012-10-28 22:31:07 +04:00
# include <linux/of_irq.h>
2015-03-12 15:07:32 +03:00
# include <linux/regmap.h>
2006-01-09 20:05:41 +03:00
2006-06-19 18:23:41 +04:00
static unsigned long last_crtr ;
2007-07-31 04:41:26 +04:00
static u32 irqmask ;
static struct clock_event_device clkevt ;
2015-03-12 15:07:32 +03:00
static struct regmap * regmap_st ;
2015-08-16 12:23:44 +03:00
static int timer_latch ;
2011-10-16 14:17:09 +04:00
2006-01-09 20:05:41 +03:00
/*
2007-07-31 04:41:26 +04:00
* The ST_CRTR is updated asynchronously to the master clock . . . but
* the updates as seen by the CPU don ' t seem to be strictly monotonic .
* Waiting until we read the same value twice avoids glitching .
2006-01-09 20:05:41 +03:00
*/
2007-07-31 04:41:26 +04:00
static inline unsigned long read_CRTR ( void )
{
2015-03-12 15:07:32 +03:00
unsigned int x1 , x2 ;
2006-01-09 20:05:41 +03:00
2015-03-12 15:07:32 +03:00
regmap_read ( regmap_st , AT91_ST_CRTR , & x1 ) ;
2006-01-09 20:05:41 +03:00
do {
2015-03-12 15:07:32 +03:00
regmap_read ( regmap_st , AT91_ST_CRTR , & x2 ) ;
2007-07-31 04:41:26 +04:00
if ( x1 = = x2 )
break ;
x1 = x2 ;
} while ( 1 ) ;
2006-01-09 20:05:41 +03:00
return x1 ;
}
/*
* IRQ handler for the timer .
*/
2006-10-06 21:53:39 +04:00
static irqreturn_t at91rm9200_timer_interrupt ( int irq , void * dev_id )
2006-01-09 20:05:41 +03:00
{
2015-03-12 15:07:32 +03:00
u32 sr ;
regmap_read ( regmap_st , AT91_ST_SR , & sr ) ;
sr & = irqmask ;
2006-01-09 20:05:41 +03:00
2009-09-21 11:30:09 +04: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 ( ) ) ;
2007-07-31 04:41:26 +04:00
/* simulate "oneshot" timer with alarm */
if ( sr & AT91_ST_ALMS ) {
clkevt . event_handler ( & clkevt ) ;
return IRQ_HANDLED ;
}
2006-01-09 20:05:41 +03:00
2007-07-31 04:41:26 +04:00
/* periodic mode should handle delayed ticks */
if ( sr & AT91_ST_PITS ) {
u32 crtr = read_CRTR ( ) ;
2006-01-09 20:05:41 +03:00
2015-08-16 12:23:44 +03:00
while ( ( ( crtr - last_crtr ) & AT91_ST_CRTV ) > = timer_latch ) {
last_crtr + = timer_latch ;
2007-07-31 04:41:26 +04:00
clkevt . event_handler ( & clkevt ) ;
}
2006-01-09 20:05:41 +03:00
return IRQ_HANDLED ;
}
2007-07-31 04:41:26 +04:00
/* this irq is shared ... */
return IRQ_NONE ;
2006-01-09 20:05:41 +03:00
}
2016-12-21 22:32:01 +03:00
static u64 read_clk32k ( struct clocksource * cs )
2006-06-19 18:26:50 +04:00
{
2007-07-31 04:41:26 +04:00
return read_CRTR ( ) ;
}
2006-06-19 18:26:50 +04:00
2007-07-31 04:41:26 +04:00
static struct clocksource clk32k = {
. name = " 32k_counter " ,
. rating = 150 ,
. read = read_clk32k ,
. mask = CLOCKSOURCE_MASK ( 20 ) ,
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
} ;
2015-06-18 13:54:45 +03:00
static void clkdev32k_disable_and_flush_irq ( void )
2007-07-31 04:41:26 +04:00
{
2015-03-12 15:07:32 +03:00
unsigned int val ;
2007-07-31 04:41:26 +04:00
/* Disable and flush pending timer interrupts */
2015-03-12 15:07:32 +03:00
regmap_write ( regmap_st , AT91_ST_IDR , AT91_ST_PITS | AT91_ST_ALMS ) ;
regmap_read ( regmap_st , AT91_ST_SR , & val ) ;
2007-07-31 04:41:26 +04:00
last_crtr = read_CRTR ( ) ;
2015-06-18 13:54:45 +03:00
}
static int clkevt32k_shutdown ( struct clock_event_device * evt )
{
clkdev32k_disable_and_flush_irq ( ) ;
irqmask = 0 ;
regmap_write ( regmap_st , AT91_ST_IER , irqmask ) ;
return 0 ;
}
static int clkevt32k_set_oneshot ( struct clock_event_device * dev )
{
clkdev32k_disable_and_flush_irq ( ) ;
/*
* ALM for oneshot irqs , set by next_event ( )
* before 32 seconds have passed .
*/
irqmask = AT91_ST_ALMS ;
regmap_write ( regmap_st , AT91_ST_RTAR , last_crtr ) ;
2015-03-12 15:07:32 +03:00
regmap_write ( regmap_st , AT91_ST_IER , irqmask ) ;
2015-06-18 13:54:45 +03:00
return 0 ;
}
static int clkevt32k_set_periodic ( struct clock_event_device * dev )
{
clkdev32k_disable_and_flush_irq ( ) ;
/* PIT for periodic irqs; fixed rate of 1/HZ */
irqmask = AT91_ST_PITS ;
2015-08-16 12:23:44 +03:00
regmap_write ( regmap_st , AT91_ST_PIMR , timer_latch ) ;
2015-06-18 13:54:45 +03:00
regmap_write ( regmap_st , AT91_ST_IER , irqmask ) ;
return 0 ;
2007-07-31 04:41:26 +04:00
}
2006-06-19 18:26:50 +04:00
2007-07-31 04:41:26 +04:00
static int
clkevt32k_next_event ( unsigned long delta , struct clock_event_device * dev )
{
u32 alm ;
int status = 0 ;
2015-03-12 15:07:32 +03:00
unsigned int val ;
2007-07-31 04:41:26 +04:00
BUG_ON ( delta < 2 ) ;
/* The alarm IRQ uses absolute time (now+delta), not the relative
* time ( delta ) in our calling convention . Like all clockevents
* using such " match " hardware , we have a race to defend against .
*
* Our defense here is to have set up the clockevent device so the
* delta is at least two . That way we never end up writing RTAR
* with the value then held in CRTR . . . which would mean the match
* wouldn ' t trigger until 32 seconds later , after CRTR wraps .
*/
alm = read_CRTR ( ) ;
/* Cancel any pending alarm; flush any pending IRQ */
2015-03-12 15:07:32 +03:00
regmap_write ( regmap_st , AT91_ST_RTAR , alm ) ;
regmap_read ( regmap_st , AT91_ST_SR , & val ) ;
2006-12-01 12:15:04 +03:00
2007-07-31 04:41:26 +04:00
/* Schedule alarm by writing RTAR. */
alm + = delta ;
2015-03-12 15:07:32 +03:00
regmap_write ( regmap_st , AT91_ST_RTAR , alm ) ;
2007-07-31 04:41:26 +04:00
return status ;
2006-06-19 18:26:50 +04:00
}
2007-07-31 04:41:26 +04:00
static struct clock_event_device clkevt = {
2015-06-18 13:54:45 +03:00
. name = " at91_tick " ,
. features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT ,
. rating = 150 ,
. set_next_event = clkevt32k_next_event ,
. set_state_shutdown = clkevt32k_shutdown ,
. set_state_periodic = clkevt32k_set_periodic ,
. set_state_oneshot = clkevt32k_set_oneshot ,
. tick_resume = clkevt32k_shutdown ,
2007-07-31 04:41:26 +04:00
} ;
2006-01-09 20:05:41 +03:00
/*
2007-07-31 04:41:26 +04:00
* ST ( system timer ) module supports both clockevents and clocksource .
2006-01-09 20:05:41 +03:00
*/
2016-06-06 20:11:12 +03:00
static int __init atmel_st_timer_init ( struct device_node * node )
2006-01-09 20:05:41 +03:00
{
2015-08-16 12:23:44 +03:00
struct clk * sclk ;
unsigned int sclk_rate , val ;
2015-03-13 13:54:37 +03:00
int irq , ret ;
2015-03-12 15:07:32 +03:00
regmap_st = syscon_node_to_regmap ( node ) ;
2016-06-06 20:11:12 +03:00
if ( IS_ERR ( regmap_st ) ) {
pr_err ( " Unable to get regmap \n " ) ;
return PTR_ERR ( regmap_st ) ;
}
2012-10-28 22:31:07 +04:00
2007-07-31 04:41:26 +04:00
/* Disable all timer interrupts, and clear any pending ones */
2015-03-12 15:07:32 +03:00
regmap_write ( regmap_st , AT91_ST_IDR ,
2007-07-31 04:41:26 +04:00
AT91_ST_PITS | AT91_ST_WDOVF | AT91_ST_RTTINC | AT91_ST_ALMS ) ;
2015-03-12 15:07:32 +03:00
regmap_read ( regmap_st , AT91_ST_SR , & val ) ;
/* Get the interrupts property */
2015-03-13 13:54:37 +03:00
irq = irq_of_parse_and_map ( node , 0 ) ;
2016-06-06 20:11:12 +03:00
if ( ! irq ) {
pr_err ( " Unable to get IRQ from DT \n " ) ;
return - EINVAL ;
}
2006-01-09 20:05:41 +03:00
2006-06-19 18:26:50 +04:00
/* Make IRQs happen for the system timer */
2015-03-13 13:54:37 +03:00
ret = request_irq ( irq , at91rm9200_timer_interrupt ,
IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL ,
" at91_tick " , regmap_st ) ;
2016-06-06 20:11:12 +03:00
if ( ret ) {
pr_err ( " Unable to setup IRQ \n " ) ;
return ret ;
}
2006-01-09 20:05:41 +03:00
2015-08-16 12:23:44 +03:00
sclk = of_clk_get ( node , 0 ) ;
2016-06-06 20:11:12 +03:00
if ( IS_ERR ( sclk ) ) {
pr_err ( " Unable to get slow clock \n " ) ;
return PTR_ERR ( sclk ) ;
}
2015-08-16 12:23:44 +03:00
2016-06-06 20:11:12 +03:00
ret = clk_prepare_enable ( sclk ) ;
if ( ret ) {
pr_err ( " Could not enable slow clock \n " ) ;
return ret ;
}
2015-08-16 12:23:44 +03:00
sclk_rate = clk_get_rate ( sclk ) ;
2016-06-06 20:11:12 +03:00
if ( ! sclk_rate ) {
pr_err ( " Invalid slow clock rate \n " ) ;
return - EINVAL ;
}
2015-08-16 12:23:44 +03:00
timer_latch = ( sclk_rate + HZ / 2 ) / HZ ;
2007-07-31 04:41:26 +04:00
/* The 32KiHz "Slow Clock" (tick every 30517.58 nanoseconds) is used
* directly for the clocksource and all clockevents , after adjusting
* its prescaler from the 1 Hz default .
*/
2015-03-12 15:07:32 +03:00
regmap_write ( regmap_st , AT91_ST_RTMR , 1 ) ;
2006-01-09 20:05:41 +03:00
2007-07-31 04:41:26 +04:00
/* Setup timer clockevent, with minimum of two ticks (important!!) */
2008-12-13 13:50:26 +03:00
clkevt . cpumask = cpumask_of ( 0 ) ;
2015-08-16 12:23:44 +03:00
clockevents_config_and_register ( & clkevt , sclk_rate ,
2013-10-08 18:38:53 +04:00
2 , AT91_ST_ALMV ) ;
2006-06-19 18:26:50 +04:00
2007-07-31 04:41:26 +04:00
/* register clocksource */
2016-06-06 20:11:12 +03:00
return clocksource_register_hz ( & clk32k , sclk_rate ) ;
2006-01-09 20:05:41 +03:00
}
2016-06-07 01:27:44 +03:00
CLOCKSOURCE_OF_DECLARE ( atmel_st_timer , " atmel,at91rm9200-st " ,
2015-03-12 15:07:30 +03:00
atmel_st_timer_init ) ;