2018-03-08 18:24:58 +03:00
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( C ) 2014 - 2018 Nuvoton Technologies tomer . maimon @ nuvoton . com
* All rights reserved .
*
* Copyright 2017 Google , Inc .
*/
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/err.h>
# include <linux/clk.h>
# include <linux/io.h>
# include <linux/clockchips.h>
# include <linux/of_irq.h>
# include <linux/of_address.h>
# include "timer-of.h"
/* Timers registers */
# define NPCM7XX_REG_TCSR0 0x0 /* Timer 0 Control and Status Register */
# define NPCM7XX_REG_TICR0 0x8 /* Timer 0 Initial Count Register */
# define NPCM7XX_REG_TCSR1 0x4 /* Timer 1 Control and Status Register */
# define NPCM7XX_REG_TICR1 0xc /* Timer 1 Initial Count Register */
# define NPCM7XX_REG_TDR1 0x14 /* Timer 1 Data Register */
# define NPCM7XX_REG_TISR 0x18 /* Timer Interrupt Status Register */
/* Timers control */
# define NPCM7XX_Tx_RESETINT 0x1f
# define NPCM7XX_Tx_PERIOD BIT(27)
# define NPCM7XX_Tx_INTEN BIT(29)
# define NPCM7XX_Tx_COUNTEN BIT(30)
# define NPCM7XX_Tx_ONESHOT 0x0
2019-07-29 20:03:54 +03:00
# define NPCM7XX_Tx_OPER GENMASK(28, 27)
2018-03-08 18:24:58 +03:00
# define NPCM7XX_Tx_MIN_PRESCALE 0x1
# define NPCM7XX_Tx_TDR_MASK_BITS 24
# define NPCM7XX_Tx_MAX_CNT 0xFFFFFF
# define NPCM7XX_T0_CLR_INT 0x1
# define NPCM7XX_Tx_CLR_CSR 0x0
/* Timers operating mode */
# define NPCM7XX_START_PERIODIC_Tx (NPCM7XX_Tx_PERIOD | NPCM7XX_Tx_COUNTEN | \
NPCM7XX_Tx_INTEN | \
NPCM7XX_Tx_MIN_PRESCALE )
# define NPCM7XX_START_ONESHOT_Tx (NPCM7XX_Tx_ONESHOT | NPCM7XX_Tx_COUNTEN | \
NPCM7XX_Tx_INTEN | \
NPCM7XX_Tx_MIN_PRESCALE )
# define NPCM7XX_START_Tx (NPCM7XX_Tx_COUNTEN | NPCM7XX_Tx_PERIOD | \
NPCM7XX_Tx_MIN_PRESCALE )
# define NPCM7XX_DEFAULT_CSR (NPCM7XX_Tx_CLR_CSR | NPCM7XX_Tx_MIN_PRESCALE)
static int npcm7xx_timer_resume ( struct clock_event_device * evt )
{
struct timer_of * to = to_timer_of ( evt ) ;
u32 val ;
val = readl ( timer_of_base ( to ) + NPCM7XX_REG_TCSR0 ) ;
val | = NPCM7XX_Tx_COUNTEN ;
writel ( val , timer_of_base ( to ) + NPCM7XX_REG_TCSR0 ) ;
return 0 ;
}
static int npcm7xx_timer_shutdown ( struct clock_event_device * evt )
{
struct timer_of * to = to_timer_of ( evt ) ;
u32 val ;
val = readl ( timer_of_base ( to ) + NPCM7XX_REG_TCSR0 ) ;
val & = ~ NPCM7XX_Tx_COUNTEN ;
writel ( val , timer_of_base ( to ) + NPCM7XX_REG_TCSR0 ) ;
return 0 ;
}
static int npcm7xx_timer_oneshot ( struct clock_event_device * evt )
{
struct timer_of * to = to_timer_of ( evt ) ;
u32 val ;
val = readl ( timer_of_base ( to ) + NPCM7XX_REG_TCSR0 ) ;
val & = ~ NPCM7XX_Tx_OPER ;
val | = NPCM7XX_START_ONESHOT_Tx ;
writel ( val , timer_of_base ( to ) + NPCM7XX_REG_TCSR0 ) ;
return 0 ;
}
static int npcm7xx_timer_periodic ( struct clock_event_device * evt )
{
struct timer_of * to = to_timer_of ( evt ) ;
u32 val ;
2019-07-29 20:03:54 +03:00
writel ( timer_of_period ( to ) , timer_of_base ( to ) + NPCM7XX_REG_TICR0 ) ;
2018-03-08 18:24:58 +03:00
val = readl ( timer_of_base ( to ) + NPCM7XX_REG_TCSR0 ) ;
val & = ~ NPCM7XX_Tx_OPER ;
val | = NPCM7XX_START_PERIODIC_Tx ;
writel ( val , timer_of_base ( to ) + NPCM7XX_REG_TCSR0 ) ;
return 0 ;
}
static int npcm7xx_clockevent_set_next_event ( unsigned long evt ,
struct clock_event_device * clk )
{
struct timer_of * to = to_timer_of ( clk ) ;
u32 val ;
writel ( evt , timer_of_base ( to ) + NPCM7XX_REG_TICR0 ) ;
val = readl ( timer_of_base ( to ) + NPCM7XX_REG_TCSR0 ) ;
val | = NPCM7XX_START_Tx ;
writel ( val , timer_of_base ( to ) + NPCM7XX_REG_TCSR0 ) ;
return 0 ;
}
static irqreturn_t npcm7xx_timer0_interrupt ( int irq , void * dev_id )
{
struct clock_event_device * evt = ( struct clock_event_device * ) dev_id ;
struct timer_of * to = to_timer_of ( evt ) ;
writel ( NPCM7XX_T0_CLR_INT , timer_of_base ( to ) + NPCM7XX_REG_TISR ) ;
evt - > event_handler ( evt ) ;
return IRQ_HANDLED ;
}
static struct timer_of npcm7xx_to = {
. flags = TIMER_OF_IRQ | TIMER_OF_BASE | TIMER_OF_CLOCK ,
. clkevt = {
. name = " npcm7xx-timer0 " ,
. features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT ,
. set_next_event = npcm7xx_clockevent_set_next_event ,
. set_state_shutdown = npcm7xx_timer_shutdown ,
. set_state_periodic = npcm7xx_timer_periodic ,
. set_state_oneshot = npcm7xx_timer_oneshot ,
. tick_resume = npcm7xx_timer_resume ,
. rating = 300 ,
} ,
. of_irq = {
. handler = npcm7xx_timer0_interrupt ,
. flags = IRQF_TIMER | IRQF_IRQPOLL ,
} ,
} ;
static void __init npcm7xx_clockevents_init ( void )
{
writel ( NPCM7XX_DEFAULT_CSR ,
timer_of_base ( & npcm7xx_to ) + NPCM7XX_REG_TCSR0 ) ;
writel ( NPCM7XX_Tx_RESETINT ,
timer_of_base ( & npcm7xx_to ) + NPCM7XX_REG_TISR ) ;
npcm7xx_to . clkevt . cpumask = cpumask_of ( 0 ) ;
clockevents_config_and_register ( & npcm7xx_to . clkevt ,
timer_of_rate ( & npcm7xx_to ) ,
0x1 , NPCM7XX_Tx_MAX_CNT ) ;
}
static void __init npcm7xx_clocksource_init ( void )
{
u32 val ;
writel ( NPCM7XX_DEFAULT_CSR ,
timer_of_base ( & npcm7xx_to ) + NPCM7XX_REG_TCSR1 ) ;
writel ( NPCM7XX_Tx_MAX_CNT ,
timer_of_base ( & npcm7xx_to ) + NPCM7XX_REG_TICR1 ) ;
val = readl ( timer_of_base ( & npcm7xx_to ) + NPCM7XX_REG_TCSR1 ) ;
val | = NPCM7XX_START_Tx ;
writel ( val , timer_of_base ( & npcm7xx_to ) + NPCM7XX_REG_TCSR1 ) ;
clocksource_mmio_init ( timer_of_base ( & npcm7xx_to ) +
NPCM7XX_REG_TDR1 ,
" npcm7xx-timer1 " , timer_of_rate ( & npcm7xx_to ) ,
200 , ( unsigned int ) NPCM7XX_Tx_TDR_MASK_BITS ,
clocksource_mmio_readl_down ) ;
}
static int __init npcm7xx_timer_init ( struct device_node * np )
{
int ret ;
ret = timer_of_init ( np , & npcm7xx_to ) ;
if ( ret )
return ret ;
/* Clock input is divided by PRESCALE + 1 before it is fed */
/* to the counter */
npcm7xx_to . of_clk . rate = npcm7xx_to . of_clk . rate /
( NPCM7XX_Tx_MIN_PRESCALE + 1 ) ;
npcm7xx_clocksource_init ( ) ;
npcm7xx_clockevents_init ( ) ;
pr_info ( " Enabling NPCM7xx clocksource timer base: %px, IRQ: %d " ,
timer_of_base ( & npcm7xx_to ) , timer_of_irq ( & npcm7xx_to ) ) ;
return 0 ;
}
TIMER_OF_DECLARE ( npcm7xx , " nuvoton,npcm750-timer " , npcm7xx_timer_init ) ;