2010-01-14 16:30:16 +03:00
/*
2015-05-18 18:29:40 +03:00
* linux / drivers / clocksource / timer - sp . c
2010-01-14 16:30:16 +03:00
*
* Copyright ( C ) 1999 - 2003 ARM Limited
* Copyright ( C ) 2000 Deep Blue Solutions Ltd
*
* 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
*/
2011-05-12 16:31:48 +04:00
# include <linux/clk.h>
2010-01-14 16:30:16 +03:00
# include <linux/clocksource.h>
# include <linux/clockchips.h>
2011-05-12 16:31:48 +04:00
# include <linux/err.h>
2010-01-14 16:30:16 +03:00
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/io.h>
2013-03-25 20:23:52 +04:00
# include <linux/of.h>
# include <linux/of_address.h>
2018-04-18 17:50:02 +03:00
# include <linux/of_clk.h>
2013-03-25 20:23:52 +04:00
# include <linux/of_irq.h>
2013-06-02 10:39:40 +04:00
# include <linux/sched_clock.h>
2010-01-14 16:30:16 +03:00
2015-05-18 18:29:40 +03:00
# include <clocksource/timer-sp804.h>
# include "timer-sp.h"
2010-01-14 16:30:16 +03:00
2013-03-25 20:23:52 +04:00
static long __init sp804_get_clock_rate ( struct clk * clk )
2011-05-12 16:31:48 +04:00
{
long rate ;
int err ;
2011-09-22 14:38:40 +04:00
err = clk_prepare ( clk ) ;
if ( err ) {
2013-03-25 20:23:52 +04:00
pr_err ( " sp804: clock failed to prepare: %d \n " , err ) ;
2011-09-22 14:38:40 +04:00
clk_put ( clk ) ;
return err ;
}
2011-05-12 16:31:48 +04:00
err = clk_enable ( clk ) ;
if ( err ) {
2013-03-25 20:23:52 +04:00
pr_err ( " sp804: clock failed to enable: %d \n " , err ) ;
2011-09-22 14:38:40 +04:00
clk_unprepare ( clk ) ;
2011-05-12 16:31:48 +04:00
clk_put ( clk ) ;
return err ;
}
rate = clk_get_rate ( clk ) ;
if ( rate < 0 ) {
2013-03-25 20:23:52 +04:00
pr_err ( " sp804: clock failed to get rate: %ld \n " , rate ) ;
2011-05-12 16:31:48 +04:00
clk_disable ( clk ) ;
2011-09-22 14:38:40 +04:00
clk_unprepare ( clk ) ;
2011-05-12 16:31:48 +04:00
clk_put ( clk ) ;
}
return rate ;
}
2011-12-13 01:29:08 +04:00
static void __iomem * sched_clock_base ;
2013-11-16 03:26:09 +04:00
static u64 notrace sp804_read ( void )
2011-12-13 01:29:08 +04:00
{
return ~ readl_relaxed ( sched_clock_base + TIMER_VALUE ) ;
}
2015-05-18 18:29:04 +03:00
void __init sp804_timer_disable ( void __iomem * base )
{
writel ( 0 , base + TIMER_CTRL ) ;
}
2016-06-07 00:28:01 +03:00
int __init __sp804_clocksource_and_sched_clock_init ( void __iomem * base ,
2011-12-13 01:29:08 +04:00
const char * name ,
2013-03-25 20:23:52 +04:00
struct clk * clk ,
2011-12-13 01:29:08 +04:00
int use_sched_clock )
2010-01-14 16:30:16 +03:00
{
2013-03-25 20:23:52 +04:00
long rate ;
if ( ! clk ) {
clk = clk_get_sys ( " sp804 " , name ) ;
if ( IS_ERR ( clk ) ) {
pr_err ( " sp804: clock not found: %d \n " ,
( int ) PTR_ERR ( clk ) ) ;
2016-06-07 00:28:01 +03:00
return PTR_ERR ( clk ) ;
2013-03-25 20:23:52 +04:00
}
}
rate = sp804_get_clock_rate ( clk ) ;
2011-05-12 16:31:48 +04:00
if ( rate < 0 )
2016-06-07 00:28:01 +03:00
return - EINVAL ;
2011-05-12 16:31:48 +04:00
2010-01-14 16:30:16 +03:00
/* setup timer 0 as free-running clocksource */
2011-05-08 18:33:30 +04:00
writel ( 0 , base + TIMER_CTRL ) ;
writel ( 0xffffffff , base + TIMER_LOAD ) ;
writel ( 0xffffffff , base + TIMER_VALUE ) ;
2010-01-14 16:30:16 +03:00
writel ( TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC ,
2011-05-08 18:33:30 +04:00
base + TIMER_CTRL ) ;
2010-01-14 16:30:16 +03:00
2011-05-12 15:08:23 +04:00
clocksource_mmio_init ( base + TIMER_VALUE , name ,
2011-05-12 16:31:48 +04:00
rate , 200 , 32 , clocksource_mmio_readl_down ) ;
2011-12-13 01:29:08 +04:00
if ( use_sched_clock ) {
sched_clock_base = base ;
2013-11-16 03:26:09 +04:00
sched_clock_register ( sp804_read , 32 , rate ) ;
2011-12-13 01:29:08 +04:00
}
2016-06-07 00:28:01 +03:00
return 0 ;
2010-01-14 16:30:16 +03:00
}
static void __iomem * clkevt_base ;
2011-05-12 18:45:16 +04:00
static unsigned long clkevt_reload ;
2010-01-14 16:30:16 +03:00
/*
* IRQ handler for the timer
*/
static irqreturn_t sp804_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-07-06 13:09:19 +03:00
static inline void timer_shutdown ( struct clock_event_device * evt )
2010-01-14 16:30:16 +03:00
{
2015-07-06 13:09:19 +03:00
writel ( 0 , clkevt_base + TIMER_CTRL ) ;
}
2010-01-14 16:30:16 +03:00
2015-07-06 13:09:19 +03:00
static int sp804_shutdown ( struct clock_event_device * evt )
{
timer_shutdown ( evt ) ;
return 0 ;
}
2010-01-14 16:30:16 +03:00
2015-07-06 13:09:19 +03:00
static int sp804_set_periodic ( struct clock_event_device * evt )
{
unsigned long ctrl = TIMER_CTRL_32BIT | TIMER_CTRL_IE |
TIMER_CTRL_PERIODIC | TIMER_CTRL_ENABLE ;
2010-01-14 16:30:16 +03:00
2015-07-06 13:09:19 +03:00
timer_shutdown ( evt ) ;
writel ( clkevt_reload , clkevt_base + TIMER_LOAD ) ;
2010-01-14 16:30:16 +03:00
writel ( ctrl , clkevt_base + TIMER_CTRL ) ;
2015-07-06 13:09:19 +03:00
return 0 ;
2010-01-14 16:30:16 +03:00
}
static int sp804_set_next_event ( unsigned long next ,
struct clock_event_device * evt )
{
2015-07-06 13:09:19 +03:00
unsigned long ctrl = TIMER_CTRL_32BIT | TIMER_CTRL_IE |
TIMER_CTRL_ONESHOT | TIMER_CTRL_ENABLE ;
2010-01-14 16:30:16 +03:00
writel ( next , clkevt_base + TIMER_LOAD ) ;
2015-07-06 13:09:19 +03:00
writel ( ctrl , clkevt_base + TIMER_CTRL ) ;
2010-01-14 16:30:16 +03:00
return 0 ;
}
static struct clock_event_device sp804_clockevent = {
2015-07-06 13:09:19 +03:00
. features = CLOCK_EVT_FEAT_PERIODIC |
CLOCK_EVT_FEAT_ONESHOT |
CLOCK_EVT_FEAT_DYNIRQ ,
. set_state_shutdown = sp804_shutdown ,
. set_state_periodic = sp804_set_periodic ,
. set_state_oneshot = sp804_shutdown ,
. tick_resume = sp804_shutdown ,
. set_next_event = sp804_set_next_event ,
. rating = 300 ,
2010-01-14 16:30:16 +03:00
} ;
static struct irqaction sp804_timer_irq = {
. name = " timer " ,
2013-10-14 07:42:20 +04:00
. flags = IRQF_TIMER | IRQF_IRQPOLL ,
2010-01-14 16:30:16 +03:00
. handler = sp804_timer_interrupt ,
. dev_id = & sp804_clockevent ,
} ;
2016-06-07 00:28:01 +03:00
int __init __sp804_clockevents_init ( void __iomem * base , unsigned int irq , struct clk * clk , const char * name )
2010-01-14 16:30:16 +03:00
{
struct clock_event_device * evt = & sp804_clockevent ;
2013-03-25 20:23:52 +04:00
long rate ;
if ( ! clk )
clk = clk_get_sys ( " sp804 " , name ) ;
if ( IS_ERR ( clk ) ) {
pr_err ( " sp804: %s clock not found: %d \n " , name ,
( int ) PTR_ERR ( clk ) ) ;
2016-06-07 00:28:01 +03:00
return PTR_ERR ( clk ) ;
2013-03-25 20:23:52 +04:00
}
2011-05-12 18:45:16 +04:00
2013-03-25 20:23:52 +04:00
rate = sp804_get_clock_rate ( clk ) ;
2011-05-12 18:45:16 +04:00
if ( rate < 0 )
2016-06-07 00:28:01 +03:00
return - EINVAL ;
2010-01-14 16:30:16 +03:00
clkevt_base = base ;
2011-05-12 18:45:16 +04:00
clkevt_reload = DIV_ROUND_CLOSEST ( rate , HZ ) ;
2011-05-12 18:31:13 +04:00
evt - > name = name ;
evt - > irq = irq ;
2012-11-23 21:55:30 +04:00
evt - > cpumask = cpu_possible_mask ;
2010-01-14 16:30:16 +03:00
2013-03-25 20:23:52 +04:00
writel ( 0 , base + TIMER_CTRL ) ;
2011-05-12 18:31:13 +04:00
setup_irq ( irq , & sp804_timer_irq ) ;
2011-12-21 16:25:34 +04:00
clockevents_config_and_register ( evt , rate , 0xf , 0xffffffff ) ;
2016-06-07 00:28:01 +03:00
return 0 ;
2010-01-14 16:30:16 +03:00
}
2013-03-25 20:23:52 +04:00
2016-06-07 00:28:01 +03:00
static int __init sp804_of_init ( struct device_node * np )
2013-03-25 20:23:52 +04:00
{
static bool initialized = false ;
void __iomem * base ;
2016-06-07 00:28:01 +03:00
int irq , ret = - EINVAL ;
2013-03-25 20:23:52 +04:00
u32 irq_num = 0 ;
struct clk * clk1 , * clk2 ;
const char * name = of_get_property ( np , " compatible " , NULL ) ;
base = of_iomap ( np , 0 ) ;
2016-06-07 00:28:01 +03:00
if ( ! base )
return - ENXIO ;
2013-03-25 20:23:52 +04:00
/* Ensure timers are disabled */
writel ( 0 , base + TIMER_CTRL ) ;
writel ( 0 , base + TIMER_2_BASE + TIMER_CTRL ) ;
2016-06-07 00:28:01 +03:00
if ( initialized | | ! of_device_is_available ( np ) ) {
ret = - EINVAL ;
2013-03-25 20:23:52 +04:00
goto err ;
2016-06-07 00:28:01 +03:00
}
2013-03-25 20:23:52 +04:00
clk1 = of_clk_get ( np , 0 ) ;
if ( IS_ERR ( clk1 ) )
clk1 = NULL ;
2014-05-30 01:01:34 +04:00
/* Get the 2nd clock if the timer has 3 timer clocks */
2018-04-18 17:50:02 +03:00
if ( of_clk_get_parent_count ( np ) = = 3 ) {
2013-03-25 20:23:52 +04:00
clk2 = of_clk_get ( np , 1 ) ;
if ( IS_ERR ( clk2 ) ) {
pr_err ( " sp804: %s clock not found: %d \n " , np - > name ,
( int ) PTR_ERR ( clk2 ) ) ;
2014-05-30 01:01:34 +04:00
clk2 = NULL ;
2013-03-25 20:23:52 +04:00
}
} else
clk2 = clk1 ;
irq = irq_of_parse_and_map ( np , 0 ) ;
if ( irq < = 0 )
goto err ;
of_property_read_u32 ( np , " arm,sp804-has-irq " , & irq_num ) ;
if ( irq_num = = 2 ) {
2016-06-07 00:28:01 +03:00
ret = __sp804_clockevents_init ( base + TIMER_2_BASE , irq , clk2 , name ) ;
if ( ret )
goto err ;
ret = __sp804_clocksource_and_sched_clock_init ( base , name , clk1 , 1 ) ;
if ( ret )
goto err ;
2013-03-25 20:23:52 +04:00
} else {
2016-06-07 00:28:01 +03:00
ret = __sp804_clockevents_init ( base , irq , clk1 , name ) ;
if ( ret )
goto err ;
ret = __sp804_clocksource_and_sched_clock_init ( base + TIMER_2_BASE ,
name , clk2 , 1 ) ;
if ( ret )
goto err ;
2013-03-25 20:23:52 +04:00
}
initialized = true ;
2016-06-07 00:28:01 +03:00
return 0 ;
2013-03-25 20:23:52 +04:00
err :
iounmap ( base ) ;
2016-06-07 00:28:01 +03:00
return ret ;
2013-03-25 20:23:52 +04:00
}
2017-05-26 17:56:11 +03:00
TIMER_OF_DECLARE ( sp804 , " arm,sp804 " , sp804_of_init ) ;
2013-03-14 00:31:12 +04:00
2016-06-07 00:28:01 +03:00
static int __init integrator_cp_of_init ( struct device_node * np )
2013-03-14 00:31:12 +04:00
{
static int init_count = 0 ;
void __iomem * base ;
2016-06-07 00:28:01 +03:00
int irq , ret = - EINVAL ;
2013-03-14 00:31:12 +04:00
const char * name = of_get_property ( np , " compatible " , NULL ) ;
2014-01-10 18:54:34 +04:00
struct clk * clk ;
2013-03-14 00:31:12 +04:00
base = of_iomap ( np , 0 ) ;
2016-06-07 00:28:01 +03:00
if ( ! base ) {
2017-03-09 12:47:10 +03:00
pr_err ( " Failed to iomap \n " ) ;
2016-06-07 00:28:01 +03:00
return - ENXIO ;
}
2014-01-10 18:54:34 +04:00
clk = of_clk_get ( np , 0 ) ;
2016-06-07 00:28:01 +03:00
if ( IS_ERR ( clk ) ) {
2017-03-09 12:47:10 +03:00
pr_err ( " Failed to get clock \n " ) ;
2016-06-07 00:28:01 +03:00
return PTR_ERR ( clk ) ;
}
2013-03-14 00:31:12 +04:00
/* Ensure timer is disabled */
writel ( 0 , base + TIMER_CTRL ) ;
if ( init_count = = 2 | | ! of_device_is_available ( np ) )
goto err ;
2016-06-07 00:28:01 +03:00
if ( ! init_count ) {
ret = __sp804_clocksource_and_sched_clock_init ( base , name , clk , 0 ) ;
if ( ret )
goto err ;
} else {
2013-03-14 00:31:12 +04:00
irq = irq_of_parse_and_map ( np , 0 ) ;
if ( irq < = 0 )
goto err ;
2016-06-07 00:28:01 +03:00
ret = __sp804_clockevents_init ( base , irq , clk , name ) ;
if ( ret )
goto err ;
2013-03-14 00:31:12 +04:00
}
init_count + + ;
2016-06-07 00:28:01 +03:00
return 0 ;
2013-03-14 00:31:12 +04:00
err :
iounmap ( base ) ;
2016-06-07 00:28:01 +03:00
return ret ;
2013-03-14 00:31:12 +04:00
}
2017-05-26 17:56:11 +03:00
TIMER_OF_DECLARE ( intcp , " arm,integrator-cp-timer " , integrator_cp_of_init ) ;