2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2006-12-01 11:04:47 +03:00
/*
2008-04-16 23:43:49 +04:00
* at91sam926x_time . c - Periodic Interval Timer ( PIT ) for at91sam926x
2006-12-01 11:04:47 +03:00
*
* Copyright ( C ) 2005 - 2006 M . Amine SAYA , ATMEL Rousset , France
* Revision 2005 M . Nicolas Diremdjian , ATMEL Rousset , France
2008-04-16 23:43:49 +04:00
* Converted to ClockSource / ClockEvents by David Brownell .
2006-12-01 11:04:47 +03:00
*/
2014-07-01 13:33:14 +04:00
2014-07-01 13:33:21 +04:00
# define pr_fmt(fmt) "AT91: PIT: " fmt
2014-07-01 13:33:14 +04:00
# include <linux/clk.h>
# include <linux/clockchips.h>
2006-12-01 11:04:47 +03:00
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/kernel.h>
2012-02-27 14:19:34 +04:00
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_irq.h>
2014-07-01 13:33:23 +04:00
# include <linux/slab.h>
2006-12-01 11:04:47 +03:00
2012-10-30 04:09:09 +04:00
# define AT91_PIT_MR 0x00 /* Mode Register */
2014-07-01 13:33:14 +04:00
# define AT91_PIT_PITIEN BIT(25) /* Timer Interrupt Enable */
# define AT91_PIT_PITEN BIT(24) /* Timer Enabled */
# define AT91_PIT_PIV GENMASK(19, 0) /* Periodic Interval Value */
2012-10-30 04:09:09 +04:00
# define AT91_PIT_SR 0x04 /* Status Register */
2014-07-01 13:33:14 +04:00
# define AT91_PIT_PITS BIT(0) /* Timer Status */
2012-10-30 04:09:09 +04:00
# define AT91_PIT_PIVR 0x08 /* Periodic Interval Value Register */
# define AT91_PIT_PIIR 0x0c /* Periodic Interval Image Register */
2014-07-01 13:33:14 +04:00
# define AT91_PIT_PICNT GENMASK(31, 20) /* Interval Counter */
# define AT91_PIT_CPIV GENMASK(19, 0) /* Inverval Value */
2006-12-01 11:04:47 +03:00
# define PIT_CPIV(x) ((x) & AT91_PIT_CPIV)
# define PIT_PICNT(x) (((x) & AT91_PIT_PICNT) >> 20)
2014-07-01 13:33:23 +04:00
struct pit_data {
struct clock_event_device clkevt ;
struct clocksource clksrc ;
2008-04-16 23:43:49 +04:00
2014-07-01 13:33:23 +04:00
void __iomem * base ;
u32 cycle ;
u32 cnt ;
unsigned int irq ;
struct clk * mck ;
} ;
static inline struct pit_data * clksrc_to_pit_data ( struct clocksource * clksrc )
2011-09-18 18:29:50 +04:00
{
2014-07-01 13:33:23 +04:00
return container_of ( clksrc , struct pit_data , clksrc ) ;
2011-09-18 18:29:50 +04:00
}
2014-07-01 13:33:23 +04:00
static inline struct pit_data * clkevt_to_pit_data ( struct clock_event_device * clkevt )
2011-09-18 18:29:50 +04:00
{
2014-07-01 13:33:23 +04:00
return container_of ( clkevt , struct pit_data , clkevt ) ;
}
static inline unsigned int pit_read ( void __iomem * base , unsigned int reg_offset )
{
2015-03-30 23:17:09 +03:00
return readl_relaxed ( base + reg_offset ) ;
2014-07-01 13:33:23 +04:00
}
static inline void pit_write ( void __iomem * base , unsigned int reg_offset , unsigned long value )
{
2015-03-30 23:17:09 +03:00
writel_relaxed ( value , base + reg_offset ) ;
2011-09-18 18:29:50 +04:00
}
2008-04-16 23:43:49 +04:00
2006-12-01 11:04:47 +03:00
/*
2008-04-16 23:43:49 +04:00
* Clocksource : just a monotonic counter of MCK / 16 cycles .
* We don ' t care whether or not PIT irqs are enabled .
2006-12-01 11:04:47 +03:00
*/
2016-12-21 22:32:01 +03:00
static u64 read_pit_clk ( struct clocksource * cs )
2006-12-01 11:04:47 +03:00
{
2014-07-01 13:33:23 +04:00
struct pit_data * data = clksrc_to_pit_data ( cs ) ;
2008-04-16 23:43:49 +04:00
unsigned long flags ;
u32 elapsed ;
u32 t ;
raw_local_irq_save ( flags ) ;
2014-07-01 13:33:23 +04:00
elapsed = data - > cnt ;
t = pit_read ( data - > base , AT91_PIT_PIIR ) ;
2008-04-16 23:43:49 +04:00
raw_local_irq_restore ( flags ) ;
2014-07-01 13:33:23 +04:00
elapsed + = PIT_PICNT ( t ) * data - > cycle ;
2008-04-16 23:43:49 +04:00
elapsed + = PIT_CPIV ( t ) ;
return elapsed ;
}
2015-06-18 13:54:44 +03:00
static int pit_clkevt_shutdown ( struct clock_event_device * dev )
{
struct pit_data * data = clkevt_to_pit_data ( dev ) ;
/* disable irq, leaving the clocksource active */
pit_write ( data - > base , AT91_PIT_MR , ( data - > cycle - 1 ) | AT91_PIT_PITEN ) ;
return 0 ;
}
2008-04-16 23:43:49 +04:00
/*
* Clockevent device : interrupts every 1 / HZ ( = = pit_cycles * MCK / 16 )
*/
2015-06-18 13:54:44 +03:00
static int pit_clkevt_set_periodic ( struct clock_event_device * dev )
2008-04-16 23:43:49 +04:00
{
2014-07-01 13:33:23 +04:00
struct pit_data * data = clkevt_to_pit_data ( dev ) ;
2015-06-18 13:54:44 +03:00
/* update clocksource counter */
data - > cnt + = data - > cycle * PIT_PICNT ( pit_read ( data - > base , AT91_PIT_PIVR ) ) ;
pit_write ( data - > base , AT91_PIT_MR ,
( data - > cycle - 1 ) | AT91_PIT_PITEN | AT91_PIT_PITIEN ) ;
return 0 ;
2006-12-01 11:04:47 +03:00
}
2012-11-08 03:32:41 +04:00
static void at91sam926x_pit_suspend ( struct clock_event_device * cedev )
{
2014-07-01 13:33:23 +04:00
struct pit_data * data = clkevt_to_pit_data ( cedev ) ;
2012-11-08 03:32:41 +04:00
/* Disable timer */
2014-07-01 13:33:23 +04:00
pit_write ( data - > base , AT91_PIT_MR , 0 ) ;
2012-11-08 03:32:41 +04:00
}
2014-07-01 13:33:23 +04:00
static void at91sam926x_pit_reset ( struct pit_data * data )
2012-11-08 03:32:41 +04:00
{
/* Disable timer and irqs */
2014-07-01 13:33:23 +04:00
pit_write ( data - > base , AT91_PIT_MR , 0 ) ;
2012-11-08 03:32:41 +04:00
/* Clear any pending interrupts, wait for PIT to stop counting */
2014-07-01 13:33:23 +04:00
while ( PIT_CPIV ( pit_read ( data - > base , AT91_PIT_PIVR ) ) ! = 0 )
2012-11-08 03:32:41 +04:00
cpu_relax ( ) ;
/* Start PIT but don't enable IRQ */
2014-07-01 13:33:23 +04:00
pit_write ( data - > base , AT91_PIT_MR ,
( data - > cycle - 1 ) | AT91_PIT_PITEN ) ;
2012-11-08 03:32:41 +04:00
}
static void at91sam926x_pit_resume ( struct clock_event_device * cedev )
{
2014-07-01 13:33:23 +04:00
struct pit_data * data = clkevt_to_pit_data ( cedev ) ;
2008-04-16 23:43:49 +04:00
2014-07-01 13:33:23 +04:00
at91sam926x_pit_reset ( data ) ;
}
2008-04-16 23:43:49 +04:00
2006-12-01 11:04:47 +03:00
/*
* IRQ handler for the timer .
*/
2008-04-16 23:43:49 +04:00
static irqreturn_t at91sam926x_pit_interrupt ( int irq , void * dev_id )
2006-12-01 11:04:47 +03:00
{
2014-07-01 13:33:23 +04:00
struct pit_data * data = dev_id ;
2008-04-16 23:43:49 +04:00
/* The PIT interrupt may be disabled, and is shared */
2015-06-18 13:54:44 +03:00
if ( clockevent_state_periodic ( & data - > clkevt ) & &
2014-07-01 13:33:23 +04:00
( pit_read ( data - > base , AT91_PIT_SR ) & AT91_PIT_PITS ) ) {
2008-04-16 23:43:49 +04:00
/* Get number of ticks performed before irq, and ack it */
2016-09-09 14:13:50 +03:00
data - > cnt + = data - > cycle * PIT_PICNT ( pit_read ( data - > base ,
AT91_PIT_PIVR ) ) ;
data - > clkevt . event_handler ( & data - > clkevt ) ;
2006-12-01 11:04:47 +03:00
return IRQ_HANDLED ;
2008-04-16 23:43:49 +04:00
}
return IRQ_NONE ;
2006-12-01 11:04:47 +03:00
}
/*
2008-04-16 23:43:49 +04:00
* Set up both clocksource and clockevent support .
2006-12-01 11:04:47 +03:00
*/
2016-09-09 14:13:48 +03:00
static int __init at91sam926x_pit_dt_init ( struct device_node * node )
2006-12-01 11:04:47 +03:00
{
2016-09-09 14:13:48 +03:00
unsigned long pit_rate ;
unsigned bits ;
int ret ;
struct pit_data * data ;
data = kzalloc ( sizeof ( * data ) , GFP_KERNEL ) ;
if ( ! data )
return - ENOMEM ;
data - > base = of_iomap ( node , 0 ) ;
if ( ! data - > base ) {
pr_err ( " Could not map PIT address \n " ) ;
2018-04-25 13:14:39 +03:00
ret = - ENXIO ;
goto exit ;
2016-09-09 14:13:48 +03:00
}
data - > mck = of_clk_get ( node , 0 ) ;
if ( IS_ERR ( data - > mck ) ) {
pr_err ( " Unable to get mck clk \n " ) ;
2018-04-25 13:14:39 +03:00
ret = PTR_ERR ( data - > mck ) ;
goto exit ;
2016-09-09 14:13:48 +03:00
}
ret = clk_prepare_enable ( data - > mck ) ;
if ( ret ) {
pr_err ( " Unable to enable mck \n " ) ;
2018-04-25 13:14:39 +03:00
goto exit ;
2016-09-09 14:13:48 +03:00
}
/* Get the interrupts property */
data - > irq = irq_of_parse_and_map ( node , 0 ) ;
if ( ! data - > irq ) {
pr_err ( " Unable to get IRQ from DT \n " ) ;
2018-04-25 13:14:39 +03:00
ret = - EINVAL ;
goto exit ;
2016-09-09 14:13:48 +03:00
}
2008-04-16 23:43:49 +04:00
/*
* Use our actual MCK to figure out how many MCK / 16 ticks per
* 1 / HZ period ( instead of a compile - time constant LATCH ) .
*/
2014-07-01 13:33:23 +04:00
pit_rate = clk_get_rate ( data - > mck ) / 16 ;
data - > cycle = DIV_ROUND_CLOSEST ( pit_rate , HZ ) ;
WARN_ON ( ( ( data - > cycle - 1 ) & ~ AT91_PIT_PIV ) ! = 0 ) ;
2006-12-01 11:04:47 +03:00
2008-04-16 23:43:49 +04:00
/* Initialize and enable the timer */
2014-07-01 13:33:23 +04:00
at91sam926x_pit_reset ( data ) ;
2008-04-16 23:43:49 +04:00
/*
* Register clocksource . The high order bits of PIV are unused ,
* so this isn ' t a 32 - bit counter unless we get clockevent irqs .
*/
2014-07-01 13:33:23 +04:00
bits = 12 /* PICNT */ + ilog2 ( data - > cycle ) /* PIV */ ;
data - > clksrc . mask = CLOCKSOURCE_MASK ( bits ) ;
data - > clksrc . name = " pit " ;
data - > clksrc . rating = 175 ;
2015-08-04 12:59:42 +03:00
data - > clksrc . read = read_pit_clk ;
data - > clksrc . flags = CLOCK_SOURCE_IS_CONTINUOUS ;
2016-06-06 20:10:55 +03:00
ret = clocksource_register_hz ( & data - > clksrc , pit_rate ) ;
if ( ret ) {
2017-03-09 12:47:10 +03:00
pr_err ( " Failed to register clocksource \n " ) ;
2018-04-25 13:14:39 +03:00
goto exit ;
2016-06-06 20:10:55 +03:00
}
2008-04-16 23:43:49 +04:00
/* Set up irq handler */
2014-07-01 13:33:23 +04:00
ret = request_irq ( data - > irq , at91sam926x_pit_interrupt ,
2014-07-01 13:33:22 +04:00
IRQF_SHARED | IRQF_TIMER | IRQF_IRQPOLL ,
2014-07-01 13:33:23 +04:00
" at91_tick " , data ) ;
2016-06-06 20:10:55 +03:00
if ( ret ) {
pr_err ( " Unable to setup IRQ \n " ) ;
2018-04-25 13:14:39 +03:00
clocksource_unregister ( & data - > clksrc ) ;
goto exit ;
2016-06-06 20:10:55 +03:00
}
2008-04-16 23:43:49 +04:00
/* Set up and register clockevents */
2014-07-01 13:33:23 +04:00
data - > clkevt . name = " pit " ;
data - > clkevt . features = CLOCK_EVT_FEAT_PERIODIC ;
data - > clkevt . shift = 32 ;
data - > clkevt . mult = div_sc ( pit_rate , NSEC_PER_SEC , data - > clkevt . shift ) ;
data - > clkevt . rating = 100 ;
data - > clkevt . cpumask = cpumask_of ( 0 ) ;
2015-06-18 13:54:44 +03:00
data - > clkevt . set_state_shutdown = pit_clkevt_shutdown ;
data - > clkevt . set_state_periodic = pit_clkevt_set_periodic ;
2014-07-01 13:33:23 +04:00
data - > clkevt . resume = at91sam926x_pit_resume ;
data - > clkevt . suspend = at91sam926x_pit_suspend ;
clockevents_register_device ( & data - > clkevt ) ;
2016-06-06 20:10:55 +03:00
return 0 ;
2018-04-25 13:14:39 +03:00
exit :
kfree ( data ) ;
return ret ;
2006-12-01 11:04:47 +03:00
}
2017-05-26 17:56:11 +03:00
TIMER_OF_DECLARE ( at91sam926x_pit , " atmel,at91sam9260-pit " ,
2014-07-01 13:33:18 +04:00
at91sam926x_pit_dt_init ) ;