2014-10-15 17:30:25 +04:00
/*
* Integrator / AP timer driver
* Copyright ( C ) 2000 - 2003 Deep Blue Solutions Ltd
* Copyright ( c ) 2014 , Linaro Limited
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/clk.h>
# include <linux/clocksource.h>
# include <linux/of_irq.h>
# include <linux/of_address.h>
# include <linux/of_platform.h>
# include <linux/clockchips.h>
# include <linux/interrupt.h>
# include <linux/sched_clock.h>
2015-05-18 18:29:40 +03:00
# include "timer-sp.h"
2014-10-15 17:30:25 +04:00
static void __iomem * sched_clk_base ;
static u64 notrace integrator_read_sched_clock ( void )
{
return - readl ( sched_clk_base + TIMER_VALUE ) ;
}
2016-06-07 00:27:24 +03:00
static int integrator_clocksource_init ( unsigned long inrate ,
void __iomem * base )
2014-10-15 17:30:25 +04:00
{
u32 ctrl = TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC ;
unsigned long rate = inrate ;
2016-06-07 00:27:24 +03:00
int ret ;
2014-10-15 17:30:25 +04:00
if ( rate > = 1500000 ) {
rate / = 16 ;
ctrl | = TIMER_CTRL_DIV16 ;
}
writel ( 0xffff , base + TIMER_LOAD ) ;
writel ( ctrl , base + TIMER_CTRL ) ;
2016-06-07 00:27:24 +03:00
ret = clocksource_mmio_init ( base + TIMER_VALUE , " timer2 " ,
rate , 200 , 16 , clocksource_mmio_readl_down ) ;
if ( ret )
return ret ;
2014-10-15 17:30:25 +04:00
sched_clk_base = base ;
sched_clock_register ( integrator_read_sched_clock , 16 , rate ) ;
2016-06-07 00:27:24 +03:00
return 0 ;
2014-10-15 17:30:25 +04:00
}
static unsigned long timer_reload ;
static void __iomem * clkevt_base ;
/*
* IRQ handler for the timer
*/
static irqreturn_t integrator_timer_interrupt ( int irq , void * dev_id )
{
struct clock_event_device * evt = dev_id ;
/* clear the interrupt */
writel ( 1 , clkevt_base + TIMER_INTCLR ) ;
evt - > event_handler ( evt ) ;
return IRQ_HANDLED ;
}
2015-06-18 13:54:47 +03:00
static int clkevt_shutdown ( struct clock_event_device * evt )
2014-10-15 17:30:25 +04:00
{
u32 ctrl = readl ( clkevt_base + TIMER_CTRL ) & ~ TIMER_CTRL_ENABLE ;
/* Disable timer */
writel ( ctrl , clkevt_base + TIMER_CTRL ) ;
2015-06-18 13:54:47 +03:00
return 0 ;
}
2014-10-15 17:30:25 +04:00
2015-06-18 13:54:47 +03:00
static int clkevt_set_oneshot ( struct clock_event_device * evt )
{
u32 ctrl = readl ( clkevt_base + TIMER_CTRL ) &
~ ( TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC ) ;
/* Leave the timer disabled, .set_next_event will enable it */
writel ( ctrl , clkevt_base + TIMER_CTRL ) ;
return 0 ;
}
2014-10-15 17:30:25 +04:00
2015-06-18 13:54:47 +03:00
static int clkevt_set_periodic ( struct clock_event_device * evt )
{
u32 ctrl = readl ( clkevt_base + TIMER_CTRL ) & ~ TIMER_CTRL_ENABLE ;
/* Disable timer */
writel ( ctrl , clkevt_base + TIMER_CTRL ) ;
/* Enable the timer and start the periodic tick */
writel ( timer_reload , clkevt_base + TIMER_LOAD ) ;
ctrl | = TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE ;
writel ( ctrl , clkevt_base + TIMER_CTRL ) ;
return 0 ;
2014-10-15 17:30:25 +04:00
}
static int clkevt_set_next_event ( unsigned long next , struct clock_event_device * evt )
{
unsigned long ctrl = readl ( clkevt_base + TIMER_CTRL ) ;
writel ( ctrl & ~ TIMER_CTRL_ENABLE , clkevt_base + TIMER_CTRL ) ;
writel ( next , clkevt_base + TIMER_LOAD ) ;
writel ( ctrl | TIMER_CTRL_ENABLE , clkevt_base + TIMER_CTRL ) ;
return 0 ;
}
static struct clock_event_device integrator_clockevent = {
2015-06-18 13:54:47 +03:00
. name = " timer1 " ,
. features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT ,
. set_state_shutdown = clkevt_shutdown ,
. set_state_periodic = clkevt_set_periodic ,
. set_state_oneshot = clkevt_set_oneshot ,
. tick_resume = clkevt_shutdown ,
. set_next_event = clkevt_set_next_event ,
. rating = 300 ,
2014-10-15 17:30:25 +04:00
} ;
static struct irqaction integrator_timer_irq = {
. name = " timer " ,
. flags = IRQF_TIMER | IRQF_IRQPOLL ,
. handler = integrator_timer_interrupt ,
. dev_id = & integrator_clockevent ,
} ;
2016-06-07 00:27:24 +03:00
static int integrator_clockevent_init ( unsigned long inrate ,
void __iomem * base , int irq )
2014-10-15 17:30:25 +04:00
{
unsigned long rate = inrate ;
unsigned int ctrl = 0 ;
2016-06-07 00:27:24 +03:00
int ret ;
2014-10-15 17:30:25 +04:00
clkevt_base = base ;
/* Calculate and program a divisor */
if ( rate > 0x100000 * HZ ) {
rate / = 256 ;
ctrl | = TIMER_CTRL_DIV256 ;
} else if ( rate > 0x10000 * HZ ) {
rate / = 16 ;
ctrl | = TIMER_CTRL_DIV16 ;
}
timer_reload = rate / HZ ;
writel ( ctrl , clkevt_base + TIMER_CTRL ) ;
2016-06-07 00:27:24 +03:00
ret = setup_irq ( irq , & integrator_timer_irq ) ;
if ( ret )
return ret ;
2014-10-15 17:30:25 +04:00
clockevents_config_and_register ( & integrator_clockevent ,
rate ,
1 ,
0xffffU ) ;
2016-06-07 00:27:24 +03:00
return 0 ;
2014-10-15 17:30:25 +04:00
}
2016-06-07 00:27:24 +03:00
static int __init integrator_ap_timer_init_of ( struct device_node * node )
2014-10-15 17:30:25 +04:00
{
const char * path ;
void __iomem * base ;
int err ;
int irq ;
struct clk * clk ;
unsigned long rate ;
struct device_node * pri_node ;
struct device_node * sec_node ;
base = of_io_request_and_map ( node , 0 , " integrator-timer " ) ;
2015-05-02 18:03:24 +03:00
if ( IS_ERR ( base ) )
2016-06-07 00:27:24 +03:00
return PTR_ERR ( base ) ;
2014-10-15 17:30:25 +04:00
clk = of_clk_get ( node , 0 ) ;
if ( IS_ERR ( clk ) ) {
pr_err ( " No clock for %s \n " , node - > name ) ;
2016-06-07 00:27:24 +03:00
return PTR_ERR ( clk ) ;
2014-10-15 17:30:25 +04:00
}
clk_prepare_enable ( clk ) ;
rate = clk_get_rate ( clk ) ;
writel ( 0 , base + TIMER_CTRL ) ;
err = of_property_read_string ( of_aliases ,
" arm,timer-primary " , & path ) ;
2016-06-07 00:27:24 +03:00
if ( err ) {
2017-03-09 12:47:10 +03:00
pr_warn ( " Failed to read property \n " ) ;
2016-06-07 00:27:24 +03:00
return err ;
}
2014-10-15 17:30:25 +04:00
pri_node = of_find_node_by_path ( path ) ;
2016-06-07 00:27:24 +03:00
2014-10-15 17:30:25 +04:00
err = of_property_read_string ( of_aliases ,
" arm,timer-secondary " , & path ) ;
2016-06-07 00:27:24 +03:00
if ( err ) {
2017-03-09 12:47:10 +03:00
pr_warn ( " Failed to read property \n " ) ;
2016-06-07 00:27:24 +03:00
return err ;
}
2014-10-15 17:30:25 +04:00
sec_node = of_find_node_by_path ( path ) ;
2016-06-07 00:27:24 +03:00
if ( node = = pri_node )
2014-10-15 17:30:25 +04:00
/* The primary timer lacks IRQ, use as clocksource */
2016-06-07 00:27:24 +03:00
return integrator_clocksource_init ( rate , base ) ;
2014-10-15 17:30:25 +04:00
if ( node = = sec_node ) {
/* The secondary timer will drive the clock event */
irq = irq_of_parse_and_map ( node , 0 ) ;
2016-06-07 00:27:24 +03:00
return integrator_clockevent_init ( rate , base , irq ) ;
2014-10-15 17:30:25 +04:00
}
pr_info ( " Timer @%p unused \n " , base ) ;
clk_disable_unprepare ( clk ) ;
2016-06-07 00:27:24 +03:00
return 0 ;
2014-10-15 17:30:25 +04:00
}
2016-06-07 01:27:44 +03:00
CLOCKSOURCE_OF_DECLARE ( integrator_ap_timer , " arm,integrator-timer " ,
2014-10-15 17:30:25 +04:00
integrator_ap_timer_init_of ) ;