2019-05-28 19:57:08 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2012-05-25 11:03:44 +04:00
/*
* Emma Mobile Timer Support - STI
*
* Copyright ( C ) 2012 Magnus Damm
*/
# include <linux/init.h>
# include <linux/platform_device.h>
# include <linux/spinlock.h>
# include <linux/interrupt.h>
# include <linux/ioport.h>
# include <linux/io.h>
# include <linux/clk.h>
# include <linux/irq.h>
# include <linux/err.h>
# include <linux/delay.h>
# include <linux/clocksource.h>
# include <linux/clockchips.h>
# include <linux/slab.h>
# include <linux/module.h>
enum { USER_CLOCKSOURCE , USER_CLOCKEVENT , USER_NR } ;
struct em_sti_priv {
void __iomem * base ;
struct clk * clk ;
struct platform_device * pdev ;
unsigned int active [ USER_NR ] ;
unsigned long rate ;
raw_spinlock_t lock ;
struct clock_event_device ced ;
struct clocksource cs ;
} ;
# define STI_CONTROL 0x00
# define STI_COMPA_H 0x10
# define STI_COMPA_L 0x14
# define STI_COMPB_H 0x18
# define STI_COMPB_L 0x1c
# define STI_COUNT_H 0x20
# define STI_COUNT_L 0x24
# define STI_COUNT_RAW_H 0x28
# define STI_COUNT_RAW_L 0x2c
# define STI_SET_H 0x30
# define STI_SET_L 0x34
# define STI_INTSTATUS 0x40
# define STI_INTRAWSTATUS 0x44
# define STI_INTENSET 0x48
# define STI_INTENCLR 0x4c
# define STI_INTFFCLR 0x50
static inline unsigned long em_sti_read ( struct em_sti_priv * p , int offs )
{
return ioread32 ( p - > base + offs ) ;
}
static inline void em_sti_write ( struct em_sti_priv * p , int offs ,
unsigned long value )
{
iowrite32 ( value , p - > base + offs ) ;
}
static int em_sti_enable ( struct em_sti_priv * p )
{
int ret ;
/* enable clock */
2017-02-07 00:12:01 +03:00
ret = clk_enable ( p - > clk ) ;
2012-05-25 11:03:44 +04:00
if ( ret ) {
dev_err ( & p - > pdev - > dev , " cannot enable clock \n " ) ;
return ret ;
}
/* reset the counter */
em_sti_write ( p , STI_SET_H , 0x40000000 ) ;
em_sti_write ( p , STI_SET_L , 0x00000000 ) ;
/* mask and clear pending interrupts */
em_sti_write ( p , STI_INTENCLR , 3 ) ;
em_sti_write ( p , STI_INTFFCLR , 3 ) ;
/* enable updates of counter registers */
em_sti_write ( p , STI_CONTROL , 1 ) ;
return 0 ;
}
static void em_sti_disable ( struct em_sti_priv * p )
{
/* mask interrupts */
em_sti_write ( p , STI_INTENCLR , 3 ) ;
/* stop clock */
2017-02-07 00:12:01 +03:00
clk_disable ( p - > clk ) ;
2012-05-25 11:03:44 +04:00
}
2016-12-21 22:32:01 +03:00
static u64 em_sti_count ( struct em_sti_priv * p )
2012-05-25 11:03:44 +04:00
{
2016-12-21 22:32:01 +03:00
u64 ticks ;
2012-05-25 11:03:44 +04:00
unsigned long flags ;
/* the STI hardware buffers the 48-bit count, but to
* break it out into two 32 - bit access the registers
* must be accessed in a certain order .
* Always read STI_COUNT_H before STI_COUNT_L .
*/
raw_spin_lock_irqsave ( & p - > lock , flags ) ;
2016-12-21 22:32:01 +03:00
ticks = ( u64 ) ( em_sti_read ( p , STI_COUNT_H ) & 0xffff ) < < 32 ;
2012-05-25 11:03:44 +04:00
ticks | = em_sti_read ( p , STI_COUNT_L ) ;
raw_spin_unlock_irqrestore ( & p - > lock , flags ) ;
return ticks ;
}
2016-12-21 22:32:01 +03:00
static u64 em_sti_set_next ( struct em_sti_priv * p , u64 next )
2012-05-25 11:03:44 +04:00
{
unsigned long flags ;
raw_spin_lock_irqsave ( & p - > lock , flags ) ;
/* mask compare A interrupt */
em_sti_write ( p , STI_INTENCLR , 1 ) ;
/* update compare A value */
em_sti_write ( p , STI_COMPA_H , next > > 32 ) ;
em_sti_write ( p , STI_COMPA_L , next & 0xffffffff ) ;
/* clear compare A interrupt source */
em_sti_write ( p , STI_INTFFCLR , 1 ) ;
/* unmask compare A interrupt */
em_sti_write ( p , STI_INTENSET , 1 ) ;
raw_spin_unlock_irqrestore ( & p - > lock , flags ) ;
return next ;
}
static irqreturn_t em_sti_interrupt ( int irq , void * dev_id )
{
struct em_sti_priv * p = dev_id ;
p - > ced . event_handler ( & p - > ced ) ;
return IRQ_HANDLED ;
}
static int em_sti_start ( struct em_sti_priv * p , unsigned int user )
{
unsigned long flags ;
int used_before ;
int ret = 0 ;
raw_spin_lock_irqsave ( & p - > lock , flags ) ;
used_before = p - > active [ USER_CLOCKSOURCE ] | p - > active [ USER_CLOCKEVENT ] ;
if ( ! used_before )
ret = em_sti_enable ( p ) ;
if ( ! ret )
p - > active [ user ] = 1 ;
raw_spin_unlock_irqrestore ( & p - > lock , flags ) ;
return ret ;
}
static void em_sti_stop ( struct em_sti_priv * p , unsigned int user )
{
unsigned long flags ;
int used_before , used_after ;
raw_spin_lock_irqsave ( & p - > lock , flags ) ;
used_before = p - > active [ USER_CLOCKSOURCE ] | p - > active [ USER_CLOCKEVENT ] ;
p - > active [ user ] = 0 ;
used_after = p - > active [ USER_CLOCKSOURCE ] | p - > active [ USER_CLOCKEVENT ] ;
if ( used_before & & ! used_after )
em_sti_disable ( p ) ;
raw_spin_unlock_irqrestore ( & p - > lock , flags ) ;
}
static struct em_sti_priv * cs_to_em_sti ( struct clocksource * cs )
{
return container_of ( cs , struct em_sti_priv , cs ) ;
}
2016-12-21 22:32:01 +03:00
static u64 em_sti_clocksource_read ( struct clocksource * cs )
2012-05-25 11:03:44 +04:00
{
return em_sti_count ( cs_to_em_sti ( cs ) ) ;
}
static int em_sti_clocksource_enable ( struct clocksource * cs )
{
struct em_sti_priv * p = cs_to_em_sti ( cs ) ;
2017-02-07 00:12:02 +03:00
return em_sti_start ( p , USER_CLOCKSOURCE ) ;
2012-05-25 11:03:44 +04:00
}
static void em_sti_clocksource_disable ( struct clocksource * cs )
{
em_sti_stop ( cs_to_em_sti ( cs ) , USER_CLOCKSOURCE ) ;
}
static void em_sti_clocksource_resume ( struct clocksource * cs )
{
em_sti_clocksource_enable ( cs ) ;
}
static int em_sti_register_clocksource ( struct em_sti_priv * p )
{
struct clocksource * cs = & p - > cs ;
cs - > name = dev_name ( & p - > pdev - > dev ) ;
cs - > rating = 200 ;
cs - > read = em_sti_clocksource_read ;
cs - > enable = em_sti_clocksource_enable ;
cs - > disable = em_sti_clocksource_disable ;
cs - > suspend = em_sti_clocksource_disable ;
cs - > resume = em_sti_clocksource_resume ;
cs - > mask = CLOCKSOURCE_MASK ( 48 ) ;
cs - > flags = CLOCK_SOURCE_IS_CONTINUOUS ;
dev_info ( & p - > pdev - > dev , " used as clock source \n " ) ;
2017-02-07 00:12:02 +03:00
clocksource_register_hz ( cs , p - > rate ) ;
2012-05-25 11:03:44 +04:00
return 0 ;
}
static struct em_sti_priv * ced_to_em_sti ( struct clock_event_device * ced )
{
return container_of ( ced , struct em_sti_priv , ced ) ;
}
2015-06-12 11:00:17 +03:00
static int em_sti_clock_event_shutdown ( struct clock_event_device * ced )
2012-05-25 11:03:44 +04:00
{
struct em_sti_priv * p = ced_to_em_sti ( ced ) ;
2015-06-12 11:00:17 +03:00
em_sti_stop ( p , USER_CLOCKEVENT ) ;
return 0 ;
}
2012-05-25 11:03:44 +04:00
2015-06-12 11:00:17 +03:00
static int em_sti_clock_event_set_oneshot ( struct clock_event_device * ced )
{
struct em_sti_priv * p = ced_to_em_sti ( ced ) ;
2012-05-25 11:03:44 +04:00
2015-06-12 11:00:17 +03:00
dev_info ( & p - > pdev - > dev , " used for oneshot clock events \n " ) ;
em_sti_start ( p , USER_CLOCKEVENT ) ;
return 0 ;
2012-05-25 11:03:44 +04:00
}
static int em_sti_clock_event_next ( unsigned long delta ,
struct clock_event_device * ced )
{
struct em_sti_priv * p = ced_to_em_sti ( ced ) ;
2016-12-21 22:32:01 +03:00
u64 next ;
2012-05-25 11:03:44 +04:00
int safe ;
next = em_sti_set_next ( p , em_sti_count ( p ) + delta ) ;
safe = em_sti_count ( p ) < ( next - 1 ) ;
return ! safe ;
}
static void em_sti_register_clockevent ( struct em_sti_priv * p )
{
struct clock_event_device * ced = & p - > ced ;
ced - > name = dev_name ( & p - > pdev - > dev ) ;
ced - > features = CLOCK_EVT_FEAT_ONESHOT ;
ced - > rating = 200 ;
2013-09-19 00:01:16 +04:00
ced - > cpumask = cpu_possible_mask ;
2012-05-25 11:03:44 +04:00
ced - > set_next_event = em_sti_clock_event_next ;
2015-06-12 11:00:17 +03:00
ced - > set_state_shutdown = em_sti_clock_event_shutdown ;
ced - > set_state_oneshot = em_sti_clock_event_set_oneshot ;
2012-05-25 11:03:44 +04:00
dev_info ( & p - > pdev - > dev , " used for clock events \n " ) ;
2017-02-07 00:12:02 +03:00
clockevents_config_and_register ( ced , p - > rate , 2 , 0xffffffff ) ;
2012-05-25 11:03:44 +04:00
}
2012-12-22 03:11:38 +04:00
static int em_sti_probe ( struct platform_device * pdev )
2012-05-25 11:03:44 +04:00
{
struct em_sti_priv * p ;
2019-12-21 20:30:25 +03:00
int irq , ret ;
2012-05-25 11:03:44 +04:00
2013-07-30 18:24:37 +04:00
p = devm_kzalloc ( & pdev - > dev , sizeof ( * p ) , GFP_KERNEL ) ;
2014-05-22 16:05:06 +04:00
if ( p = = NULL )
2013-07-30 18:24:37 +04:00
return - ENOMEM ;
2012-05-25 11:03:44 +04:00
p - > pdev = pdev ;
platform_set_drvdata ( pdev , p ) ;
irq = platform_get_irq ( pdev , 0 ) ;
2019-07-30 21:15:04 +03:00
if ( irq < 0 )
2017-06-30 09:14:45 +03:00
return irq ;
2012-05-25 11:03:44 +04:00
/* map memory, let base point to the STI instance */
2019-12-21 20:30:24 +03:00
p - > base = devm_platform_ioremap_resource ( pdev , 0 ) ;
2013-07-30 18:24:37 +04:00
if ( IS_ERR ( p - > base ) )
return PTR_ERR ( p - > base ) ;
2012-05-25 11:03:44 +04:00
2017-06-30 09:14:45 +03:00
ret = devm_request_irq ( & pdev - > dev , irq , em_sti_interrupt ,
IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING ,
dev_name ( & pdev - > dev ) , p ) ;
if ( ret ) {
2017-02-07 00:12:01 +03:00
dev_err ( & pdev - > dev , " failed to request low IRQ \n " ) ;
2017-06-30 09:14:45 +03:00
return ret ;
2017-02-07 00:12:01 +03:00
}
2012-05-25 11:03:44 +04:00
/* get hold of clock */
2013-07-30 18:24:37 +04:00
p - > clk = devm_clk_get ( & pdev - > dev , " sclk " ) ;
2012-05-25 11:03:44 +04:00
if ( IS_ERR ( p - > clk ) ) {
dev_err ( & pdev - > dev , " cannot get clock \n " ) ;
2013-07-30 18:24:37 +04:00
return PTR_ERR ( p - > clk ) ;
2012-05-25 11:03:44 +04:00
}
2017-02-07 00:12:01 +03:00
ret = clk_prepare ( p - > clk ) ;
if ( ret < 0 ) {
dev_err ( & pdev - > dev , " cannot prepare clock \n " ) ;
return ret ;
2012-05-25 11:03:44 +04:00
}
2017-02-07 00:12:02 +03:00
ret = clk_enable ( p - > clk ) ;
if ( ret < 0 ) {
dev_err ( & p - > pdev - > dev , " cannot enable clock \n " ) ;
clk_unprepare ( p - > clk ) ;
return ret ;
}
p - > rate = clk_get_rate ( p - > clk ) ;
clk_disable ( p - > clk ) ;
2012-05-25 11:03:44 +04:00
raw_spin_lock_init ( & p - > lock ) ;
em_sti_register_clockevent ( p ) ;
em_sti_register_clocksource ( p ) ;
return 0 ;
}
2012-12-22 03:11:38 +04:00
static int em_sti_remove ( struct platform_device * pdev )
2012-05-25 11:03:44 +04:00
{
return - EBUSY ; /* cannot unregister clockevent and clocksource */
}
2012-12-22 03:11:38 +04:00
static const struct of_device_id em_sti_dt_ids [ ] = {
2012-05-09 18:39:50 +04:00
{ . compatible = " renesas,em-sti " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , em_sti_dt_ids ) ;
2012-05-25 11:03:44 +04:00
static struct platform_driver em_sti_device_driver = {
. probe = em_sti_probe ,
2012-12-22 03:11:38 +04:00
. remove = em_sti_remove ,
2012-05-25 11:03:44 +04:00
. driver = {
. name = " em_sti " ,
2012-05-09 18:39:50 +04:00
. of_match_table = em_sti_dt_ids ,
2012-05-25 11:03:44 +04:00
}
} ;
2013-03-05 10:40:42 +04:00
static int __init em_sti_init ( void )
{
return platform_driver_register ( & em_sti_device_driver ) ;
}
static void __exit em_sti_exit ( void )
{
platform_driver_unregister ( & em_sti_device_driver ) ;
}
subsys_initcall ( em_sti_init ) ;
module_exit ( em_sti_exit ) ;
2012-05-25 11:03:44 +04:00
MODULE_AUTHOR ( " Magnus Damm " ) ;
MODULE_DESCRIPTION ( " Renesas Emma Mobile STI Timer Driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;