2014-11-06 10:20:04 +03:00
/*
* Copyright ( C ) 2013 - 2014 Altera Corporation
* Copyright ( C ) 2010 Tobias Klauser < tklauser @ distanz . ch >
* Copyright ( C ) 2004 Microtronix Datacom Ltd .
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*/
2015-06-09 07:46:46 +03:00
# include <linux/export.h>
2014-11-06 10:20:04 +03:00
# include <linux/interrupt.h>
# include <linux/clockchips.h>
# include <linux/clocksource.h>
# include <linux/delay.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_irq.h>
# include <linux/io.h>
# include <linux/slab.h>
2015-06-24 12:52:44 +03:00
# define ALTR_TIMER_COMPATIBLE "altr,timer-1.0"
# define ALTERA_TIMER_STATUS_REG 0
2014-11-06 10:20:04 +03:00
# define ALTERA_TIMER_CONTROL_REG 4
# define ALTERA_TIMER_PERIODL_REG 8
# define ALTERA_TIMER_PERIODH_REG 12
# define ALTERA_TIMER_SNAPL_REG 16
# define ALTERA_TIMER_SNAPH_REG 20
# define ALTERA_TIMER_CONTROL_ITO_MSK (0x1)
# define ALTERA_TIMER_CONTROL_CONT_MSK (0x2)
# define ALTERA_TIMER_CONTROL_START_MSK (0x4)
# define ALTERA_TIMER_CONTROL_STOP_MSK (0x8)
struct nios2_timer {
void __iomem * base ;
unsigned long freq ;
} ;
struct nios2_clockevent_dev {
struct nios2_timer timer ;
struct clock_event_device ced ;
} ;
struct nios2_clocksource {
struct nios2_timer timer ;
struct clocksource cs ;
} ;
static inline struct nios2_clockevent_dev *
to_nios2_clkevent ( struct clock_event_device * evt )
{
return container_of ( evt , struct nios2_clockevent_dev , ced ) ;
}
static inline struct nios2_clocksource *
to_nios2_clksource ( struct clocksource * cs )
{
return container_of ( cs , struct nios2_clocksource , cs ) ;
}
static u16 timer_readw ( struct nios2_timer * timer , u32 offs )
{
return readw ( timer - > base + offs ) ;
}
static void timer_writew ( struct nios2_timer * timer , u16 val , u32 offs )
{
writew ( val , timer - > base + offs ) ;
}
static inline unsigned long read_timersnapshot ( struct nios2_timer * timer )
{
unsigned long count ;
timer_writew ( timer , 0 , ALTERA_TIMER_SNAPL_REG ) ;
count = timer_readw ( timer , ALTERA_TIMER_SNAPH_REG ) < < 16 |
timer_readw ( timer , ALTERA_TIMER_SNAPL_REG ) ;
return count ;
}
static cycle_t nios2_timer_read ( struct clocksource * cs )
{
struct nios2_clocksource * nios2_cs = to_nios2_clksource ( cs ) ;
unsigned long flags ;
u32 count ;
local_irq_save ( flags ) ;
count = read_timersnapshot ( & nios2_cs - > timer ) ;
local_irq_restore ( flags ) ;
/* Counter is counting down */
return ~ count ;
}
static struct nios2_clocksource nios2_cs = {
. cs = {
. name = " nios2-clksrc " ,
. rating = 250 ,
. read = nios2_timer_read ,
. mask = CLOCKSOURCE_MASK ( 32 ) ,
. flags = CLOCK_SOURCE_IS_CONTINUOUS ,
} ,
} ;
cycles_t get_cycles ( void )
{
return nios2_timer_read ( & nios2_cs . cs ) ;
}
2015-06-09 07:46:46 +03:00
EXPORT_SYMBOL ( get_cycles ) ;
2014-11-06 10:20:04 +03:00
static void nios2_timer_start ( struct nios2_timer * timer )
{
u16 ctrl ;
ctrl = timer_readw ( timer , ALTERA_TIMER_CONTROL_REG ) ;
ctrl | = ALTERA_TIMER_CONTROL_START_MSK ;
timer_writew ( timer , ctrl , ALTERA_TIMER_CONTROL_REG ) ;
}
static void nios2_timer_stop ( struct nios2_timer * timer )
{
u16 ctrl ;
ctrl = timer_readw ( timer , ALTERA_TIMER_CONTROL_REG ) ;
ctrl | = ALTERA_TIMER_CONTROL_STOP_MSK ;
timer_writew ( timer , ctrl , ALTERA_TIMER_CONTROL_REG ) ;
}
static void nios2_timer_config ( struct nios2_timer * timer , unsigned long period ,
2015-08-18 08:59:28 +03:00
bool periodic )
2014-11-06 10:20:04 +03:00
{
u16 ctrl ;
/* The timer's actual period is one cycle greater than the value
* stored in the period register . */
period - - ;
ctrl = timer_readw ( timer , ALTERA_TIMER_CONTROL_REG ) ;
/* stop counter */
timer_writew ( timer , ctrl | ALTERA_TIMER_CONTROL_STOP_MSK ,
ALTERA_TIMER_CONTROL_REG ) ;
/* write new count */
timer_writew ( timer , period , ALTERA_TIMER_PERIODL_REG ) ;
timer_writew ( timer , period > > 16 , ALTERA_TIMER_PERIODH_REG ) ;
ctrl | = ALTERA_TIMER_CONTROL_START_MSK | ALTERA_TIMER_CONTROL_ITO_MSK ;
2015-08-18 08:59:28 +03:00
if ( periodic )
2014-11-06 10:20:04 +03:00
ctrl | = ALTERA_TIMER_CONTROL_CONT_MSK ;
else
ctrl & = ~ ALTERA_TIMER_CONTROL_CONT_MSK ;
timer_writew ( timer , ctrl , ALTERA_TIMER_CONTROL_REG ) ;
}
static int nios2_timer_set_next_event ( unsigned long delta ,
struct clock_event_device * evt )
{
struct nios2_clockevent_dev * nios2_ced = to_nios2_clkevent ( evt ) ;
2015-08-18 08:59:28 +03:00
nios2_timer_config ( & nios2_ced - > timer , delta , false ) ;
2014-11-06 10:20:04 +03:00
return 0 ;
}
2015-08-18 08:59:28 +03:00
static int nios2_timer_shutdown ( struct clock_event_device * evt )
{
struct nios2_clockevent_dev * nios2_ced = to_nios2_clkevent ( evt ) ;
struct nios2_timer * timer = & nios2_ced - > timer ;
nios2_timer_stop ( timer ) ;
return 0 ;
}
static int nios2_timer_set_periodic ( struct clock_event_device * evt )
2014-11-06 10:20:04 +03:00
{
unsigned long period ;
struct nios2_clockevent_dev * nios2_ced = to_nios2_clkevent ( evt ) ;
struct nios2_timer * timer = & nios2_ced - > timer ;
2015-08-18 08:59:28 +03:00
period = DIV_ROUND_UP ( timer - > freq , HZ ) ;
nios2_timer_config ( timer , period , true ) ;
return 0 ;
}
static int nios2_timer_resume ( struct clock_event_device * evt )
{
struct nios2_clockevent_dev * nios2_ced = to_nios2_clkevent ( evt ) ;
struct nios2_timer * timer = & nios2_ced - > timer ;
nios2_timer_start ( timer ) ;
return 0 ;
2014-11-06 10:20:04 +03:00
}
irqreturn_t timer_interrupt ( int irq , void * dev_id )
{
struct clock_event_device * evt = ( struct clock_event_device * ) dev_id ;
struct nios2_clockevent_dev * nios2_ced = to_nios2_clkevent ( evt ) ;
/* Clear the interrupt condition */
timer_writew ( & nios2_ced - > timer , 0 , ALTERA_TIMER_STATUS_REG ) ;
evt - > event_handler ( evt ) ;
return IRQ_HANDLED ;
}
2016-06-07 01:04:02 +03:00
static int __init nios2_timer_get_base_and_freq ( struct device_node * np ,
2014-11-06 10:20:04 +03:00
void __iomem * * base , u32 * freq )
{
* base = of_iomap ( np , 0 ) ;
2016-06-07 01:04:02 +03:00
if ( ! * base ) {
pr_crit ( " Unable to map reg for %s \n " , np - > name ) ;
return - ENXIO ;
}
if ( of_property_read_u32 ( np , " clock-frequency " , freq ) ) {
pr_crit ( " Unable to get %s clock frequency \n " , np - > name ) ;
return - EINVAL ;
}
2014-11-06 10:20:04 +03:00
2016-06-07 01:04:02 +03:00
return 0 ;
2014-11-06 10:20:04 +03:00
}
static struct nios2_clockevent_dev nios2_ce = {
. ced = {
. name = " nios2-clkevent " ,
. features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT ,
. rating = 250 ,
. shift = 32 ,
. set_next_event = nios2_timer_set_next_event ,
2015-08-18 08:59:28 +03:00
. set_state_shutdown = nios2_timer_shutdown ,
. set_state_periodic = nios2_timer_set_periodic ,
. set_state_oneshot = nios2_timer_shutdown ,
. tick_resume = nios2_timer_resume ,
2014-11-06 10:20:04 +03:00
} ,
} ;
2016-06-07 01:04:02 +03:00
static __init int nios2_clockevent_init ( struct device_node * timer )
2014-11-06 10:20:04 +03:00
{
void __iomem * iobase ;
u32 freq ;
2016-06-07 01:04:02 +03:00
int irq , ret ;
2014-11-06 10:20:04 +03:00
2016-06-07 01:04:02 +03:00
ret = nios2_timer_get_base_and_freq ( timer , & iobase , & freq ) ;
if ( ret )
return ret ;
2014-11-06 10:20:04 +03:00
irq = irq_of_parse_and_map ( timer , 0 ) ;
2016-06-07 01:04:02 +03:00
if ( ! irq ) {
pr_crit ( " Unable to parse timer irq \n " ) ;
return - EINVAL ;
}
2014-11-06 10:20:04 +03:00
nios2_ce . timer . base = iobase ;
nios2_ce . timer . freq = freq ;
nios2_ce . ced . cpumask = cpumask_of ( 0 ) ;
nios2_ce . ced . irq = irq ;
nios2_timer_stop ( & nios2_ce . timer ) ;
/* clear pending interrupt */
timer_writew ( & nios2_ce . timer , 0 , ALTERA_TIMER_STATUS_REG ) ;
2016-06-07 01:04:02 +03:00
ret = request_irq ( irq , timer_interrupt , IRQF_TIMER , timer - > name ,
& nios2_ce . ced ) ;
if ( ret ) {
pr_crit ( " Unable to setup timer irq \n " ) ;
return ret ;
}
2014-11-06 10:20:04 +03:00
clockevents_config_and_register ( & nios2_ce . ced , freq , 1 , ULONG_MAX ) ;
2016-06-07 01:04:02 +03:00
return 0 ;
2014-11-06 10:20:04 +03:00
}
2016-06-07 01:04:02 +03:00
static __init int nios2_clocksource_init ( struct device_node * timer )
2014-11-06 10:20:04 +03:00
{
unsigned int ctrl ;
void __iomem * iobase ;
u32 freq ;
2016-06-07 01:04:02 +03:00
int ret ;
2014-11-06 10:20:04 +03:00
2016-06-07 01:04:02 +03:00
ret = nios2_timer_get_base_and_freq ( timer , & iobase , & freq ) ;
if ( ret )
return ret ;
2014-11-06 10:20:04 +03:00
nios2_cs . timer . base = iobase ;
nios2_cs . timer . freq = freq ;
2016-06-07 01:04:02 +03:00
ret = clocksource_register_hz ( & nios2_cs . cs , freq ) ;
if ( ret )
return ret ;
2014-11-06 10:20:04 +03:00
timer_writew ( & nios2_cs . timer , USHRT_MAX , ALTERA_TIMER_PERIODL_REG ) ;
timer_writew ( & nios2_cs . timer , USHRT_MAX , ALTERA_TIMER_PERIODH_REG ) ;
/* interrupt disable + continuous + start */
ctrl = ALTERA_TIMER_CONTROL_CONT_MSK | ALTERA_TIMER_CONTROL_START_MSK ;
timer_writew ( & nios2_cs . timer , ctrl , ALTERA_TIMER_CONTROL_REG ) ;
/* Calibrate the delay loop directly */
lpj_fine = freq / HZ ;
2016-06-07 01:04:02 +03:00
return 0 ;
2014-11-06 10:20:04 +03:00
}
/*
* The first timer instance will use as a clockevent . If there are two or
* more instances , the second one gets used as clocksource and all
* others are unused .
*/
2016-06-07 01:04:02 +03:00
static int __init nios2_time_init ( struct device_node * timer )
2014-11-06 10:20:04 +03:00
{
static int num_called ;
2016-06-07 01:04:02 +03:00
int ret ;
2014-11-06 10:20:04 +03:00
switch ( num_called ) {
case 0 :
2016-06-07 01:04:02 +03:00
ret = nios2_clockevent_init ( timer ) ;
2014-11-06 10:20:04 +03:00
break ;
case 1 :
2016-06-07 01:04:02 +03:00
ret = nios2_clocksource_init ( timer ) ;
2014-11-06 10:20:04 +03:00
break ;
default :
break ;
}
num_called + + ;
2016-06-07 01:04:02 +03:00
return ret ;
2014-11-06 10:20:04 +03:00
}
void read_persistent_clock ( struct timespec * ts )
{
ts - > tv_sec = mktime ( 2007 , 1 , 1 , 0 , 0 , 0 ) ;
ts - > tv_nsec = 0 ;
}
void __init time_init ( void )
{
2015-06-24 12:52:44 +03:00
struct device_node * np ;
int count = 0 ;
for_each_compatible_node ( np , NULL , ALTR_TIMER_COMPATIBLE )
count + + ;
if ( count < 2 )
panic ( " %d timer is found, it needs 2 timers in system \n " , count ) ;
2015-09-28 17:49:18 +03:00
clocksource_probe ( ) ;
2014-11-06 10:20:04 +03:00
}
2016-06-07 01:27:44 +03:00
CLOCKSOURCE_OF_DECLARE ( nios2_timer , ALTR_TIMER_COMPATIBLE , nios2_time_init ) ;