2009-07-02 22:06:47 +04:00
/*
* Copyright ( C ) 2008 STMicroelectronics
2010-03-05 14:38:51 +03:00
* Copyright ( C ) 2010 Alessandro Rubini
2010-11-19 12:16:05 +03:00
* Copyright ( C ) 2010 Linus Walleij for ST - Ericsson
2009-07-02 22:06:47 +04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 , as
* published by the Free Software Foundation .
*/
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/io.h>
# include <linux/clockchips.h>
2012-10-18 16:01:25 +04:00
# include <linux/clocksource.h>
2010-05-26 10:38:54 +04:00
# include <linux/clk.h>
2009-07-02 22:06:47 +04:00
# include <linux/jiffies.h>
2012-12-04 14:10:44 +04:00
# include <linux/delay.h>
2010-05-26 10:38:54 +04:00
# include <linux/err.h>
2012-10-18 16:01:25 +04:00
# include <linux/platform_data/clocksource-nomadik-mtu.h>
2009-07-02 22:06:47 +04:00
# include <asm/mach/time.h>
2010-12-16 00:53:02 +03:00
# include <asm/sched_clock.h>
2009-07-02 22:06:47 +04:00
2011-09-20 13:18:27 +04:00
/*
* The MTU device hosts four different counters , with 4 set of
* registers . These are register names .
*/
# define MTU_IMSC 0x00 /* Interrupt mask set/clear */
# define MTU_RIS 0x04 /* Raw interrupt status */
# define MTU_MIS 0x08 /* Masked interrupt status */
# define MTU_ICR 0x0C /* Interrupt clear register */
/* per-timer registers take 0..3 as argument */
# define MTU_LR(x) (0x10 + 0x10 * (x) + 0x00) /* Load value */
# define MTU_VAL(x) (0x10 + 0x10 * (x) + 0x04) /* Current value */
# define MTU_CR(x) (0x10 + 0x10 * (x) + 0x08) /* Control reg */
# define MTU_BGLR(x) (0x10 + 0x10 * (x) + 0x0c) /* At next overflow */
/* bits for the control register */
# define MTU_CRn_ENA 0x80
# define MTU_CRn_PERIODIC 0x40 /* if 0 = free-running */
# define MTU_CRn_PRESCALE_MASK 0x0c
# define MTU_CRn_PRESCALE_1 0x00
# define MTU_CRn_PRESCALE_16 0x04
# define MTU_CRn_PRESCALE_256 0x08
# define MTU_CRn_32BITS 0x02
# define MTU_CRn_ONESHOT 0x01 /* if 0 = wraps reloading from BGLR*/
/* Other registers are usual amba/primecell registers, currently not used */
# define MTU_ITCR 0xff0
# define MTU_ITOP 0xff4
# define MTU_PERIPH_ID0 0xfe0
# define MTU_PERIPH_ID1 0xfe4
# define MTU_PERIPH_ID2 0xfe8
# define MTU_PERIPH_ID3 0xfeC
# define MTU_PCELL0 0xff0
# define MTU_PCELL1 0xff4
# define MTU_PCELL2 0xff8
# define MTU_PCELL3 0xffC
2009-07-02 22:06:47 +04:00
2012-01-11 12:46:59 +04:00
static void __iomem * mtu_base ;
2011-09-14 12:32:51 +04:00
static bool clkevt_periodic ;
static u32 clk_prescale ;
static u32 nmdk_cycle ; /* write-once */
2012-12-04 14:10:44 +04:00
static struct delay_timer mtu_delay_timer ;
2011-09-14 12:32:51 +04:00
2011-05-27 12:29:25 +04:00
# ifdef CONFIG_NOMADIK_MTU_SCHED_CLOCK
2010-05-07 13:03:02 +04:00
/*
* Override the global weak sched_clock symbol with this
* local implementation which uses the clocksource to get some
2010-11-19 12:16:05 +03:00
* better resolution when scheduling the kernel .
2010-05-07 13:03:02 +04:00
*/
2011-12-15 15:19:23 +04:00
static u32 notrace nomadik_read_sched_clock ( void )
2010-05-07 13:03:02 +04:00
{
2010-11-19 12:16:05 +03:00
if ( unlikely ( ! mtu_base ) )
return 0 ;
2011-12-15 15:19:23 +04:00
return - readl ( mtu_base + MTU_VAL ( 0 ) ) ;
2010-05-07 13:03:02 +04:00
}
2011-05-27 12:29:25 +04:00
# endif
2011-09-14 12:32:51 +04:00
2012-12-04 14:10:44 +04:00
static unsigned long nmdk_timer_read_current_timer ( void )
{
return ~ readl_relaxed ( mtu_base + MTU_VAL ( 0 ) ) ;
}
2010-03-05 14:38:51 +03:00
/* Clockevent device: use one-shot mode */
2011-09-14 12:32:51 +04:00
static int nmdk_clkevt_next ( unsigned long evt , struct clock_event_device * ev )
{
writel ( 1 < < 1 , mtu_base + MTU_IMSC ) ;
writel ( evt , mtu_base + MTU_LR ( 1 ) ) ;
/* Load highest value, enable device, enable interrupts */
writel ( MTU_CRn_ONESHOT | clk_prescale |
MTU_CRn_32BITS | MTU_CRn_ENA ,
mtu_base + MTU_CR ( 1 ) ) ;
return 0 ;
}
2011-09-20 13:18:27 +04:00
void nmdk_clkevt_reset ( void )
2011-09-14 12:32:51 +04:00
{
if ( clkevt_periodic ) {
/* Timer: configure load and background-load, and fire it up */
writel ( nmdk_cycle , mtu_base + MTU_LR ( 1 ) ) ;
writel ( nmdk_cycle , mtu_base + MTU_BGLR ( 1 ) ) ;
writel ( MTU_CRn_PERIODIC | clk_prescale |
MTU_CRn_32BITS | MTU_CRn_ENA ,
mtu_base + MTU_CR ( 1 ) ) ;
writel ( 1 < < 1 , mtu_base + MTU_IMSC ) ;
} else {
/* Generate an interrupt to start the clockevent again */
( void ) nmdk_clkevt_next ( nmdk_cycle , NULL ) ;
}
}
2009-07-02 22:06:47 +04:00
static void nmdk_clkevt_mode ( enum clock_event_mode mode ,
struct clock_event_device * dev )
{
switch ( mode ) {
case CLOCK_EVT_MODE_PERIODIC :
2011-09-14 12:32:51 +04:00
clkevt_periodic = true ;
nmdk_clkevt_reset ( ) ;
2009-07-02 22:06:47 +04:00
break ;
case CLOCK_EVT_MODE_ONESHOT :
2011-09-14 12:32:51 +04:00
clkevt_periodic = false ;
2010-03-05 14:38:51 +03:00
break ;
2009-07-02 22:06:47 +04:00
case CLOCK_EVT_MODE_SHUTDOWN :
case CLOCK_EVT_MODE_UNUSED :
2010-03-05 14:38:51 +03:00
writel ( 0 , mtu_base + MTU_IMSC ) ;
2010-06-01 11:26:49 +04:00
/* disable timer */
2011-09-14 12:32:51 +04:00
writel ( 0 , mtu_base + MTU_CR ( 1 ) ) ;
2010-06-01 11:26:49 +04:00
/* load some high default value */
writel ( 0xffffffff , mtu_base + MTU_LR ( 1 ) ) ;
2009-07-02 22:06:47 +04:00
break ;
case CLOCK_EVT_MODE_RESUME :
break ;
}
}
2012-11-08 04:07:45 +04:00
void nmdk_clksrc_reset ( void )
{
/* Disable */
writel ( 0 , mtu_base + MTU_CR ( 0 ) ) ;
/* ClockSource: configure load and background-load, and fire it up */
writel ( nmdk_cycle , mtu_base + MTU_LR ( 0 ) ) ;
writel ( nmdk_cycle , mtu_base + MTU_BGLR ( 0 ) ) ;
writel ( clk_prescale | MTU_CRn_32BITS | MTU_CRn_ENA ,
mtu_base + MTU_CR ( 0 ) ) ;
}
static void nmdk_clkevt_resume ( struct clock_event_device * cedev )
{
nmdk_clkevt_reset ( ) ;
nmdk_clksrc_reset ( ) ;
}
2009-07-02 22:06:47 +04:00
static struct clock_event_device nmdk_clkevt = {
2010-03-05 14:38:51 +03:00
. name = " mtu_1 " ,
2011-09-14 12:32:51 +04:00
. features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC ,
2010-03-05 14:38:51 +03:00
. rating = 200 ,
2009-07-02 22:06:47 +04:00
. set_mode = nmdk_clkevt_mode ,
2010-03-05 14:38:51 +03:00
. set_next_event = nmdk_clkevt_next ,
2012-11-08 04:07:45 +04:00
. resume = nmdk_clkevt_resume ,
2009-07-02 22:06:47 +04:00
} ;
/*
2010-03-05 14:38:51 +03:00
* IRQ Handler for timer 1 of the MTU block .
2009-07-02 22:06:47 +04:00
*/
static irqreturn_t nmdk_timer_interrupt ( int irq , void * dev_id )
{
2010-03-05 14:38:51 +03:00
struct clock_event_device * evdev = dev_id ;
2009-07-02 22:06:47 +04:00
2010-03-05 14:38:51 +03:00
writel ( 1 < < 1 , mtu_base + MTU_ICR ) ; /* Interrupt clear reg */
evdev - > event_handler ( evdev ) ;
2009-07-02 22:06:47 +04:00
return IRQ_HANDLED ;
}
static struct irqaction nmdk_timer_irq = {
. name = " Nomadik Timer Tick " ,
. flags = IRQF_DISABLED | IRQF_TIMER ,
. handler = nmdk_timer_interrupt ,
2010-03-05 14:38:51 +03:00
. dev_id = & nmdk_clkevt ,
2009-07-02 22:06:47 +04:00
} ;
2012-10-18 13:06:02 +04:00
void __init nmdk_timer_init ( void __iomem * base , int irq )
2009-07-02 22:06:47 +04:00
{
unsigned long rate ;
2012-10-24 16:13:41 +04:00
struct clk * clk0 , * pclk0 ;
2010-05-26 10:38:54 +04:00
2012-01-11 12:46:59 +04:00
mtu_base = base ;
2012-10-24 16:13:41 +04:00
pclk0 = clk_get_sys ( " mtu0 " , " apb_pclk " ) ;
BUG_ON ( IS_ERR ( pclk0 ) ) ;
BUG_ON ( clk_prepare ( pclk0 ) < 0 ) ;
BUG_ON ( clk_enable ( pclk0 ) < 0 ) ;
2010-05-26 10:38:54 +04:00
clk0 = clk_get_sys ( " mtu0 " , NULL ) ;
BUG_ON ( IS_ERR ( clk0 ) ) ;
2012-01-11 12:51:14 +04:00
BUG_ON ( clk_prepare ( clk0 ) < 0 ) ;
BUG_ON ( clk_enable ( clk0 ) < 0 ) ;
2010-03-05 14:38:51 +03:00
/*
2010-09-13 16:40:04 +04:00
* Tick rate is 2.4 MHz for Nomadik and 2.4 Mhz , 100 MHz or 133 MHz
* for ux500 .
* Use a divide - by - 16 counter if the tick rate is more than 32 MHz .
* At 32 MHz , the timer ( with 32 bit counter ) can be programmed
* to wake - up at a max 127 s a head in time . Dividing a 2.4 MHz timer
* with 16 gives too low timer resolution .
2010-03-05 14:38:51 +03:00
*/
2010-05-26 10:38:54 +04:00
rate = clk_get_rate ( clk0 ) ;
2010-09-13 16:40:04 +04:00
if ( rate > 32000000 ) {
2010-03-05 14:38:51 +03:00
rate / = 16 ;
2011-09-14 12:32:51 +04:00
clk_prescale = MTU_CRn_PRESCALE_16 ;
2010-03-05 14:38:51 +03:00
} else {
2011-09-14 12:32:51 +04:00
clk_prescale = MTU_CRn_PRESCALE_1 ;
2010-03-05 14:38:51 +03:00
}
2009-07-02 22:06:47 +04:00
2012-10-18 13:12:31 +04:00
/* Cycles for periodic mode */
nmdk_cycle = DIV_ROUND_CLOSEST ( rate , HZ ) ;
2011-09-14 12:32:51 +04:00
2010-03-05 14:38:51 +03:00
/* Timer 0 is the free running clocksource */
2011-09-14 12:32:51 +04:00
nmdk_clksrc_reset ( ) ;
2009-07-02 22:06:47 +04:00
2011-05-08 18:33:30 +04:00
if ( clocksource_mmio_init ( mtu_base + MTU_VAL ( 0 ) , " mtu_0 " ,
rate , 200 , 32 , clocksource_mmio_readl_down ) )
2010-03-05 14:38:51 +03:00
pr_err ( " timer: failed to initialize clock source %s \n " ,
2011-05-08 18:33:30 +04:00
" mtu_0 " ) ;
2011-12-15 15:19:23 +04:00
2011-05-27 12:29:25 +04:00
# ifdef CONFIG_NOMADIK_MTU_SCHED_CLOCK
2011-12-15 15:19:23 +04:00
setup_sched_clock ( nomadik_read_sched_clock , 32 , rate ) ;
2011-05-27 12:29:25 +04:00
# endif
2011-12-15 15:19:23 +04:00
2012-01-11 12:57:56 +04:00
/* Timer 1 is used for events, register irq and clockevents */
2012-10-18 13:06:02 +04:00
setup_irq ( irq , & nmdk_timer_irq ) ;
2012-01-11 12:57:56 +04:00
nmdk_clkevt . cpumask = cpumask_of ( 0 ) ;
2013-02-22 19:44:30 +04:00
nmdk_clkevt . irq = irq ;
2012-01-11 12:57:56 +04:00
clockevents_config_and_register ( & nmdk_clkevt , rate , 2 , 0xffffffffU ) ;
2012-12-04 14:10:44 +04:00
mtu_delay_timer . read_current_timer = & nmdk_timer_read_current_timer ;
mtu_delay_timer . freq = rate ;
register_current_timer_delay ( & mtu_delay_timer ) ;
2009-07-02 22:06:47 +04:00
}