2009-04-30 11:02:49 +04:00
/*
* SuperH Timer Support - MTU2
*
* Copyright ( C ) 2009 Magnus Damm
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License
*
* 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 .
*/
2014-03-04 17:11:47 +04:00
# include <linux/clk.h>
# include <linux/clockchips.h>
# include <linux/delay.h>
# include <linux/err.h>
2009-04-30 11:02:49 +04:00
# include <linux/init.h>
# include <linux/interrupt.h>
# include <linux/io.h>
2014-03-04 17:11:47 +04:00
# include <linux/ioport.h>
2009-04-30 11:02:49 +04:00
# include <linux/irq.h>
2011-07-03 21:36:22 +04:00
# include <linux/module.h>
2014-03-04 17:11:47 +04:00
# include <linux/platform_device.h>
2012-03-14 01:40:14 +04:00
# include <linux/pm_domain.h>
2012-08-13 16:00:16 +04:00
# include <linux/pm_runtime.h>
2014-03-04 17:11:47 +04:00
# include <linux/sh_timer.h>
# include <linux/slab.h>
# include <linux/spinlock.h>
2009-04-30 11:02:49 +04:00
2014-03-04 16:04:48 +04:00
struct sh_mtu2_device ;
2014-03-04 15:58:30 +04:00
struct sh_mtu2_channel {
2014-03-04 16:04:48 +04:00
struct sh_mtu2_device * mtu ;
2014-03-04 17:17:26 +04:00
unsigned int index ;
2014-03-04 17:04:24 +04:00
void __iomem * base ;
2014-03-04 15:58:30 +04:00
struct clock_event_device ced ;
} ;
2014-03-04 16:04:48 +04:00
struct sh_mtu2_device {
2014-03-04 15:58:30 +04:00
struct platform_device * pdev ;
2009-04-30 11:02:49 +04:00
void __iomem * mapbase ;
struct clk * clk ;
2014-03-04 15:58:30 +04:00
2014-03-04 18:25:56 +04:00
raw_spinlock_t lock ; /* Protect the shared registers */
2014-03-04 17:23:00 +04:00
struct sh_mtu2_channel * channels ;
unsigned int num_channels ;
2014-03-04 21:05:45 +04:00
bool has_clockevent ;
2009-04-30 11:02:49 +04:00
} ;
# define TSTR -1 /* shared register */
# define TCR 0 /* channel register */
# define TMDR 1 /* channel register */
# define TIOR 2 /* channel register */
# define TIER 3 /* channel register */
# define TSR 4 /* channel register */
# define TCNT 5 /* channel register */
# define TGR 6 /* channel register */
2014-03-04 18:16:25 +04:00
# define TCR_CCLR_NONE (0 << 5)
# define TCR_CCLR_TGRA (1 << 5)
# define TCR_CCLR_TGRB (2 << 5)
# define TCR_CCLR_SYNC (3 << 5)
# define TCR_CCLR_TGRC (5 << 5)
# define TCR_CCLR_TGRD (6 << 5)
# define TCR_CCLR_MASK (7 << 5)
# define TCR_CKEG_RISING (0 << 3)
# define TCR_CKEG_FALLING (1 << 3)
# define TCR_CKEG_BOTH (2 << 3)
# define TCR_CKEG_MASK (3 << 3)
/* Values 4 to 7 are channel-dependent */
# define TCR_TPSC_P1 (0 << 0)
# define TCR_TPSC_P4 (1 << 0)
# define TCR_TPSC_P16 (2 << 0)
# define TCR_TPSC_P64 (3 << 0)
# define TCR_TPSC_CH0_TCLKA (4 << 0)
# define TCR_TPSC_CH0_TCLKB (5 << 0)
# define TCR_TPSC_CH0_TCLKC (6 << 0)
# define TCR_TPSC_CH0_TCLKD (7 << 0)
# define TCR_TPSC_CH1_TCLKA (4 << 0)
# define TCR_TPSC_CH1_TCLKB (5 << 0)
# define TCR_TPSC_CH1_P256 (6 << 0)
# define TCR_TPSC_CH1_TCNT2 (7 << 0)
# define TCR_TPSC_CH2_TCLKA (4 << 0)
# define TCR_TPSC_CH2_TCLKB (5 << 0)
# define TCR_TPSC_CH2_TCLKC (6 << 0)
# define TCR_TPSC_CH2_P1024 (7 << 0)
# define TCR_TPSC_CH34_P256 (4 << 0)
# define TCR_TPSC_CH34_P1024 (5 << 0)
# define TCR_TPSC_CH34_TCLKA (6 << 0)
# define TCR_TPSC_CH34_TCLKB (7 << 0)
# define TCR_TPSC_MASK (7 << 0)
# define TMDR_BFE (1 << 6)
# define TMDR_BFB (1 << 5)
# define TMDR_BFA (1 << 4)
# define TMDR_MD_NORMAL (0 << 0)
# define TMDR_MD_PWM_1 (2 << 0)
# define TMDR_MD_PWM_2 (3 << 0)
# define TMDR_MD_PHASE_1 (4 << 0)
# define TMDR_MD_PHASE_2 (5 << 0)
# define TMDR_MD_PHASE_3 (6 << 0)
# define TMDR_MD_PHASE_4 (7 << 0)
# define TMDR_MD_PWM_SYNC (8 << 0)
# define TMDR_MD_PWM_COMP_CREST (13 << 0)
# define TMDR_MD_PWM_COMP_TROUGH (14 << 0)
# define TMDR_MD_PWM_COMP_BOTH (15 << 0)
# define TMDR_MD_MASK (15 << 0)
# define TIOC_IOCH(n) ((n) << 4)
# define TIOC_IOCL(n) ((n) << 0)
# define TIOR_OC_RETAIN (0 << 0)
# define TIOR_OC_0_CLEAR (1 << 0)
# define TIOR_OC_0_SET (2 << 0)
# define TIOR_OC_0_TOGGLE (3 << 0)
# define TIOR_OC_1_CLEAR (5 << 0)
# define TIOR_OC_1_SET (6 << 0)
# define TIOR_OC_1_TOGGLE (7 << 0)
# define TIOR_IC_RISING (8 << 0)
# define TIOR_IC_FALLING (9 << 0)
# define TIOR_IC_BOTH (10 << 0)
# define TIOR_IC_TCNT (12 << 0)
# define TIOR_MASK (15 << 0)
# define TIER_TTGE (1 << 7)
# define TIER_TTGE2 (1 << 6)
# define TIER_TCIEU (1 << 5)
# define TIER_TCIEV (1 << 4)
# define TIER_TGIED (1 << 3)
# define TIER_TGIEC (1 << 2)
# define TIER_TGIEB (1 << 1)
# define TIER_TGIEA (1 << 0)
# define TSR_TCFD (1 << 7)
# define TSR_TCFU (1 << 5)
# define TSR_TCFV (1 << 4)
# define TSR_TGFD (1 << 3)
# define TSR_TGFC (1 << 2)
# define TSR_TGFB (1 << 1)
# define TSR_TGFA (1 << 0)
2009-04-30 11:02:49 +04:00
static unsigned long mtu2_reg_offs [ ] = {
[ TCR ] = 0 ,
[ TMDR ] = 1 ,
[ TIOR ] = 2 ,
[ TIER ] = 4 ,
[ TSR ] = 5 ,
[ TCNT ] = 6 ,
[ TGR ] = 8 ,
} ;
2014-03-04 15:58:30 +04:00
static inline unsigned long sh_mtu2_read ( struct sh_mtu2_channel * ch , int reg_nr )
2009-04-30 11:02:49 +04:00
{
unsigned long offs ;
2014-03-04 21:13:57 +04:00
if ( reg_nr = = TSTR )
return ioread8 ( ch - > mtu - > mapbase + 0x280 ) ;
2009-04-30 11:02:49 +04:00
offs = mtu2_reg_offs [ reg_nr ] ;
if ( ( reg_nr = = TCNT ) | | ( reg_nr = = TGR ) )
2014-03-04 17:04:24 +04:00
return ioread16 ( ch - > base + offs ) ;
2009-04-30 11:02:49 +04:00
else
2014-03-04 17:04:24 +04:00
return ioread8 ( ch - > base + offs ) ;
2009-04-30 11:02:49 +04:00
}
2014-03-04 15:58:30 +04:00
static inline void sh_mtu2_write ( struct sh_mtu2_channel * ch , int reg_nr ,
2009-04-30 11:02:49 +04:00
unsigned long value )
{
unsigned long offs ;
2014-03-04 21:13:57 +04:00
if ( reg_nr = = TSTR )
return iowrite8 ( value , ch - > mtu - > mapbase + 0x280 ) ;
2009-04-30 11:02:49 +04:00
offs = mtu2_reg_offs [ reg_nr ] ;
if ( ( reg_nr = = TCNT ) | | ( reg_nr = = TGR ) )
2014-03-04 17:04:24 +04:00
iowrite16 ( value , ch - > base + offs ) ;
2009-04-30 11:02:49 +04:00
else
2014-03-04 17:04:24 +04:00
iowrite8 ( value , ch - > base + offs ) ;
2009-04-30 11:02:49 +04:00
}
2014-03-04 15:58:30 +04:00
static void sh_mtu2_start_stop_ch ( struct sh_mtu2_channel * ch , int start )
2009-04-30 11:02:49 +04:00
{
unsigned long flags , value ;
/* start stop register shared by multiple timer channels */
2014-03-04 18:25:56 +04:00
raw_spin_lock_irqsave ( & ch - > mtu - > lock , flags ) ;
2014-03-04 15:58:30 +04:00
value = sh_mtu2_read ( ch , TSTR ) ;
2009-04-30 11:02:49 +04:00
if ( start )
2014-03-04 17:17:26 +04:00
value | = 1 < < ch - > index ;
2009-04-30 11:02:49 +04:00
else
2014-03-04 17:17:26 +04:00
value & = ~ ( 1 < < ch - > index ) ;
2009-04-30 11:02:49 +04:00
2014-03-04 15:58:30 +04:00
sh_mtu2_write ( ch , TSTR , value ) ;
2014-03-04 18:25:56 +04:00
raw_spin_unlock_irqrestore ( & ch - > mtu - > lock , flags ) ;
2009-04-30 11:02:49 +04:00
}
2014-03-04 15:58:30 +04:00
static int sh_mtu2_enable ( struct sh_mtu2_channel * ch )
2009-04-30 11:02:49 +04:00
{
2014-03-04 15:59:54 +04:00
unsigned long periodic ;
unsigned long rate ;
2009-04-30 11:02:49 +04:00
int ret ;
2014-03-04 15:58:30 +04:00
pm_runtime_get_sync ( & ch - > mtu - > pdev - > dev ) ;
dev_pm_syscore_device ( & ch - > mtu - > pdev - > dev , true ) ;
2012-08-13 16:00:16 +04:00
2009-04-30 11:02:49 +04:00
/* enable clock */
2014-03-04 15:58:30 +04:00
ret = clk_enable ( ch - > mtu - > clk ) ;
2009-04-30 11:02:49 +04:00
if ( ret ) {
2014-03-04 17:17:26 +04:00
dev_err ( & ch - > mtu - > pdev - > dev , " ch%u: cannot enable clock \n " ,
ch - > index ) ;
2009-04-30 11:02:49 +04:00
return ret ;
}
/* make sure channel is disabled */
2014-03-04 15:58:30 +04:00
sh_mtu2_start_stop_ch ( ch , 0 ) ;
2009-04-30 11:02:49 +04:00
2014-03-04 15:58:30 +04:00
rate = clk_get_rate ( ch - > mtu - > clk ) / 64 ;
2014-03-04 15:59:54 +04:00
periodic = ( rate + HZ / 2 ) / HZ ;
2009-04-30 11:02:49 +04:00
2014-03-04 18:16:25 +04:00
/*
* " Periodic Counter Operation "
* Clear on TGRA compare match , divide clock by 64.
*/
sh_mtu2_write ( ch , TCR , TCR_CCLR_TGRA | TCR_TPSC_P64 ) ;
sh_mtu2_write ( ch , TIOR , TIOC_IOCH ( TIOR_OC_0_CLEAR ) |
TIOC_IOCL ( TIOR_OC_0_CLEAR ) ) ;
2014-03-04 15:58:30 +04:00
sh_mtu2_write ( ch , TGR , periodic ) ;
sh_mtu2_write ( ch , TCNT , 0 ) ;
2014-03-04 18:16:25 +04:00
sh_mtu2_write ( ch , TMDR , TMDR_MD_NORMAL ) ;
sh_mtu2_write ( ch , TIER , TIER_TGIEA ) ;
2009-04-30 11:02:49 +04:00
/* enable channel */
2014-03-04 15:58:30 +04:00
sh_mtu2_start_stop_ch ( ch , 1 ) ;
2009-04-30 11:02:49 +04:00
return 0 ;
}
2014-03-04 15:58:30 +04:00
static void sh_mtu2_disable ( struct sh_mtu2_channel * ch )
2009-04-30 11:02:49 +04:00
{
/* disable channel */
2014-03-04 15:58:30 +04:00
sh_mtu2_start_stop_ch ( ch , 0 ) ;
2009-04-30 11:02:49 +04:00
/* stop clock */
2014-03-04 15:58:30 +04:00
clk_disable ( ch - > mtu - > clk ) ;
2012-08-13 16:00:16 +04:00
2014-03-04 15:58:30 +04:00
dev_pm_syscore_device ( & ch - > mtu - > pdev - > dev , false ) ;
pm_runtime_put ( & ch - > mtu - > pdev - > dev ) ;
2009-04-30 11:02:49 +04:00
}
static irqreturn_t sh_mtu2_interrupt ( int irq , void * dev_id )
{
2014-03-04 15:58:30 +04:00
struct sh_mtu2_channel * ch = dev_id ;
2009-04-30 11:02:49 +04:00
/* acknowledge interrupt */
2014-03-04 15:58:30 +04:00
sh_mtu2_read ( ch , TSR ) ;
2014-03-04 18:16:25 +04:00
sh_mtu2_write ( ch , TSR , ~ TSR_TGFA ) ;
2009-04-30 11:02:49 +04:00
/* notify clockevent layer */
2014-03-04 15:58:30 +04:00
ch - > ced . event_handler ( & ch - > ced ) ;
2009-04-30 11:02:49 +04:00
return IRQ_HANDLED ;
}
2014-03-04 15:58:30 +04:00
static struct sh_mtu2_channel * ced_to_sh_mtu2 ( struct clock_event_device * ced )
2009-04-30 11:02:49 +04:00
{
2014-03-04 15:58:30 +04:00
return container_of ( ced , struct sh_mtu2_channel , ced ) ;
2009-04-30 11:02:49 +04:00
}
static void sh_mtu2_clock_event_mode ( enum clock_event_mode mode ,
struct clock_event_device * ced )
{
2014-03-04 15:58:30 +04:00
struct sh_mtu2_channel * ch = ced_to_sh_mtu2 ( ced ) ;
2009-04-30 11:02:49 +04:00
int disabled = 0 ;
/* deal with old setting first */
switch ( ced - > mode ) {
case CLOCK_EVT_MODE_PERIODIC :
2014-03-04 15:58:30 +04:00
sh_mtu2_disable ( ch ) ;
2009-04-30 11:02:49 +04:00
disabled = 1 ;
break ;
default :
break ;
}
switch ( mode ) {
case CLOCK_EVT_MODE_PERIODIC :
2014-03-04 15:58:30 +04:00
dev_info ( & ch - > mtu - > pdev - > dev ,
2014-03-04 17:17:26 +04:00
" ch%u: used for periodic clock events \n " , ch - > index ) ;
2014-03-04 15:58:30 +04:00
sh_mtu2_enable ( ch ) ;
2009-04-30 11:02:49 +04:00
break ;
case CLOCK_EVT_MODE_UNUSED :
if ( ! disabled )
2014-03-04 15:58:30 +04:00
sh_mtu2_disable ( ch ) ;
2009-04-30 11:02:49 +04:00
break ;
case CLOCK_EVT_MODE_SHUTDOWN :
default :
break ;
}
}
2012-08-06 03:43:41 +04:00
static void sh_mtu2_clock_event_suspend ( struct clock_event_device * ced )
{
2014-03-04 15:58:30 +04:00
pm_genpd_syscore_poweroff ( & ced_to_sh_mtu2 ( ced ) - > mtu - > pdev - > dev ) ;
2012-08-06 03:43:41 +04:00
}
static void sh_mtu2_clock_event_resume ( struct clock_event_device * ced )
{
2014-03-04 15:58:30 +04:00
pm_genpd_syscore_poweron ( & ced_to_sh_mtu2 ( ced ) - > mtu - > pdev - > dev ) ;
2012-08-06 03:43:41 +04:00
}
2014-03-04 15:58:30 +04:00
static void sh_mtu2_register_clockevent ( struct sh_mtu2_channel * ch ,
2014-03-04 18:19:41 +04:00
const char * name )
2009-04-30 11:02:49 +04:00
{
2014-03-04 15:58:30 +04:00
struct clock_event_device * ced = & ch - > ced ;
2009-04-30 11:02:49 +04:00
ced - > name = name ;
ced - > features = CLOCK_EVT_FEAT_PERIODIC ;
2014-03-04 18:19:41 +04:00
ced - > rating = 200 ;
2014-03-04 18:22:19 +04:00
ced - > cpumask = cpu_possible_mask ;
2009-04-30 11:02:49 +04:00
ced - > set_mode = sh_mtu2_clock_event_mode ;
2012-08-06 03:43:41 +04:00
ced - > suspend = sh_mtu2_clock_event_suspend ;
ced - > resume = sh_mtu2_clock_event_resume ;
2009-04-30 11:02:49 +04:00
2014-03-04 17:17:26 +04:00
dev_info ( & ch - > mtu - > pdev - > dev , " ch%u: used for clock events \n " ,
ch - > index ) ;
2010-02-25 10:37:46 +03:00
clockevents_register_device ( ced ) ;
2009-04-30 11:02:49 +04:00
}
2014-03-04 21:13:57 +04:00
static int sh_mtu2_register ( struct sh_mtu2_channel * ch , const char * name )
2009-04-30 11:02:49 +04:00
{
2014-03-04 21:13:57 +04:00
ch - > mtu - > has_clockevent = true ;
sh_mtu2_register_clockevent ( ch , name ) ;
2009-04-30 11:02:49 +04:00
return 0 ;
}
2014-03-04 21:05:45 +04:00
static int sh_mtu2_setup_channel ( struct sh_mtu2_channel * ch , unsigned int index ,
2014-03-04 16:11:23 +04:00
struct sh_mtu2_device * mtu )
{
2014-03-04 21:05:45 +04:00
static const unsigned int channel_offsets [ ] = {
0x300 , 0x380 , 0x000 ,
} ;
2014-03-04 21:13:57 +04:00
char name [ 6 ] ;
int irq ;
int ret ;
2014-03-04 16:11:23 +04:00
ch - > mtu = mtu ;
2014-03-04 21:13:57 +04:00
sprintf ( name , " tgi%ua " , index ) ;
irq = platform_get_irq_byname ( mtu - > pdev , name ) ;
if ( irq < 0 ) {
2014-03-04 21:05:45 +04:00
/* Skip channels with no declared interrupt. */
2014-03-04 21:13:57 +04:00
return 0 ;
}
2014-03-04 21:05:45 +04:00
2014-03-04 21:13:57 +04:00
ret = request_irq ( irq , sh_mtu2_interrupt ,
IRQF_TIMER | IRQF_IRQPOLL | IRQF_NOBALANCING ,
dev_name ( & ch - > mtu - > pdev - > dev ) , ch ) ;
if ( ret ) {
dev_err ( & ch - > mtu - > pdev - > dev , " ch%u: failed to request irq %d \n " ,
index , irq ) ;
return ret ;
2014-03-04 16:11:23 +04:00
}
2014-03-04 21:13:57 +04:00
ch - > base = mtu - > mapbase + channel_offsets [ index ] ;
ch - > index = index ;
return sh_mtu2_register ( ch , dev_name ( & mtu - > pdev - > dev ) ) ;
2014-03-04 16:11:23 +04:00
}
2014-03-04 21:05:45 +04:00
static int sh_mtu2_map_memory ( struct sh_mtu2_device * mtu )
2009-04-30 11:02:49 +04:00
{
struct resource * res ;
2014-03-04 16:04:48 +04:00
res = platform_get_resource ( mtu - > pdev , IORESOURCE_MEM , 0 ) ;
2009-04-30 11:02:49 +04:00
if ( ! res ) {
2014-03-04 16:04:48 +04:00
dev_err ( & mtu - > pdev - > dev , " failed to get I/O memory \n " ) ;
2014-03-04 21:05:45 +04:00
return - ENXIO ;
2009-04-30 11:02:49 +04:00
}
2014-03-04 21:05:45 +04:00
mtu - > mapbase = ioremap_nocache ( res - > start , resource_size ( res ) ) ;
if ( mtu - > mapbase = = NULL )
return - ENXIO ;
return 0 ;
}
static int sh_mtu2_setup ( struct sh_mtu2_device * mtu ,
struct platform_device * pdev )
{
unsigned int i ;
int ret ;
mtu - > pdev = pdev ;
2014-03-04 17:04:24 +04:00
2014-03-04 18:25:56 +04:00
raw_spin_lock_init ( & mtu - > lock ) ;
2014-03-04 21:05:45 +04:00
/* Get hold of clock. */
2014-03-04 21:13:57 +04:00
mtu - > clk = clk_get ( & mtu - > pdev - > dev , " fck " ) ;
2014-03-04 16:04:48 +04:00
if ( IS_ERR ( mtu - > clk ) ) {
dev_err ( & mtu - > pdev - > dev , " cannot get clock \n " ) ;
2014-03-04 21:05:45 +04:00
return PTR_ERR ( mtu - > clk ) ;
2009-04-30 11:02:49 +04:00
}
2014-03-04 16:04:48 +04:00
ret = clk_prepare ( mtu - > clk ) ;
2013-11-08 14:07:59 +04:00
if ( ret < 0 )
2014-03-04 21:05:45 +04:00
goto err_clk_put ;
2013-11-08 14:07:59 +04:00
2014-03-04 21:05:45 +04:00
/* Map the memory resource. */
ret = sh_mtu2_map_memory ( mtu ) ;
if ( ret < 0 ) {
dev_err ( & mtu - > pdev - > dev , " failed to remap I/O memory \n " ) ;
goto err_clk_unprepare ;
}
/* Allocate and setup the channels. */
2014-03-04 21:13:57 +04:00
mtu - > num_channels = 3 ;
2014-03-04 21:05:45 +04:00
mtu - > channels = kzalloc ( sizeof ( * mtu - > channels ) * mtu - > num_channels ,
GFP_KERNEL ) ;
2014-03-04 17:23:00 +04:00
if ( mtu - > channels = = NULL ) {
ret = - ENOMEM ;
2014-03-04 21:05:45 +04:00
goto err_unmap ;
2014-03-04 17:23:00 +04:00
}
2014-03-04 21:13:57 +04:00
for ( i = 0 ; i < mtu - > num_channels ; + + i ) {
ret = sh_mtu2_setup_channel ( & mtu - > channels [ i ] , i , mtu ) ;
2014-03-04 21:05:45 +04:00
if ( ret < 0 )
goto err_unmap ;
}
2014-03-04 17:23:00 +04:00
2014-03-04 21:05:45 +04:00
platform_set_drvdata ( pdev , mtu ) ;
2013-11-08 14:07:59 +04:00
return 0 ;
2014-03-04 21:05:45 +04:00
err_unmap :
2014-03-04 17:23:00 +04:00
kfree ( mtu - > channels ) ;
2014-03-04 21:13:57 +04:00
iounmap ( mtu - > mapbase ) ;
2014-03-04 21:05:45 +04:00
err_clk_unprepare :
2014-03-04 16:04:48 +04:00
clk_unprepare ( mtu - > clk ) ;
2014-03-04 21:05:45 +04:00
err_clk_put :
2014-03-04 16:04:48 +04:00
clk_put ( mtu - > clk ) ;
2009-04-30 11:02:49 +04:00
return ret ;
}
2012-12-22 03:11:38 +04:00
static int sh_mtu2_probe ( struct platform_device * pdev )
2009-04-30 11:02:49 +04:00
{
2014-03-04 16:04:48 +04:00
struct sh_mtu2_device * mtu = platform_get_drvdata ( pdev ) ;
2009-04-30 11:02:49 +04:00
int ret ;
2012-03-14 01:40:14 +04:00
2012-08-06 03:43:41 +04:00
if ( ! is_early_platform_device ( pdev ) ) {
2012-08-13 16:00:16 +04:00
pm_runtime_set_active ( & pdev - > dev ) ;
pm_runtime_enable ( & pdev - > dev ) ;
2012-08-06 03:43:41 +04:00
}
2009-04-30 11:02:49 +04:00
2014-03-04 16:04:48 +04:00
if ( mtu ) {
2010-03-10 10:26:25 +03:00
dev_info ( & pdev - > dev , " kept as earlytimer \n " ) ;
2012-08-13 16:00:16 +04:00
goto out ;
2009-04-30 11:02:49 +04:00
}
2014-03-04 17:10:55 +04:00
mtu = kzalloc ( sizeof ( * mtu ) , GFP_KERNEL ) ;
2014-05-22 16:05:07 +04:00
if ( mtu = = NULL )
2009-04-30 11:02:49 +04:00
return - ENOMEM ;
2014-03-04 16:04:48 +04:00
ret = sh_mtu2_setup ( mtu , pdev ) ;
2009-04-30 11:02:49 +04:00
if ( ret ) {
2014-03-04 16:04:48 +04:00
kfree ( mtu ) ;
2012-08-13 16:00:16 +04:00
pm_runtime_idle ( & pdev - > dev ) ;
return ret ;
2009-04-30 11:02:49 +04:00
}
2012-08-13 16:00:16 +04:00
if ( is_early_platform_device ( pdev ) )
return 0 ;
out :
2014-03-04 21:05:45 +04:00
if ( mtu - > has_clockevent )
2012-08-13 16:00:16 +04:00
pm_runtime_irq_safe ( & pdev - > dev ) ;
else
pm_runtime_idle ( & pdev - > dev ) ;
return 0 ;
2009-04-30 11:02:49 +04:00
}
2012-12-22 03:11:38 +04:00
static int sh_mtu2_remove ( struct platform_device * pdev )
2009-04-30 11:02:49 +04:00
{
return - EBUSY ; /* cannot unregister clockevent */
}
2014-03-04 21:05:45 +04:00
static const struct platform_device_id sh_mtu2_id_table [ ] = {
{ " sh-mtu2 " , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( platform , sh_mtu2_id_table ) ;
2009-04-30 11:02:49 +04:00
static struct platform_driver sh_mtu2_device_driver = {
. probe = sh_mtu2_probe ,
2012-12-22 03:11:38 +04:00
. remove = sh_mtu2_remove ,
2009-04-30 11:02:49 +04:00
. driver = {
. name = " sh_mtu2 " ,
2014-03-04 21:05:45 +04:00
} ,
. id_table = sh_mtu2_id_table ,
2009-04-30 11:02:49 +04:00
} ;
static int __init sh_mtu2_init ( void )
{
return platform_driver_register ( & sh_mtu2_device_driver ) ;
}
static void __exit sh_mtu2_exit ( void )
{
platform_driver_unregister ( & sh_mtu2_device_driver ) ;
}
early_platform_init ( " earlytimer " , & sh_mtu2_device_driver ) ;
2013-03-05 10:40:42 +04:00
subsys_initcall ( sh_mtu2_init ) ;
2009-04-30 11:02:49 +04:00
module_exit ( sh_mtu2_exit ) ;
MODULE_AUTHOR ( " Magnus Damm " ) ;
MODULE_DESCRIPTION ( " SuperH MTU2 Timer Driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;