2011-06-20 21:47:27 +04:00
/*
* This file contains driver for the Xilinx PS Timer Counter IP .
*
* Copyright ( C ) 2011 Xilinx
*
* based on arch / mips / kernel / time . c timer driver
*
* This software is licensed under the terms of the GNU General Public
* License version 2 , as published by the Free Software Foundation , and
* may be copied , distributed , and modified under those terms .
*
* 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 .
*/
# include <linux/kernel.h>
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/types.h>
# include <linux/clocksource.h>
# include <linux/clockchips.h>
# include <linux/io.h>
2012-10-31 23:56:14 +04:00
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_irq.h>
# include <linux/slab.h>
# include <linux/clk-provider.h>
2011-06-20 21:47:27 +04:00
# include "common.h"
/*
* Timer Register Offset Definitions of Timer 1 , Increment base address by 4
* and use same offsets for Timer 2
*/
2012-12-19 22:18:36 +04:00
# define XTTCPS_CLK_CNTRL_OFFSET 0x00 /* Clock Control Reg, RW */
# define XTTCPS_CNT_CNTRL_OFFSET 0x0C /* Counter Control Reg, RW */
# define XTTCPS_COUNT_VAL_OFFSET 0x18 /* Counter Value Reg, RO */
# define XTTCPS_INTR_VAL_OFFSET 0x24 /* Interval Count Reg, RW */
# define XTTCPS_ISR_OFFSET 0x54 /* Interrupt Status Reg, RO */
# define XTTCPS_IER_OFFSET 0x60 /* Interrupt Enable Reg, RW */
# define XTTCPS_CNT_CNTRL_DISABLE_MASK 0x1
2011-06-20 21:47:27 +04:00
2012-10-31 23:56:14 +04:00
/* Setup the timers to use pre-scaling, using a fixed value for now that will
* work across most input frequency , but it may need to be more dynamic
*/
# define PRESCALE_EXPONENT 11 /* 2 ^ PRESCALE_EXPONENT = PRESCALE */
# define PRESCALE 2048 /* The exponent must match this */
# define CLK_CNTRL_PRESCALE ((PRESCALE_EXPONENT - 1) << 1)
# define CLK_CNTRL_PRESCALE_EN 1
# define CNT_CNTRL_RESET (1<<4)
2011-06-20 21:47:27 +04:00
/**
2012-12-19 22:18:36 +04:00
* struct xttcps_timer - This definition defines local timer structure
2011-06-20 21:47:27 +04:00
*
* @ base_addr : Base address of timer
* */
2012-12-19 22:18:36 +04:00
struct xttcps_timer {
2012-10-31 23:56:14 +04:00
void __iomem * base_addr ;
} ;
2012-12-19 22:18:36 +04:00
struct xttcps_timer_clocksource {
struct xttcps_timer xttc ;
2012-10-31 23:56:14 +04:00
struct clocksource cs ;
2011-06-20 21:47:27 +04:00
} ;
2012-12-19 22:18:36 +04:00
# define to_xttcps_timer_clksrc(x) \
container_of ( x , struct xttcps_timer_clocksource , cs )
2012-10-31 23:56:14 +04:00
2012-12-19 22:18:36 +04:00
struct xttcps_timer_clockevent {
struct xttcps_timer xttc ;
2012-10-31 23:56:14 +04:00
struct clock_event_device ce ;
struct clk * clk ;
} ;
2012-12-19 22:18:36 +04:00
# define to_xttcps_timer_clkevent(x) \
container_of ( x , struct xttcps_timer_clockevent , ce )
2011-06-20 21:47:27 +04:00
/**
2012-12-19 22:18:36 +04:00
* xttcps_set_interval - Set the timer interval value
2011-06-20 21:47:27 +04:00
*
* @ timer : Pointer to the timer instance
* @ cycles : Timer interval ticks
* */
2012-12-19 22:18:36 +04:00
static void xttcps_set_interval ( struct xttcps_timer * timer ,
2011-06-20 21:47:27 +04:00
unsigned long cycles )
{
u32 ctrl_reg ;
/* Disable the counter, set the counter value and re-enable counter */
2012-12-19 22:18:36 +04:00
ctrl_reg = __raw_readl ( timer - > base_addr + XTTCPS_CNT_CNTRL_OFFSET ) ;
ctrl_reg | = XTTCPS_CNT_CNTRL_DISABLE_MASK ;
__raw_writel ( ctrl_reg , timer - > base_addr + XTTCPS_CNT_CNTRL_OFFSET ) ;
2011-06-20 21:47:27 +04:00
2012-12-19 22:18:36 +04:00
__raw_writel ( cycles , timer - > base_addr + XTTCPS_INTR_VAL_OFFSET ) ;
2011-06-20 21:47:27 +04:00
/* Reset the counter (0x10) so that it starts from 0, one-shot
mode makes this needed for timing to be right . */
2012-10-31 23:56:14 +04:00
ctrl_reg | = CNT_CNTRL_RESET ;
2012-12-19 22:18:36 +04:00
ctrl_reg & = ~ XTTCPS_CNT_CNTRL_DISABLE_MASK ;
__raw_writel ( ctrl_reg , timer - > base_addr + XTTCPS_CNT_CNTRL_OFFSET ) ;
2011-06-20 21:47:27 +04:00
}
/**
2012-12-19 22:18:36 +04:00
* xttcps_clock_event_interrupt - Clock event timer interrupt handler
2011-06-20 21:47:27 +04:00
*
* @ irq : IRQ number of the Timer
2012-12-19 22:18:36 +04:00
* @ dev_id : void pointer to the xttcps_timer instance
2011-06-20 21:47:27 +04:00
*
* returns : Always IRQ_HANDLED - success
* */
2012-12-19 22:18:36 +04:00
static irqreturn_t xttcps_clock_event_interrupt ( int irq , void * dev_id )
2011-06-20 21:47:27 +04:00
{
2012-12-19 22:18:36 +04:00
struct xttcps_timer_clockevent * xttce = dev_id ;
struct xttcps_timer * timer = & xttce - > xttc ;
2011-06-20 21:47:27 +04:00
/* Acknowledge the interrupt and call event handler */
2012-12-19 22:18:37 +04:00
__raw_readl ( timer - > base_addr + XTTCPS_ISR_OFFSET ) ;
2011-06-20 21:47:27 +04:00
2012-10-31 23:56:14 +04:00
xttce - > ce . event_handler ( & xttce - > ce ) ;
2011-06-20 21:47:27 +04:00
return IRQ_HANDLED ;
}
/**
2012-10-31 23:56:14 +04:00
* __xttc_clocksource_read - Reads the timer counter register
2011-06-20 21:47:27 +04:00
*
* returns : Current timer counter register value
* */
2012-10-31 23:56:14 +04:00
static cycle_t __xttc_clocksource_read ( struct clocksource * cs )
2011-06-20 21:47:27 +04:00
{
2012-12-19 22:18:36 +04:00
struct xttcps_timer * timer = & to_xttcps_timer_clksrc ( cs ) - > xttc ;
2011-06-20 21:47:27 +04:00
return ( cycle_t ) __raw_readl ( timer - > base_addr +
2012-12-19 22:18:36 +04:00
XTTCPS_COUNT_VAL_OFFSET ) ;
2011-06-20 21:47:27 +04:00
}
/**
2012-12-19 22:18:36 +04:00
* xttcps_set_next_event - Sets the time interval for next event
2011-06-20 21:47:27 +04:00
*
* @ cycles : Timer interval ticks
* @ evt : Address of clock event instance
*
* returns : Always 0 - success
* */
2012-12-19 22:18:36 +04:00
static int xttcps_set_next_event ( unsigned long cycles ,
2011-06-20 21:47:27 +04:00
struct clock_event_device * evt )
{
2012-12-19 22:18:36 +04:00
struct xttcps_timer_clockevent * xttce = to_xttcps_timer_clkevent ( evt ) ;
struct xttcps_timer * timer = & xttce - > xttc ;
2011-06-20 21:47:27 +04:00
2012-12-19 22:18:36 +04:00
xttcps_set_interval ( timer , cycles ) ;
2011-06-20 21:47:27 +04:00
return 0 ;
}
/**
2012-12-19 22:18:36 +04:00
* xttcps_set_mode - Sets the mode of timer
2011-06-20 21:47:27 +04:00
*
* @ mode : Mode to be set
* @ evt : Address of clock event instance
* */
2012-12-19 22:18:36 +04:00
static void xttcps_set_mode ( enum clock_event_mode mode ,
2011-06-20 21:47:27 +04:00
struct clock_event_device * evt )
{
2012-12-19 22:18:36 +04:00
struct xttcps_timer_clockevent * xttce = to_xttcps_timer_clkevent ( evt ) ;
struct xttcps_timer * timer = & xttce - > xttc ;
2011-06-20 21:47:27 +04:00
u32 ctrl_reg ;
switch ( mode ) {
case CLOCK_EVT_MODE_PERIODIC :
2012-12-19 22:18:36 +04:00
xttcps_set_interval ( timer ,
2012-10-31 23:56:14 +04:00
DIV_ROUND_CLOSEST ( clk_get_rate ( xttce - > clk ) ,
PRESCALE * HZ ) ) ;
2011-06-20 21:47:27 +04:00
break ;
case CLOCK_EVT_MODE_ONESHOT :
case CLOCK_EVT_MODE_UNUSED :
case CLOCK_EVT_MODE_SHUTDOWN :
ctrl_reg = __raw_readl ( timer - > base_addr +
2012-12-19 22:18:36 +04:00
XTTCPS_CNT_CNTRL_OFFSET ) ;
ctrl_reg | = XTTCPS_CNT_CNTRL_DISABLE_MASK ;
2011-06-20 21:47:27 +04:00
__raw_writel ( ctrl_reg ,
2012-12-19 22:18:36 +04:00
timer - > base_addr + XTTCPS_CNT_CNTRL_OFFSET ) ;
2011-06-20 21:47:27 +04:00
break ;
case CLOCK_EVT_MODE_RESUME :
ctrl_reg = __raw_readl ( timer - > base_addr +
2012-12-19 22:18:36 +04:00
XTTCPS_CNT_CNTRL_OFFSET ) ;
ctrl_reg & = ~ XTTCPS_CNT_CNTRL_DISABLE_MASK ;
2011-06-20 21:47:27 +04:00
__raw_writel ( ctrl_reg ,
2012-12-19 22:18:36 +04:00
timer - > base_addr + XTTCPS_CNT_CNTRL_OFFSET ) ;
2011-06-20 21:47:27 +04:00
break ;
}
}
2012-10-31 23:56:14 +04:00
static void __init zynq_ttc_setup_clocksource ( struct device_node * np ,
void __iomem * base )
{
2012-12-19 22:18:36 +04:00
struct xttcps_timer_clocksource * ttccs ;
2012-10-31 23:56:14 +04:00
struct clk * clk ;
int err ;
u32 reg ;
ttccs = kzalloc ( sizeof ( * ttccs ) , GFP_KERNEL ) ;
if ( WARN_ON ( ! ttccs ) )
return ;
err = of_property_read_u32 ( np , " reg " , & reg ) ;
if ( WARN_ON ( err ) )
return ;
clk = of_clk_get_by_name ( np , " cpu_1x " ) ;
if ( WARN_ON ( IS_ERR ( clk ) ) )
return ;
err = clk_prepare_enable ( clk ) ;
if ( WARN_ON ( err ) )
return ;
ttccs - > xttc . base_addr = base + reg * 4 ;
ttccs - > cs . name = np - > name ;
ttccs - > cs . rating = 200 ;
ttccs - > cs . read = __xttc_clocksource_read ;
ttccs - > cs . mask = CLOCKSOURCE_MASK ( 16 ) ;
ttccs - > cs . flags = CLOCK_SOURCE_IS_CONTINUOUS ;
2012-12-19 22:18:36 +04:00
__raw_writel ( 0x0 , ttccs - > xttc . base_addr + XTTCPS_IER_OFFSET ) ;
2012-10-31 23:56:14 +04:00
__raw_writel ( CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN ,
2012-12-19 22:18:36 +04:00
ttccs - > xttc . base_addr + XTTCPS_CLK_CNTRL_OFFSET ) ;
2012-10-31 23:56:14 +04:00
__raw_writel ( CNT_CNTRL_RESET ,
2012-12-19 22:18:36 +04:00
ttccs - > xttc . base_addr + XTTCPS_CNT_CNTRL_OFFSET ) ;
2012-10-31 23:56:14 +04:00
err = clocksource_register_hz ( & ttccs - > cs , clk_get_rate ( clk ) / PRESCALE ) ;
if ( WARN_ON ( err ) )
return ;
}
static void __init zynq_ttc_setup_clockevent ( struct device_node * np ,
void __iomem * base )
{
2012-12-19 22:18:36 +04:00
struct xttcps_timer_clockevent * ttcce ;
2012-10-31 23:56:14 +04:00
int err , irq ;
u32 reg ;
ttcce = kzalloc ( sizeof ( * ttcce ) , GFP_KERNEL ) ;
if ( WARN_ON ( ! ttcce ) )
return ;
err = of_property_read_u32 ( np , " reg " , & reg ) ;
if ( WARN_ON ( err ) )
return ;
ttcce - > xttc . base_addr = base + reg * 4 ;
ttcce - > clk = of_clk_get_by_name ( np , " cpu_1x " ) ;
if ( WARN_ON ( IS_ERR ( ttcce - > clk ) ) )
return ;
err = clk_prepare_enable ( ttcce - > clk ) ;
if ( WARN_ON ( err ) )
return ;
irq = irq_of_parse_and_map ( np , 0 ) ;
if ( WARN_ON ( ! irq ) )
return ;
ttcce - > ce . name = np - > name ;
ttcce - > ce . features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT ;
2012-12-19 22:18:36 +04:00
ttcce - > ce . set_next_event = xttcps_set_next_event ;
ttcce - > ce . set_mode = xttcps_set_mode ;
2012-10-31 23:56:14 +04:00
ttcce - > ce . rating = 200 ;
ttcce - > ce . irq = irq ;
2012-12-19 22:18:36 +04:00
__raw_writel ( 0x23 , ttcce - > xttc . base_addr + XTTCPS_CNT_CNTRL_OFFSET ) ;
2012-10-31 23:56:14 +04:00
__raw_writel ( CLK_CNTRL_PRESCALE | CLK_CNTRL_PRESCALE_EN ,
2012-12-19 22:18:36 +04:00
ttcce - > xttc . base_addr + XTTCPS_CLK_CNTRL_OFFSET ) ;
__raw_writel ( 0x1 , ttcce - > xttc . base_addr + XTTCPS_IER_OFFSET ) ;
2012-10-31 23:56:14 +04:00
2012-12-19 22:18:36 +04:00
err = request_irq ( irq , xttcps_clock_event_interrupt , IRQF_TIMER ,
2012-10-31 23:56:14 +04:00
np - > name , ttcce ) ;
if ( WARN_ON ( err ) )
return ;
clockevents_config_and_register ( & ttcce - > ce ,
clk_get_rate ( ttcce - > clk ) / PRESCALE ,
1 , 0xfffe ) ;
}
static const __initconst struct of_device_id zynq_ttc_match [ ] = {
{ . compatible = " xlnx,ttc-counter-clocksource " ,
. data = zynq_ttc_setup_clocksource , } ,
{ . compatible = " xlnx,ttc-counter-clockevent " ,
. data = zynq_ttc_setup_clockevent , } ,
{ }
2011-06-20 21:47:27 +04:00
} ;
/**
2012-12-19 22:18:36 +04:00
* xttcps_timer_init - Initialize the timer
2011-06-20 21:47:27 +04:00
*
* Initializes the timer hardware and register the clock source and clock event
* timers with Linux kernal timer framework
* */
2012-12-19 22:18:36 +04:00
void __init xttcps_timer_init ( void )
2011-06-20 21:47:27 +04:00
{
2012-10-31 23:56:14 +04:00
struct device_node * np ;
for_each_compatible_node ( np , NULL , " xlnx,ttc " ) {
struct device_node * np_chld ;
void __iomem * base ;
base = of_iomap ( np , 0 ) ;
if ( WARN_ON ( ! base ) )
return ;
for_each_available_child_of_node ( np , np_chld ) {
int ( * cb ) ( struct device_node * np , void __iomem * base ) ;
const struct of_device_id * match ;
match = of_match_node ( zynq_ttc_match , np_chld ) ;
if ( match ) {
cb = match - > data ;
cb ( np_chld , base ) ;
}
}
}
2011-06-20 21:47:27 +04:00
}