2012-11-12 18:07:50 +04:00
/*
* Allwinner A1X SoCs timer handling .
*
* Copyright ( C ) 2012 Maxime Ripard
*
* Maxime Ripard < maxime . ripard @ free - electrons . com >
*
* Based on code from
* Allwinner Technology Co . , Ltd . < www . allwinnertech . com >
* Benn Huang < benn @ allwinnertech . com >
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
# include <linux/clk.h>
# include <linux/clockchips.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/irqreturn.h>
2013-07-16 18:45:37 +04:00
# include <linux/sched_clock.h>
2012-11-12 18:07:50 +04:00
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_irq.h>
2017-06-07 00:07:51 +03:00
# include "timer-of.h"
2013-03-10 20:03:46 +04:00
# define TIMER_IRQ_EN_REG 0x00
2013-07-16 18:45:37 +04:00
# define TIMER_IRQ_EN(val) BIT(val)
2012-11-12 18:07:50 +04:00
# define TIMER_IRQ_ST_REG 0x04
2013-03-10 20:03:46 +04:00
# define TIMER_CTL_REG(val) (0x10 * val + 0x10)
2013-07-16 18:45:37 +04:00
# define TIMER_CTL_ENABLE BIT(0)
2013-07-16 18:45:37 +04:00
# define TIMER_CTL_RELOAD BIT(1)
2013-07-16 18:45:38 +04:00
# define TIMER_CTL_CLK_SRC(val) (((val) & 0x3) << 2)
# define TIMER_CTL_CLK_SRC_OSC24M (1)
# define TIMER_CTL_CLK_PRES(val) (((val) & 0x7) << 4)
2013-07-16 18:45:37 +04:00
# define TIMER_CTL_ONESHOT BIT(7)
2013-07-16 18:45:37 +04:00
# define TIMER_INTVAL_REG(val) (0x10 * (val) + 0x14)
# define TIMER_CNTVAL_REG(val) (0x10 * (val) + 0x18)
2012-11-12 18:07:50 +04:00
2013-10-14 23:07:47 +04:00
# define TIMER_SYNC_TICKS 3
2013-07-16 18:45:38 +04:00
/*
* When we disable a timer , we need to wait at least for 2 cycles of
* the timer source clock . We will use for that the clocksource timer
* that is already setup and runs at the same frequency than the other
* timers , and we never will be disabled .
*/
2017-06-07 00:07:51 +03:00
static void sun4i_clkevt_sync ( void __iomem * base )
2013-07-16 18:45:38 +04:00
{
2017-06-07 00:07:51 +03:00
u32 old = readl ( base + TIMER_CNTVAL_REG ( 1 ) ) ;
2013-07-16 18:45:38 +04:00
2017-06-07 00:07:51 +03:00
while ( ( old - readl ( base + TIMER_CNTVAL_REG ( 1 ) ) ) < TIMER_SYNC_TICKS )
2013-07-16 18:45:38 +04:00
cpu_relax ( ) ;
}
2017-06-07 00:07:51 +03:00
static void sun4i_clkevt_time_stop ( void __iomem * base , u8 timer )
2013-07-16 18:45:38 +04:00
{
2017-06-07 00:07:51 +03:00
u32 val = readl ( base + TIMER_CTL_REG ( timer ) ) ;
writel ( val & ~ TIMER_CTL_ENABLE , base + TIMER_CTL_REG ( timer ) ) ;
sun4i_clkevt_sync ( base ) ;
2013-07-16 18:45:38 +04:00
}
2017-06-07 00:07:51 +03:00
static void sun4i_clkevt_time_setup ( void __iomem * base , u8 timer ,
unsigned long delay )
2013-07-16 18:45:38 +04:00
{
2017-06-07 00:07:51 +03:00
writel ( delay , base + TIMER_INTVAL_REG ( timer ) ) ;
2013-07-16 18:45:38 +04:00
}
2017-06-07 00:07:51 +03:00
static void sun4i_clkevt_time_start ( void __iomem * base , u8 timer ,
bool periodic )
2013-07-16 18:45:38 +04:00
{
2017-06-07 00:07:51 +03:00
u32 val = readl ( base + TIMER_CTL_REG ( timer ) ) ;
2013-07-16 18:45:38 +04:00
if ( periodic )
val & = ~ TIMER_CTL_ONESHOT ;
else
val | = TIMER_CTL_ONESHOT ;
2013-07-16 18:45:38 +04:00
writel ( val | TIMER_CTL_ENABLE | TIMER_CTL_RELOAD ,
2017-06-07 00:07:51 +03:00
base + TIMER_CTL_REG ( timer ) ) ;
2013-07-16 18:45:38 +04:00
}
2015-06-18 13:54:37 +03:00
static int sun4i_clkevt_shutdown ( struct clock_event_device * evt )
2012-11-12 18:07:50 +04:00
{
2017-06-07 00:07:51 +03:00
struct timer_of * to = to_timer_of ( evt ) ;
sun4i_clkevt_time_stop ( timer_of_base ( to ) , 0 ) ;
2015-06-18 13:54:37 +03:00
return 0 ;
}
static int sun4i_clkevt_set_oneshot ( struct clock_event_device * evt )
{
2017-06-07 00:07:51 +03:00
struct timer_of * to = to_timer_of ( evt ) ;
sun4i_clkevt_time_stop ( timer_of_base ( to ) , 0 ) ;
sun4i_clkevt_time_start ( timer_of_base ( to ) , 0 , false ) ;
2015-06-18 13:54:37 +03:00
return 0 ;
}
static int sun4i_clkevt_set_periodic ( struct clock_event_device * evt )
{
2017-06-07 00:07:51 +03:00
struct timer_of * to = to_timer_of ( evt ) ;
sun4i_clkevt_time_stop ( timer_of_base ( to ) , 0 ) ;
sun4i_clkevt_time_setup ( timer_of_base ( to ) , 0 , timer_of_period ( to ) ) ;
sun4i_clkevt_time_start ( timer_of_base ( to ) , 0 , true ) ;
2015-06-18 13:54:37 +03:00
return 0 ;
2012-11-12 18:07:50 +04:00
}
2013-03-24 14:49:25 +04:00
static int sun4i_clkevt_next_event ( unsigned long evt ,
2017-06-07 00:07:51 +03:00
struct clock_event_device * clkevt )
2012-11-12 18:07:50 +04:00
{
2017-06-07 00:07:51 +03:00
struct timer_of * to = to_timer_of ( clkevt ) ;
sun4i_clkevt_time_stop ( timer_of_base ( to ) , 0 ) ;
sun4i_clkevt_time_setup ( timer_of_base ( to ) , 0 , evt - TIMER_SYNC_TICKS ) ;
sun4i_clkevt_time_start ( timer_of_base ( to ) , 0 , false ) ;
2012-11-12 18:07:50 +04:00
return 0 ;
}
2017-06-07 00:07:51 +03:00
static void sun4i_timer_clear_interrupt ( void __iomem * base )
2016-08-25 09:26:59 +03:00
{
2017-06-07 00:07:51 +03:00
writel ( TIMER_IRQ_EN ( 0 ) , base + TIMER_IRQ_ST_REG ) ;
2016-08-25 09:26:59 +03:00
}
2012-11-12 18:07:50 +04:00
2013-03-24 14:49:25 +04:00
static irqreturn_t sun4i_timer_interrupt ( int irq , void * dev_id )
2012-11-12 18:07:50 +04:00
{
struct clock_event_device * evt = ( struct clock_event_device * ) dev_id ;
2017-06-07 00:07:51 +03:00
struct timer_of * to = to_timer_of ( evt ) ;
2012-11-12 18:07:50 +04:00
2017-06-07 00:07:51 +03:00
sun4i_timer_clear_interrupt ( timer_of_base ( to ) ) ;
2012-11-12 18:07:50 +04:00
evt - > event_handler ( evt ) ;
return IRQ_HANDLED ;
}
2017-06-07 00:07:51 +03:00
static struct timer_of to = {
. flags = TIMER_OF_IRQ | TIMER_OF_CLOCK | TIMER_OF_BASE ,
. clkevt = {
. name = " sun4i_tick " ,
. rating = 350 ,
. features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT ,
. set_state_shutdown = sun4i_clkevt_shutdown ,
. set_state_periodic = sun4i_clkevt_set_periodic ,
. set_state_oneshot = sun4i_clkevt_set_oneshot ,
. tick_resume = sun4i_clkevt_shutdown ,
. set_next_event = sun4i_clkevt_next_event ,
. cpumask = cpu_possible_mask ,
} ,
. of_irq = {
. handler = sun4i_timer_interrupt ,
. flags = IRQF_TIMER | IRQF_IRQPOLL ,
} ,
2012-11-12 18:07:50 +04:00
} ;
2013-11-20 03:47:32 +04:00
static u64 notrace sun4i_timer_sched_read ( void )
2013-07-16 18:45:37 +04:00
{
2017-06-07 00:07:51 +03:00
return ~ readl ( timer_of_base ( & to ) + TIMER_CNTVAL_REG ( 1 ) ) ;
2013-07-16 18:45:37 +04:00
}
2016-06-06 18:59:09 +03:00
static int __init sun4i_timer_init ( struct device_node * node )
2012-11-12 18:07:50 +04:00
{
2017-06-07 00:07:51 +03:00
int ret ;
2012-11-12 18:07:50 +04:00
u32 val ;
2017-06-07 00:07:51 +03:00
ret = timer_of_init ( node , & to ) ;
if ( ret )
2016-06-06 18:59:09 +03:00
return ret ;
2012-11-12 18:07:50 +04:00
2017-06-07 00:07:51 +03:00
writel ( ~ 0 , timer_of_base ( & to ) + TIMER_INTVAL_REG ( 1 ) ) ;
2013-07-16 18:45:37 +04:00
writel ( TIMER_CTL_ENABLE | TIMER_CTL_RELOAD |
TIMER_CTL_CLK_SRC ( TIMER_CTL_CLK_SRC_OSC24M ) ,
2017-06-07 00:07:51 +03:00
timer_of_base ( & to ) + TIMER_CTL_REG ( 1 ) ) ;
2013-07-16 18:45:37 +04:00
2015-03-30 23:17:10 +03:00
/*
* sched_clock_register does not have priorities , and on sun6i and
* later there is a better sched_clock registered by arm_arch_timer . c
*/
if ( of_machine_is_compatible ( " allwinner,sun4i-a10 " ) | |
of_machine_is_compatible ( " allwinner,sun5i-a13 " ) | |
2019-02-11 12:21:08 +03:00
of_machine_is_compatible ( " allwinner,sun5i-a10s " ) | |
of_machine_is_compatible ( " allwinner,suniv-f1c100s " ) )
2017-06-07 00:07:51 +03:00
sched_clock_register ( sun4i_timer_sched_read , 32 ,
timer_of_rate ( & to ) ) ;
2015-03-30 23:17:10 +03:00
2017-06-07 00:07:51 +03:00
ret = clocksource_mmio_init ( timer_of_base ( & to ) + TIMER_CNTVAL_REG ( 1 ) ,
node - > name , timer_of_rate ( & to ) , 350 , 32 ,
clocksource_mmio_readl_down ) ;
2016-06-06 18:59:09 +03:00
if ( ret ) {
2017-03-09 12:47:10 +03:00
pr_err ( " Failed to register clocksource \n " ) ;
2016-06-06 18:59:09 +03:00
return ret ;
}
2013-07-16 18:45:37 +04:00
2013-07-16 18:45:38 +04:00
writel ( TIMER_CTL_CLK_SRC ( TIMER_CTL_CLK_SRC_OSC24M ) ,
2017-06-07 00:07:51 +03:00
timer_of_base ( & to ) + TIMER_CTL_REG ( 0 ) ) ;
2012-11-12 18:07:50 +04:00
2013-12-02 13:29:35 +04:00
/* Make sure timer is stopped before playing with interrupts */
2017-06-07 00:07:51 +03:00
sun4i_clkevt_time_stop ( timer_of_base ( & to ) , 0 ) ;
2013-12-02 13:29:35 +04:00
2016-08-25 09:26:59 +03:00
/* clear timer0 interrupt */
2017-06-07 00:07:51 +03:00
sun4i_timer_clear_interrupt ( timer_of_base ( & to ) ) ;
2014-11-19 01:59:33 +03:00
2017-06-07 00:07:51 +03:00
clockevents_config_and_register ( & to . clkevt , timer_of_rate ( & to ) ,
2014-11-19 01:59:33 +03:00
TIMER_SYNC_TICKS , 0xffffffff ) ;
2012-11-12 18:07:50 +04:00
/* Enable timer0 interrupt */
2017-06-07 00:07:51 +03:00
val = readl ( timer_of_base ( & to ) + TIMER_IRQ_EN_REG ) ;
writel ( val | TIMER_IRQ_EN ( 0 ) , timer_of_base ( & to ) + TIMER_IRQ_EN_REG ) ;
2016-06-06 18:59:09 +03:00
return ret ;
2012-11-12 18:07:50 +04:00
}
2017-05-26 17:56:11 +03:00
TIMER_OF_DECLARE ( sun4i , " allwinner,sun4i-a10-timer " ,
2013-03-24 14:49:25 +04:00
sun4i_timer_init ) ;
2019-07-22 11:12:21 +03:00
TIMER_OF_DECLARE ( sun8i_a23 , " allwinner,sun8i-a23-timer " ,
sun4i_timer_init ) ;
TIMER_OF_DECLARE ( sun8i_v3s , " allwinner,sun8i-v3s-timer " ,
sun4i_timer_init ) ;
2019-02-11 12:21:08 +03:00
TIMER_OF_DECLARE ( suniv , " allwinner,suniv-f1c100s-timer " ,
sun4i_timer_init ) ;