2015-08-07 18:39:31 +03:00
/*
* Pistachio clocksource based on general - purpose timers
*
* Copyright ( C ) 2015 Imagination Technologies
*
* 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 .
*/
# define pr_fmt(fmt) "%s: " fmt, __func__
# include <linux/clk.h>
# include <linux/clocksource.h>
# include <linux/clockchips.h>
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/init.h>
# include <linux/spinlock.h>
# include <linux/mfd/syscon.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/platform_device.h>
# include <linux/regmap.h>
# include <linux/sched_clock.h>
# include <linux/time.h>
/* Top level reg */
# define CR_TIMER_CTRL_CFG 0x00
# define TIMER_ME_GLOBAL BIT(0)
# define CR_TIMER_REV 0x10
/* Timer specific registers */
# define TIMER_CFG 0x20
# define TIMER_ME_LOCAL BIT(0)
# define TIMER_RELOAD_VALUE 0x24
# define TIMER_CURRENT_VALUE 0x28
# define TIMER_CURRENT_OVERFLOW_VALUE 0x2C
# define TIMER_IRQ_STATUS 0x30
# define TIMER_IRQ_CLEAR 0x34
# define TIMER_IRQ_MASK 0x38
# define PERIP_TIMER_CONTROL 0x90
/* Timer specific configuration Values */
# define RELOAD_VALUE 0xffffffff
struct pistachio_clocksource {
void __iomem * base ;
raw_spinlock_t lock ;
struct clocksource cs ;
} ;
static struct pistachio_clocksource pcs_gpt ;
# define to_pistachio_clocksource(cs) \
container_of ( cs , struct pistachio_clocksource , cs )
static inline u32 gpt_readl ( void __iomem * base , u32 offset , u32 gpt_id )
{
return readl ( base + 0x20 * gpt_id + offset ) ;
}
static inline void gpt_writel ( void __iomem * base , u32 value , u32 offset ,
u32 gpt_id )
{
writel ( value , base + 0x20 * gpt_id + offset ) ;
}
2015-10-20 11:02:36 +03:00
static cycle_t notrace
pistachio_clocksource_read_cycles ( struct clocksource * cs )
2015-08-07 18:39:31 +03:00
{
struct pistachio_clocksource * pcs = to_pistachio_clocksource ( cs ) ;
u32 counter , overflw ;
unsigned long flags ;
/*
* The counter value is only refreshed after the overflow value is read .
* And they must be read in strict order , hence raw spin lock added .
*/
raw_spin_lock_irqsave ( & pcs - > lock , flags ) ;
overflw = gpt_readl ( pcs - > base , TIMER_CURRENT_OVERFLOW_VALUE , 0 ) ;
counter = gpt_readl ( pcs - > base , TIMER_CURRENT_VALUE , 0 ) ;
raw_spin_unlock_irqrestore ( & pcs - > lock , flags ) ;
2015-11-25 18:42:49 +03:00
return ( cycle_t ) ~ counter ;
2015-08-07 18:39:31 +03:00
}
static u64 notrace pistachio_read_sched_clock ( void )
{
return pistachio_clocksource_read_cycles ( & pcs_gpt . cs ) ;
}
static void pistachio_clksrc_set_mode ( struct clocksource * cs , int timeridx ,
int enable )
{
struct pistachio_clocksource * pcs = to_pistachio_clocksource ( cs ) ;
u32 val ;
val = gpt_readl ( pcs - > base , TIMER_CFG , timeridx ) ;
if ( enable )
val | = TIMER_ME_LOCAL ;
else
val & = ~ TIMER_ME_LOCAL ;
gpt_writel ( pcs - > base , val , TIMER_CFG , timeridx ) ;
}
static void pistachio_clksrc_enable ( struct clocksource * cs , int timeridx )
{
struct pistachio_clocksource * pcs = to_pistachio_clocksource ( cs ) ;
/* Disable GPT local before loading reload value */
pistachio_clksrc_set_mode ( cs , timeridx , false ) ;
gpt_writel ( pcs - > base , RELOAD_VALUE , TIMER_RELOAD_VALUE , timeridx ) ;
pistachio_clksrc_set_mode ( cs , timeridx , true ) ;
}
static void pistachio_clksrc_disable ( struct clocksource * cs , int timeridx )
{
/* Disable GPT local */
pistachio_clksrc_set_mode ( cs , timeridx , false ) ;
}
static int pistachio_clocksource_enable ( struct clocksource * cs )
{
pistachio_clksrc_enable ( cs , 0 ) ;
return 0 ;
}
static void pistachio_clocksource_disable ( struct clocksource * cs )
{
pistachio_clksrc_disable ( cs , 0 ) ;
}
/* Desirable clock source for pistachio platform */
static struct pistachio_clocksource pcs_gpt = {
. cs = {
. name = " gptimer " ,
. rating = 300 ,
. enable = pistachio_clocksource_enable ,
. disable = pistachio_clocksource_disable ,
. read = pistachio_clocksource_read_cycles ,
. mask = CLOCKSOURCE_MASK ( 32 ) ,
. flags = CLOCK_SOURCE_IS_CONTINUOUS |
CLOCK_SOURCE_SUSPEND_NONSTOP ,
} ,
} ;
2016-06-06 19:00:51 +03:00
static int __init pistachio_clksrc_of_init ( struct device_node * node )
2015-08-07 18:39:31 +03:00
{
struct clk * sys_clk , * fast_clk ;
struct regmap * periph_regs ;
unsigned long rate ;
int ret ;
pcs_gpt . base = of_iomap ( node , 0 ) ;
if ( ! pcs_gpt . base ) {
pr_err ( " cannot iomap \n " ) ;
2016-06-06 19:00:51 +03:00
return - ENXIO ;
2015-08-07 18:39:31 +03:00
}
periph_regs = syscon_regmap_lookup_by_phandle ( node , " img,cr-periph " ) ;
if ( IS_ERR ( periph_regs ) ) {
2016-03-22 02:42:07 +03:00
pr_err ( " cannot get peripheral regmap (%ld) \n " ,
2015-08-07 18:39:31 +03:00
PTR_ERR ( periph_regs ) ) ;
2016-06-06 19:00:51 +03:00
return PTR_ERR ( periph_regs ) ;
2015-08-07 18:39:31 +03:00
}
/* Switch to using the fast counter clock */
ret = regmap_update_bits ( periph_regs , PERIP_TIMER_CONTROL ,
0xf , 0x0 ) ;
if ( ret )
2016-06-06 19:00:51 +03:00
return ret ;
2015-08-07 18:39:31 +03:00
sys_clk = of_clk_get_by_name ( node , " sys " ) ;
if ( IS_ERR ( sys_clk ) ) {
2016-03-22 02:42:07 +03:00
pr_err ( " clock get failed (%ld) \n " , PTR_ERR ( sys_clk ) ) ;
2016-06-06 19:00:51 +03:00
return PTR_ERR ( sys_clk ) ;
2015-08-07 18:39:31 +03:00
}
fast_clk = of_clk_get_by_name ( node , " fast " ) ;
if ( IS_ERR ( fast_clk ) ) {
pr_err ( " clock get failed (%lu) \n " , PTR_ERR ( fast_clk ) ) ;
2016-06-06 19:00:51 +03:00
return PTR_ERR ( fast_clk ) ;
2015-08-07 18:39:31 +03:00
}
ret = clk_prepare_enable ( sys_clk ) ;
if ( ret < 0 ) {
pr_err ( " failed to enable clock (%d) \n " , ret ) ;
2016-06-06 19:00:51 +03:00
return ret ;
2015-08-07 18:39:31 +03:00
}
ret = clk_prepare_enable ( fast_clk ) ;
if ( ret < 0 ) {
pr_err ( " failed to enable clock (%d) \n " , ret ) ;
clk_disable_unprepare ( sys_clk ) ;
2016-06-06 19:00:51 +03:00
return ret ;
2015-08-07 18:39:31 +03:00
}
rate = clk_get_rate ( fast_clk ) ;
/* Disable irq's for clocksource usage */
gpt_writel ( & pcs_gpt . base , 0 , TIMER_IRQ_MASK , 0 ) ;
gpt_writel ( & pcs_gpt . base , 0 , TIMER_IRQ_MASK , 1 ) ;
gpt_writel ( & pcs_gpt . base , 0 , TIMER_IRQ_MASK , 2 ) ;
gpt_writel ( & pcs_gpt . base , 0 , TIMER_IRQ_MASK , 3 ) ;
/* Enable timer block */
writel ( TIMER_ME_GLOBAL , pcs_gpt . base ) ;
raw_spin_lock_init ( & pcs_gpt . lock ) ;
sched_clock_register ( pistachio_read_sched_clock , 32 , rate ) ;
2016-06-06 19:00:51 +03:00
return clocksource_register_hz ( & pcs_gpt . cs , rate ) ;
2015-08-07 18:39:31 +03:00
}
2016-06-07 01:27:44 +03:00
CLOCKSOURCE_OF_DECLARE ( pistachio_gptimer , " img,pistachio-gptimer " ,
2015-08-07 18:39:31 +03:00
pistachio_clksrc_of_init ) ;