2019-05-27 09:55:06 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2014-07-18 13:36:43 +04:00
/*
* Mediatek SoCs General - Purpose Timer handling .
*
* Copyright ( C ) 2014 Matthias Brugger
*
* Matthias Brugger < matthias . bgg @ gmail . com >
*/
2015-10-26 02:21:22 +03:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2014-07-18 13:36:43 +04:00
# include <linux/clockchips.h>
2018-07-06 02:11:27 +03:00
# include <linux/clocksource.h>
2014-07-18 13:36:43 +04:00
# include <linux/interrupt.h>
# include <linux/irqreturn.h>
2015-07-13 12:32:46 +03:00
# include <linux/sched_clock.h>
2014-07-18 13:36:43 +04:00
# include <linux/slab.h>
2018-07-06 02:11:27 +03:00
# include "timer-of.h"
2014-07-18 13:36:43 +04:00
2018-07-06 02:11:26 +03:00
# define TIMER_CLK_EVT (1)
# define TIMER_CLK_SRC (2)
# define TIMER_SYNC_TICKS (3)
/* gpt */
# define GPT_IRQ_EN_REG 0x00
# define GPT_IRQ_ENABLE(val) BIT((val) - 1)
# define GPT_IRQ_ACK_REG 0x08
# define GPT_IRQ_ACK(val) BIT((val) - 1)
# define GPT_CTRL_REG(val) (0x10 * (val))
# define GPT_CTRL_OP(val) (((val) & 0x3) << 4)
# define GPT_CTRL_OP_ONESHOT (0)
# define GPT_CTRL_OP_REPEAT (1)
# define GPT_CTRL_OP_FREERUN (3)
# define GPT_CTRL_CLEAR (2)
# define GPT_CTRL_ENABLE (1)
# define GPT_CTRL_DISABLE (0)
# define GPT_CLK_REG(val) (0x04 + (0x10 * (val)))
# define GPT_CLK_SRC(val) (((val) & 0x1) << 4)
# define GPT_CLK_SRC_SYS13M (0)
# define GPT_CLK_SRC_RTC32K (1)
# define GPT_CLK_DIV1 (0x0)
# define GPT_CLK_DIV2 (0x1)
# define GPT_CNT_REG(val) (0x08 + (0x10 * (val)))
# define GPT_CMP_REG(val) (0x0C + (0x10 * (val)))
2014-07-18 13:36:43 +04:00
2018-07-06 02:11:28 +03:00
/* system timer */
# define SYST_BASE (0x40)
# define SYST_CON (SYST_BASE + 0x0)
# define SYST_VAL (SYST_BASE + 0x4)
# define SYST_CON_REG(to) (timer_of_base(to) + SYST_CON)
# define SYST_VAL_REG(to) (timer_of_base(to) + SYST_VAL)
/*
* SYST_CON_EN : Clock enable . Shall be set to
* - Start timer countdown .
* - Allow timeout ticks being updated .
* - Allow changing interrupt functions .
*
* SYST_CON_IRQ_EN : Set to allow interrupt .
*
* SYST_CON_IRQ_CLR : Set to clear interrupt .
*/
# define SYST_CON_EN BIT(0)
# define SYST_CON_IRQ_EN BIT(1)
# define SYST_CON_IRQ_CLR BIT(4)
2015-07-13 12:32:46 +03:00
static void __iomem * gpt_sched_reg __read_mostly ;
2018-07-06 02:11:28 +03:00
static void mtk_syst_ack_irq ( struct timer_of * to )
{
/* Clear and disable interrupt */
writel ( SYST_CON_IRQ_CLR | SYST_CON_EN , SYST_CON_REG ( to ) ) ;
}
static irqreturn_t mtk_syst_handler ( int irq , void * dev_id )
{
struct clock_event_device * clkevt = dev_id ;
struct timer_of * to = to_timer_of ( clkevt ) ;
mtk_syst_ack_irq ( to ) ;
clkevt - > event_handler ( clkevt ) ;
return IRQ_HANDLED ;
}
static int mtk_syst_clkevt_next_event ( unsigned long ticks ,
struct clock_event_device * clkevt )
{
struct timer_of * to = to_timer_of ( clkevt ) ;
/* Enable clock to allow timeout tick update later */
writel ( SYST_CON_EN , SYST_CON_REG ( to ) ) ;
/*
* Write new timeout ticks . Timer shall start countdown
* after timeout ticks are updated .
*/
writel ( ticks , SYST_VAL_REG ( to ) ) ;
/* Enable interrupt */
writel ( SYST_CON_EN | SYST_CON_IRQ_EN , SYST_CON_REG ( to ) ) ;
return 0 ;
}
static int mtk_syst_clkevt_shutdown ( struct clock_event_device * clkevt )
{
/* Disable timer */
writel ( 0 , SYST_CON_REG ( to_timer_of ( clkevt ) ) ) ;
return 0 ;
}
static int mtk_syst_clkevt_resume ( struct clock_event_device * clkevt )
{
return mtk_syst_clkevt_shutdown ( clkevt ) ;
}
static int mtk_syst_clkevt_oneshot ( struct clock_event_device * clkevt )
{
return 0 ;
}
2018-07-06 02:11:26 +03:00
static u64 notrace mtk_gpt_read_sched_clock ( void )
2015-07-13 12:32:46 +03:00
{
return readl_relaxed ( gpt_sched_reg ) ;
}
2018-07-06 02:11:27 +03:00
static void mtk_gpt_clkevt_time_stop ( struct timer_of * to , u8 timer )
2014-07-18 13:36:43 +04:00
{
u32 val ;
2018-07-06 02:11:27 +03:00
val = readl ( timer_of_base ( to ) + GPT_CTRL_REG ( timer ) ) ;
writel ( val & ~ GPT_CTRL_ENABLE , timer_of_base ( to ) +
GPT_CTRL_REG ( timer ) ) ;
2014-07-18 13:36:43 +04:00
}
2018-07-06 02:11:27 +03:00
static void mtk_gpt_clkevt_time_setup ( struct timer_of * to ,
unsigned long delay , u8 timer )
2014-07-18 13:36:43 +04:00
{
2018-07-06 02:11:27 +03:00
writel ( delay , timer_of_base ( to ) + GPT_CMP_REG ( timer ) ) ;
2014-07-18 13:36:43 +04:00
}
2018-07-06 02:11:27 +03:00
static void mtk_gpt_clkevt_time_start ( struct timer_of * to ,
bool periodic , u8 timer )
2014-07-18 13:36:43 +04:00
{
u32 val ;
/* Acknowledge interrupt */
2018-07-06 02:11:27 +03:00
writel ( GPT_IRQ_ACK ( timer ) , timer_of_base ( to ) + GPT_IRQ_ACK_REG ) ;
2014-07-18 13:36:43 +04:00
2018-07-06 02:11:27 +03:00
val = readl ( timer_of_base ( to ) + GPT_CTRL_REG ( timer ) ) ;
2014-07-18 13:36:43 +04:00
/* Clear 2 bit timer operation mode field */
2018-07-06 02:11:26 +03:00
val & = ~ GPT_CTRL_OP ( 0x3 ) ;
2014-07-18 13:36:43 +04:00
if ( periodic )
2018-07-06 02:11:26 +03:00
val | = GPT_CTRL_OP ( GPT_CTRL_OP_REPEAT ) ;
2014-07-18 13:36:43 +04:00
else
2018-07-06 02:11:26 +03:00
val | = GPT_CTRL_OP ( GPT_CTRL_OP_ONESHOT ) ;
2014-07-18 13:36:43 +04:00
2018-07-06 02:11:26 +03:00
writel ( val | GPT_CTRL_ENABLE | GPT_CTRL_CLEAR ,
2018-07-06 02:11:27 +03:00
timer_of_base ( to ) + GPT_CTRL_REG ( timer ) ) ;
2014-07-18 13:36:43 +04:00
}
2018-07-06 02:11:26 +03:00
static int mtk_gpt_clkevt_shutdown ( struct clock_event_device * clk )
2015-06-18 13:54:27 +03:00
{
2018-07-06 02:11:27 +03:00
mtk_gpt_clkevt_time_stop ( to_timer_of ( clk ) , TIMER_CLK_EVT ) ;
2015-06-18 13:54:27 +03:00
return 0 ;
}
2018-07-06 02:11:26 +03:00
static int mtk_gpt_clkevt_set_periodic ( struct clock_event_device * clk )
2014-07-18 13:36:43 +04:00
{
2018-07-06 02:11:27 +03:00
struct timer_of * to = to_timer_of ( clk ) ;
mtk_gpt_clkevt_time_stop ( to , TIMER_CLK_EVT ) ;
mtk_gpt_clkevt_time_setup ( to , to - > of_clk . period , TIMER_CLK_EVT ) ;
mtk_gpt_clkevt_time_start ( to , true , TIMER_CLK_EVT ) ;
2014-07-18 13:36:43 +04:00
2015-06-18 13:54:27 +03:00
return 0 ;
2014-07-18 13:36:43 +04:00
}
2018-07-06 02:11:26 +03:00
static int mtk_gpt_clkevt_next_event ( unsigned long event ,
2018-07-06 02:11:27 +03:00
struct clock_event_device * clk )
2014-07-18 13:36:43 +04:00
{
2018-07-06 02:11:27 +03:00
struct timer_of * to = to_timer_of ( clk ) ;
2014-07-18 13:36:43 +04:00
2018-07-06 02:11:27 +03:00
mtk_gpt_clkevt_time_stop ( to , TIMER_CLK_EVT ) ;
mtk_gpt_clkevt_time_setup ( to , event , TIMER_CLK_EVT ) ;
mtk_gpt_clkevt_time_start ( to , false , TIMER_CLK_EVT ) ;
2014-07-18 13:36:43 +04:00
return 0 ;
}
2018-07-06 02:11:26 +03:00
static irqreturn_t mtk_gpt_interrupt ( int irq , void * dev_id )
2014-07-18 13:36:43 +04:00
{
2018-07-06 02:11:27 +03:00
struct clock_event_device * clkevt = ( struct clock_event_device * ) dev_id ;
struct timer_of * to = to_timer_of ( clkevt ) ;
2014-07-18 13:36:43 +04:00
/* Acknowledge timer0 irq */
2018-07-06 02:11:27 +03:00
writel ( GPT_IRQ_ACK ( TIMER_CLK_EVT ) , timer_of_base ( to ) + GPT_IRQ_ACK_REG ) ;
clkevt - > event_handler ( clkevt ) ;
2014-07-18 13:36:43 +04:00
return IRQ_HANDLED ;
}
static void
2018-07-06 02:11:27 +03:00
__init mtk_gpt_setup ( struct timer_of * to , u8 timer , u8 option )
2014-07-18 13:36:43 +04:00
{
2018-07-06 02:11:26 +03:00
writel ( GPT_CTRL_CLEAR | GPT_CTRL_DISABLE ,
2018-07-06 02:11:27 +03:00
timer_of_base ( to ) + GPT_CTRL_REG ( timer ) ) ;
2014-07-18 13:36:43 +04:00
2018-07-06 02:11:26 +03:00
writel ( GPT_CLK_SRC ( GPT_CLK_SRC_SYS13M ) | GPT_CLK_DIV1 ,
2018-07-06 02:11:27 +03:00
timer_of_base ( to ) + GPT_CLK_REG ( timer ) ) ;
2014-07-18 13:36:43 +04:00
2018-07-06 02:11:27 +03:00
writel ( 0x0 , timer_of_base ( to ) + GPT_CMP_REG ( timer ) ) ;
2014-07-18 13:36:43 +04:00
2018-07-06 02:11:26 +03:00
writel ( GPT_CTRL_OP ( option ) | GPT_CTRL_ENABLE ,
2018-07-06 02:11:27 +03:00
timer_of_base ( to ) + GPT_CTRL_REG ( timer ) ) ;
2014-07-18 13:36:43 +04:00
}
2018-07-06 02:11:27 +03:00
static void mtk_gpt_enable_irq ( struct timer_of * to , u8 timer )
2014-07-18 13:36:43 +04:00
{
u32 val ;
2015-08-24 16:14:30 +03:00
/* Disable all interrupts */
2018-07-06 02:11:27 +03:00
writel ( 0x0 , timer_of_base ( to ) + GPT_IRQ_EN_REG ) ;
2015-08-24 16:14:30 +03:00
/* Acknowledge all spurious pending interrupts */
2018-07-06 02:11:27 +03:00
writel ( 0x3f , timer_of_base ( to ) + GPT_IRQ_ACK_REG ) ;
2015-08-24 16:14:30 +03:00
2018-07-06 02:11:27 +03:00
val = readl ( timer_of_base ( to ) + GPT_IRQ_EN_REG ) ;
2014-07-18 13:36:43 +04:00
writel ( val | GPT_IRQ_ENABLE ( timer ) ,
2018-07-06 02:11:27 +03:00
timer_of_base ( to ) + GPT_IRQ_EN_REG ) ;
2014-07-18 13:36:43 +04:00
}
2018-07-06 02:11:27 +03:00
static struct timer_of to = {
. flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK ,
. clkevt = {
. name = " mtk-clkevt " ,
. rating = 300 ,
. cpumask = cpu_possible_mask ,
} ,
. of_irq = {
. flags = IRQF_TIMER | IRQF_IRQPOLL ,
} ,
} ;
2018-07-06 02:11:28 +03:00
static int __init mtk_syst_init ( struct device_node * node )
{
int ret ;
to . clkevt . features = CLOCK_EVT_FEAT_DYNIRQ | CLOCK_EVT_FEAT_ONESHOT ;
to . clkevt . set_state_shutdown = mtk_syst_clkevt_shutdown ;
to . clkevt . set_state_oneshot = mtk_syst_clkevt_oneshot ;
to . clkevt . tick_resume = mtk_syst_clkevt_resume ;
to . clkevt . set_next_event = mtk_syst_clkevt_next_event ;
to . of_irq . handler = mtk_syst_handler ;
ret = timer_of_init ( node , & to ) ;
if ( ret )
2019-09-19 22:13:15 +03:00
return ret ;
2018-07-06 02:11:28 +03:00
clockevents_config_and_register ( & to . clkevt , timer_of_rate ( & to ) ,
TIMER_SYNC_TICKS , 0xffffffff ) ;
return 0 ;
}
2018-07-06 02:11:26 +03:00
static int __init mtk_gpt_init ( struct device_node * node )
2014-07-18 13:36:43 +04:00
{
2018-07-06 02:11:27 +03:00
int ret ;
to . clkevt . features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT ;
to . clkevt . set_state_shutdown = mtk_gpt_clkevt_shutdown ;
to . clkevt . set_state_periodic = mtk_gpt_clkevt_set_periodic ;
to . clkevt . set_state_oneshot = mtk_gpt_clkevt_shutdown ;
to . clkevt . tick_resume = mtk_gpt_clkevt_shutdown ;
to . clkevt . set_next_event = mtk_gpt_clkevt_next_event ;
to . of_irq . handler = mtk_gpt_interrupt ;
ret = timer_of_init ( node , & to ) ;
if ( ret )
2019-09-19 22:13:15 +03:00
return ret ;
2014-07-18 13:36:43 +04:00
/* Configure clock source */
2018-07-06 02:11:27 +03:00
mtk_gpt_setup ( & to , TIMER_CLK_SRC , GPT_CTRL_OP_FREERUN ) ;
clocksource_mmio_init ( timer_of_base ( & to ) + GPT_CNT_REG ( TIMER_CLK_SRC ) ,
node - > name , timer_of_rate ( & to ) , 300 , 32 ,
clocksource_mmio_readl_up ) ;
gpt_sched_reg = timer_of_base ( & to ) + GPT_CNT_REG ( TIMER_CLK_SRC ) ;
sched_clock_register ( mtk_gpt_read_sched_clock , 32 , timer_of_rate ( & to ) ) ;
2014-07-18 13:36:43 +04:00
/* Configure clock event */
2018-07-06 02:11:27 +03:00
mtk_gpt_setup ( & to , TIMER_CLK_EVT , GPT_CTRL_OP_REPEAT ) ;
clockevents_config_and_register ( & to . clkevt , timer_of_rate ( & to ) ,
TIMER_SYNC_TICKS , 0xffffffff ) ;
2015-02-19 13:41:33 +03:00
2018-07-06 02:11:27 +03:00
mtk_gpt_enable_irq ( & to , TIMER_CLK_EVT ) ;
2015-02-19 13:41:33 +03:00
2016-05-31 18:43:47 +03:00
return 0 ;
2014-07-18 13:36:43 +04:00
}
2018-07-06 02:11:26 +03:00
TIMER_OF_DECLARE ( mtk_mt6577 , " mediatek,mt6577-timer " , mtk_gpt_init ) ;
2018-07-06 02:11:28 +03:00
TIMER_OF_DECLARE ( mtk_mt6765 , " mediatek,mt6765-timer " , mtk_syst_init ) ;