2017-01-22 13:17:17 +01:00
/*
2017-03-24 22:32:34 +01:00
* Faraday Technology FTTMR010 timer driver
2017-01-22 13:17:17 +01:00
* Copyright ( C ) 2017 Linus Walleij < linus . walleij @ linaro . org >
*
* Based on a rewrite of arch / arm / mach - gemini / timer . c :
* Copyright ( C ) 2001 - 2006 Storlink , Corp .
* Copyright ( C ) 2008 - 2009 Paulius Zaleckas < paulius . zaleckas @ teltonika . lt >
*/
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_irq.h>
# include <linux/clockchips.h>
# include <linux/clocksource.h>
# include <linux/sched_clock.h>
2017-03-24 22:32:35 +01:00
# include <linux/clk.h>
2017-01-22 13:17:17 +01:00
/*
* Register definitions for the timers
*/
# define TIMER1_COUNT (0x00)
# define TIMER1_LOAD (0x04)
# define TIMER1_MATCH1 (0x08)
# define TIMER1_MATCH2 (0x0c)
# define TIMER2_COUNT (0x10)
# define TIMER2_LOAD (0x14)
# define TIMER2_MATCH1 (0x18)
# define TIMER2_MATCH2 (0x1c)
# define TIMER3_COUNT (0x20)
# define TIMER3_LOAD (0x24)
# define TIMER3_MATCH1 (0x28)
# define TIMER3_MATCH2 (0x2c)
# define TIMER_CR (0x30)
# define TIMER_INTR_STATE (0x34)
# define TIMER_INTR_MASK (0x38)
# define TIMER_1_CR_ENABLE (1 << 0)
# define TIMER_1_CR_CLOCK (1 << 1)
# define TIMER_1_CR_INT (1 << 2)
# define TIMER_2_CR_ENABLE (1 << 3)
# define TIMER_2_CR_CLOCK (1 << 4)
# define TIMER_2_CR_INT (1 << 5)
# define TIMER_3_CR_ENABLE (1 << 6)
# define TIMER_3_CR_CLOCK (1 << 7)
# define TIMER_3_CR_INT (1 << 8)
# define TIMER_1_CR_UPDOWN (1 << 9)
# define TIMER_2_CR_UPDOWN (1 << 10)
# define TIMER_3_CR_UPDOWN (1 << 11)
# define TIMER_DEFAULT_FLAGS (TIMER_1_CR_UPDOWN | \
TIMER_3_CR_ENABLE | \
TIMER_3_CR_UPDOWN )
# define TIMER_1_INT_MATCH1 (1 << 0)
# define TIMER_1_INT_MATCH2 (1 << 1)
# define TIMER_1_INT_OVERFLOW (1 << 2)
# define TIMER_2_INT_MATCH1 (1 << 3)
# define TIMER_2_INT_MATCH2 (1 << 4)
# define TIMER_2_INT_OVERFLOW (1 << 5)
# define TIMER_3_INT_MATCH1 (1 << 6)
# define TIMER_3_INT_MATCH2 (1 << 7)
# define TIMER_3_INT_OVERFLOW (1 << 8)
# define TIMER_INT_ALL_MASK 0x1ff
static unsigned int tick_rate ;
static void __iomem * base ;
2017-03-24 22:32:34 +01:00
static u64 notrace fttmr010_read_sched_clock ( void )
2017-01-22 13:17:17 +01:00
{
return readl ( base + TIMER3_COUNT ) ;
}
2017-03-24 22:32:34 +01:00
static int fttmr010_timer_set_next_event ( unsigned long cycles ,
2017-01-22 13:17:17 +01:00
struct clock_event_device * evt )
{
u32 cr ;
/* Setup the match register */
cr = readl ( base + TIMER1_COUNT ) ;
writel ( cr + cycles , base + TIMER1_MATCH1 ) ;
if ( readl ( base + TIMER1_COUNT ) - cr > cycles )
return - ETIME ;
return 0 ;
}
2017-03-24 22:32:34 +01:00
static int fttmr010_timer_shutdown ( struct clock_event_device * evt )
2017-01-22 13:17:17 +01:00
{
u32 cr ;
/*
* Disable also for oneshot : the set_next ( ) call will arm the timer
* instead .
*/
/* Stop timer and interrupt. */
cr = readl ( base + TIMER_CR ) ;
cr & = ~ ( TIMER_1_CR_ENABLE | TIMER_1_CR_INT ) ;
writel ( cr , base + TIMER_CR ) ;
/* Setup counter start from 0 */
writel ( 0 , base + TIMER1_COUNT ) ;
writel ( 0 , base + TIMER1_LOAD ) ;
/* enable interrupt */
cr = readl ( base + TIMER_INTR_MASK ) ;
cr & = ~ ( TIMER_1_INT_OVERFLOW | TIMER_1_INT_MATCH2 ) ;
cr | = TIMER_1_INT_MATCH1 ;
writel ( cr , base + TIMER_INTR_MASK ) ;
/* start the timer */
cr = readl ( base + TIMER_CR ) ;
cr | = TIMER_1_CR_ENABLE ;
writel ( cr , base + TIMER_CR ) ;
return 0 ;
}
2017-03-24 22:32:34 +01:00
static int fttmr010_timer_set_periodic ( struct clock_event_device * evt )
2017-01-22 13:17:17 +01:00
{
u32 period = DIV_ROUND_CLOSEST ( tick_rate , HZ ) ;
u32 cr ;
/* Stop timer and interrupt */
cr = readl ( base + TIMER_CR ) ;
cr & = ~ ( TIMER_1_CR_ENABLE | TIMER_1_CR_INT ) ;
writel ( cr , base + TIMER_CR ) ;
/* Setup timer to fire at 1/HT intervals. */
cr = 0xffffffff - ( period - 1 ) ;
writel ( cr , base + TIMER1_COUNT ) ;
writel ( cr , base + TIMER1_LOAD ) ;
/* enable interrupt on overflow */
cr = readl ( base + TIMER_INTR_MASK ) ;
cr & = ~ ( TIMER_1_INT_MATCH1 | TIMER_1_INT_MATCH2 ) ;
cr | = TIMER_1_INT_OVERFLOW ;
writel ( cr , base + TIMER_INTR_MASK ) ;
/* Start the timer */
cr = readl ( base + TIMER_CR ) ;
cr | = TIMER_1_CR_ENABLE ;
cr | = TIMER_1_CR_INT ;
writel ( cr , base + TIMER_CR ) ;
return 0 ;
}
/* Use TIMER1 as clock event */
2017-03-24 22:32:34 +01:00
static struct clock_event_device fttmr010_clockevent = {
2017-01-22 13:17:17 +01:00
. name = " TIMER1 " ,
/* Reasonably fast and accurate clock event */
. rating = 300 ,
. shift = 32 ,
. features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT ,
2017-03-24 22:32:34 +01:00
. set_next_event = fttmr010_timer_set_next_event ,
. set_state_shutdown = fttmr010_timer_shutdown ,
. set_state_periodic = fttmr010_timer_set_periodic ,
. set_state_oneshot = fttmr010_timer_shutdown ,
. tick_resume = fttmr010_timer_shutdown ,
2017-01-22 13:17:17 +01:00
} ;
/*
* IRQ handler for the timer
*/
2017-03-24 22:32:34 +01:00
static irqreturn_t fttmr010_timer_interrupt ( int irq , void * dev_id )
2017-01-22 13:17:17 +01:00
{
2017-03-24 22:32:34 +01:00
struct clock_event_device * evt = & fttmr010_clockevent ;
2017-01-22 13:17:17 +01:00
evt - > event_handler ( evt ) ;
return IRQ_HANDLED ;
}
2017-03-24 22:32:34 +01:00
static struct irqaction fttmr010_timer_irq = {
. name = " Faraday FTTMR010 Timer Tick " ,
2017-01-22 13:17:17 +01:00
. flags = IRQF_TIMER ,
2017-03-24 22:32:34 +01:00
. handler = fttmr010_timer_interrupt ,
2017-01-22 13:17:17 +01:00
} ;
2017-05-18 22:17:00 +02:00
static int __init fttmr010_timer_init ( struct device_node * np )
2017-01-22 13:17:17 +01:00
{
int irq ;
2017-05-18 22:17:00 +02:00
struct clk * clk ;
int ret ;
/*
* These implementations require a clock reference .
* FIXME : we currently only support clocking using PCLK
* and using EXTCLK is not supported in the driver .
*/
clk = of_clk_get_by_name ( np , " PCLK " ) ;
if ( IS_ERR ( clk ) ) {
pr_err ( " could not get PCLK \n " ) ;
return PTR_ERR ( clk ) ;
}
ret = clk_prepare_enable ( clk ) ;
if ( ret ) {
pr_err ( " failed to enable PCLK \n " ) ;
return ret ;
}
tick_rate = clk_get_rate ( clk ) ;
2017-01-22 13:17:17 +01:00
base = of_iomap ( np , 0 ) ;
if ( ! base ) {
pr_err ( " Can't remap registers " ) ;
return - ENXIO ;
}
/* IRQ for timer 1 */
irq = irq_of_parse_and_map ( np , 0 ) ;
if ( irq < = 0 ) {
pr_err ( " Can't parse IRQ " ) ;
return - EINVAL ;
}
/*
* Reset the interrupt mask and status
*/
writel ( TIMER_INT_ALL_MASK , base + TIMER_INTR_MASK ) ;
writel ( 0 , base + TIMER_INTR_STATE ) ;
writel ( TIMER_DEFAULT_FLAGS , base + TIMER_CR ) ;
/*
* Setup free - running clocksource timer ( interrupts
* disabled . )
*/
writel ( 0 , base + TIMER3_COUNT ) ;
writel ( 0 , base + TIMER3_LOAD ) ;
writel ( 0 , base + TIMER3_MATCH1 ) ;
writel ( 0 , base + TIMER3_MATCH2 ) ;
clocksource_mmio_init ( base + TIMER3_COUNT ,
2017-03-24 22:32:34 +01:00
" fttmr010_clocksource " , tick_rate ,
2017-01-22 13:17:17 +01:00
300 , 32 , clocksource_mmio_readl_up ) ;
2017-03-24 22:32:34 +01:00
sched_clock_register ( fttmr010_read_sched_clock , 32 , tick_rate ) ;
2017-01-22 13:17:17 +01:00
/*
* Setup clockevent timer ( interrupt - driven . )
*/
writel ( 0 , base + TIMER1_COUNT ) ;
writel ( 0 , base + TIMER1_LOAD ) ;
writel ( 0 , base + TIMER1_MATCH1 ) ;
writel ( 0 , base + TIMER1_MATCH2 ) ;
2017-03-24 22:32:34 +01:00
setup_irq ( irq , & fttmr010_timer_irq ) ;
fttmr010_clockevent . cpumask = cpumask_of ( 0 ) ;
clockevents_config_and_register ( & fttmr010_clockevent , tick_rate ,
2017-01-22 13:17:17 +01:00
1 , 0xffffffff ) ;
return 0 ;
}
2017-05-18 22:17:00 +02:00
CLOCKSOURCE_OF_DECLARE ( fttmr010 , " faraday,fttmr010 " , fttmr010_timer_init ) ;
CLOCKSOURCE_OF_DECLARE ( gemini , " cortina,gemini-timer " , fttmr010_timer_init ) ;