2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2014-07-13 08:45:13 +04:00
/*
* Cirrus Logic CLPS711X clocksource driver
*
* Copyright ( C ) 2014 Alexander Shiyan < shc_work @ mail . ru >
*/
# include <linux/clk.h>
# include <linux/clockchips.h>
# include <linux/clocksource.h>
# include <linux/interrupt.h>
# include <linux/io.h>
# include <linux/of_address.h>
# include <linux/of_irq.h>
# include <linux/sched_clock.h>
# include <linux/slab.h>
enum {
CLPS711X_CLKSRC_CLOCKSOURCE ,
CLPS711X_CLKSRC_CLOCKEVENT ,
} ;
static void __iomem * tcd ;
static u64 notrace clps711x_sched_clock_read ( void )
{
return ~ readw ( tcd ) ;
}
2018-12-20 14:16:26 +03:00
static void __init clps711x_clksrc_init ( struct clk * clock , void __iomem * base )
2014-07-13 08:45:13 +04:00
{
2018-12-20 14:16:26 +03:00
unsigned long rate = clk_get_rate ( clock ) ;
2014-07-13 08:45:13 +04:00
tcd = base ;
clocksource_mmio_init ( tcd , " clps711x-clocksource " , rate , 300 , 16 ,
clocksource_mmio_readw_down ) ;
sched_clock_register ( clps711x_sched_clock_read , 16 , rate ) ;
}
static irqreturn_t clps711x_timer_interrupt ( int irq , void * dev_id )
{
struct clock_event_device * evt = dev_id ;
evt - > event_handler ( evt ) ;
return IRQ_HANDLED ;
}
static int __init _clps711x_clkevt_init ( struct clk * clock , void __iomem * base ,
unsigned int irq )
{
struct clock_event_device * clkevt ;
unsigned long rate ;
clkevt = kzalloc ( sizeof ( * clkevt ) , GFP_KERNEL ) ;
if ( ! clkevt )
return - ENOMEM ;
rate = clk_get_rate ( clock ) ;
/* Set Timer prescaler */
writew ( DIV_ROUND_CLOSEST ( rate , HZ ) , base ) ;
clkevt - > name = " clps711x-clockevent " ;
clkevt - > rating = 300 ;
clkevt - > features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_C3STOP ;
clkevt - > cpumask = cpumask_of ( 0 ) ;
clockevents_config_and_register ( clkevt , HZ , 0 , 0 ) ;
return request_irq ( irq , clps711x_timer_interrupt , IRQF_TIMER ,
" clps711x-timer " , clkevt ) ;
}
2016-06-02 21:07:35 +03:00
static int __init clps711x_timer_init ( struct device_node * np )
2014-07-13 08:45:13 +04:00
{
unsigned int irq = irq_of_parse_and_map ( np , 0 ) ;
struct clk * clock = of_clk_get ( np , 0 ) ;
void __iomem * base = of_iomap ( np , 0 ) ;
2018-12-20 14:16:26 +03:00
if ( ! base )
return - ENOMEM ;
if ( ! irq )
return - EINVAL ;
if ( IS_ERR ( clock ) )
return PTR_ERR ( clock ) ;
2014-07-13 08:45:13 +04:00
switch ( of_alias_get_id ( np , " timer " ) ) {
case CLPS711X_CLKSRC_CLOCKSOURCE :
2018-12-20 14:16:26 +03:00
clps711x_clksrc_init ( clock , base ) ;
break ;
2014-07-13 08:45:13 +04:00
case CLPS711X_CLKSRC_CLOCKEVENT :
2016-06-02 21:07:35 +03:00
return _clps711x_clkevt_init ( clock , base , irq ) ;
2014-07-13 08:45:13 +04:00
default :
2016-06-02 21:07:35 +03:00
return - EINVAL ;
2014-07-13 08:45:13 +04:00
}
2018-12-20 14:16:26 +03:00
return 0 ;
2014-07-13 08:45:13 +04:00
}
2017-05-26 17:56:11 +03:00
TIMER_OF_DECLARE ( clps711x , " cirrus,ep7209-timer " , clps711x_timer_init ) ;