@ -35,6 +35,12 @@
# include <linux/pm_domain.h>
# include <linux/pm_runtime.h>
enum sh_tmu_model {
SH_TMU_LEGACY ,
SH_TMU ,
SH_TMU_SH3 ,
} ;
struct sh_tmu_device ;
struct sh_tmu_channel {
@ -58,8 +64,13 @@ struct sh_tmu_device {
void __iomem * mapbase ;
struct clk * clk ;
enum sh_tmu_model model ;
struct sh_tmu_channel * channels ;
unsigned int num_channels ;
bool has_clockevent ;
bool has_clocksource ;
} ;
static DEFINE_RAW_SPINLOCK ( sh_tmu_lock ) ;
@ -82,8 +93,16 @@ static inline unsigned long sh_tmu_read(struct sh_tmu_channel *ch, int reg_nr)
{
unsigned long offs ;
if ( reg_nr = = TSTR )
if ( reg_nr = = TSTR ) {
switch ( ch - > tmu - > model ) {
case SH_TMU_LEGACY :
return ioread8 ( ch - > tmu - > mapbase ) ;
case SH_TMU_SH3 :
return ioread8 ( ch - > tmu - > mapbase + 2 ) ;
case SH_TMU :
return ioread8 ( ch - > tmu - > mapbase + 4 ) ;
}
}
offs = reg_nr < < 2 ;
@ -99,8 +118,14 @@ static inline void sh_tmu_write(struct sh_tmu_channel *ch, int reg_nr,
unsigned long offs ;
if ( reg_nr = = TSTR ) {
iowrite8 ( value , ch - > tmu - > mapbase ) ;
return ;
switch ( ch - > tmu - > model ) {
case SH_TMU_LEGACY :
return iowrite8 ( value , ch - > tmu - > mapbase ) ;
case SH_TMU_SH3 :
return iowrite8 ( value , ch - > tmu - > mapbase + 2 ) ;
case SH_TMU :
return iowrite8 ( value , ch - > tmu - > mapbase + 4 ) ;
}
}
offs = reg_nr < < 2 ;
@ -435,31 +460,49 @@ static void sh_tmu_register_clockevent(struct sh_tmu_channel *ch,
static int sh_tmu_register ( struct sh_tmu_channel * ch , const char * name ,
bool clockevent , bool clocksource )
{
if ( clockevent )
if ( clockevent ) {
ch - > tmu - > has_clockevent = true ;
sh_tmu_register_clockevent ( ch , name ) ;
else if ( clocksource )
} else if ( clocksource ) {
ch - > tmu - > has_clocksource = true ;
sh_tmu_register_clocksource ( ch , name ) ;
}
return 0 ;
}
static int sh_tmu_channel_setup ( struct sh_tmu_channel * ch ,
static int sh_tmu_channel_setup ( struct sh_tmu_channel * ch , unsigned int index ,
bool clockevent , bool clocksource ,
struct sh_tmu_device * tmu )
{
struct sh_timer_config * cfg = tmu - > pdev - > dev . platform_data ;
/* Skip unused channels. */
if ( ! clockevent & & ! clocksource )
return 0 ;
ch - > tmu = tmu ;
if ( tmu - > model = = SH_TMU_LEGACY ) {
struct sh_timer_config * cfg = tmu - > pdev - > dev . platform_data ;
/*
* The SH3 variant ( SH770x , SH7705 , SH7710 and SH7720 ) maps channel
* registers blocks at base + 2 + 12 * index , while all other variants
* map them at base + 4 + 12 * index . We can compute the index by just
* dividing by 12 , the 2 bytes or 4 bytes offset being hidden by the
* integer division .
* The SH3 variant ( SH770x , SH7705 , SH7710 and SH7720 ) maps
* channel registers blocks at base + 2 + 12 * index , while all
* other variants map them at base + 4 + 12 * index . We can
* compute the index by just dividing by 12 , the 2 bytes or 4
* bytes offset being hidden by the integer division .
*/
ch - > index = cfg - > channel_offset / 12 ;
ch - > base = tmu - > mapbase + cfg - > channel_offset ;
} else {
ch - > index = index ;
ch - > irq = platform_get_irq ( tmu - > pdev , 0 ) ;
if ( tmu - > model = = SH_TMU_SH3 )
ch - > base = tmu - > mapbase + 4 + ch - > index * 12 ;
else
ch - > base = tmu - > mapbase + 8 + ch - > index * 12 ;
}
ch - > irq = platform_get_irq ( tmu - > pdev , ch - > index ) ;
if ( ch - > irq < 0 ) {
dev_err ( & tmu - > pdev - > dev , " ch%u: failed to get irq \n " ,
ch - > index ) ;
@ -470,88 +513,127 @@ static int sh_tmu_channel_setup(struct sh_tmu_channel *ch,
ch - > enable_count = 0 ;
return sh_tmu_register ( ch , dev_name ( & tmu - > pdev - > dev ) ,
cfg - > clockevent_rating ! = 0 ,
cfg - > clocksource_rating ! = 0 ) ;
clockevent , clocksource ) ;
}
static int sh_tmu_setup ( struct sh_tmu_device * tmu , struct platform_device * pdev )
static int sh_tmu_map_memory ( struct sh_tmu_device * tmu )
{
struct sh_timer_config * cfg = pdev - > dev . platform_data ;
struct resource * res ;
void __iomem * base ;
int ret ;
ret = - ENXIO ;
tmu - > pdev = pdev ;
if ( ! cfg ) {
dev_err ( & tmu - > pdev - > dev , " missing platform data \n " ) ;
goto err0 ;
}
platform_set_drvdata ( pdev , tmu ) ;
res = platform_get_resource ( tmu - > pdev , IORESOURCE_MEM , 0 ) ;
if ( ! res ) {
dev_err ( & tmu - > pdev - > dev , " failed to get I/O memory \n " ) ;
goto err0 ;
return - ENXIO ;
}
tmu - > mapbase = ioremap_nocache ( res - > start , resource_size ( res ) ) ;
if ( tmu - > mapbase = = NULL )
return - ENXIO ;
/*
* Map memory , let base point to our channel and mapbase to the
* start / stop shared register .
* In legacy platform device configuration ( with one device per channel )
* the resource points to the channel base address .
*/
base = ioremap_nocache ( res - > start , resource_size ( res ) ) ;
if ( base = = NULL ) {
dev_err ( & tmu - > pdev - > dev , " failed to remap I/O memory \n " ) ;
goto err0 ;
if ( tmu - > model = = SH_TMU_LEGACY ) {
struct sh_timer_config * cfg = tmu - > pdev - > dev . platform_data ;
tmu - > mapbase - = cfg - > channel_offset ;
}
tmu - > mapbase = base - cfg - > channel_offset ;
return 0 ;
}
/* get hold of clock */
static void sh_tmu_unmap_memory ( struct sh_tmu_device * tmu )
{
if ( tmu - > model = = SH_TMU_LEGACY ) {
struct sh_timer_config * cfg = tmu - > pdev - > dev . platform_data ;
tmu - > mapbase + = cfg - > channel_offset ;
}
iounmap ( tmu - > mapbase ) ;
}
static int sh_tmu_setup ( struct sh_tmu_device * tmu , struct platform_device * pdev )
{
struct sh_timer_config * cfg = pdev - > dev . platform_data ;
const struct platform_device_id * id = pdev - > id_entry ;
unsigned int i ;
int ret ;
if ( ! cfg ) {
dev_err ( & tmu - > pdev - > dev , " missing platform data \n " ) ;
return - ENXIO ;
}
tmu - > pdev = pdev ;
tmu - > model = id - > driver_data ;
/* Get hold of clock. */
tmu - > clk = clk_get ( & tmu - > pdev - > dev , " tmu_fck " ) ;
if ( IS_ERR ( tmu - > clk ) ) {
dev_err ( & tmu - > pdev - > dev , " cannot get clock \n " ) ;
ret = PTR_ERR ( tmu - > clk ) ;
goto err1 ;
return PTR_ERR ( tmu - > clk ) ;
}
ret = clk_prepare ( tmu - > clk ) ;
if ( ret < 0 )
goto err2 ;
goto err_clk_put ;
tmu - > channels = kzalloc ( sizeof ( * tmu - > channels ) , GFP_KERNEL ) ;
if ( tmu - > channels = = NULL ) {
ret = - ENOMEM ;
goto err3 ;
/* Map the memory resource. */
ret = sh_tmu_map_memory ( tmu ) ;
if ( ret < 0 ) {
dev_err ( & tmu - > pdev - > dev , " failed to remap I/O memory \n " ) ;
goto err_clk_unprepare ;
}
/* Allocate and setup the channels. */
if ( tmu - > model = = SH_TMU_LEGACY )
tmu - > num_channels = 1 ;
else
tmu - > num_channels = hweight8 ( cfg - > channels_mask ) ;
tmu - > channels [ 0 ] . base = base ;
tmu - > channels = kzalloc ( sizeof ( * tmu - > channels ) * tmu - > num_channels ,
GFP_KERNEL ) ;
if ( tmu - > channels = = NULL ) {
ret = - ENOMEM ;
goto err_unmap ;
}
ret = sh_tmu_channel_setup ( & tmu - > channels [ 0 ] , tmu ) ;
if ( tmu - > model = = SH_TMU_LEGACY ) {
ret = sh_tmu_channel_setup ( & tmu - > channels [ 0 ] , 0 ,
cfg - > clockevent_rating ! = 0 ,
cfg - > clocksource_rating ! = 0 , tmu ) ;
if ( ret < 0 )
goto err_unmap ;
} else {
/*
* Use the first channel as a clock event device and the second
* channel as a clock source .
*/
for ( i = 0 ; i < tmu - > num_channels ; + + i ) {
ret = sh_tmu_channel_setup ( & tmu - > channels [ i ] , i ,
i = = 0 , i = = 1 , tmu ) ;
if ( ret < 0 )
goto err3 ;
goto err_unmap ;
}
}
platform_set_drvdata ( pdev , tmu ) ;
return 0 ;
err3 :
err_unmap :
kfree ( tmu - > channels ) ;
sh_tmu_unmap_memory ( tmu ) ;
err_clk_unprepare :
clk_unprepare ( tmu - > clk ) ;
err2 :
err_clk_put :
clk_put ( tmu - > clk ) ;
err1 :
iounmap ( base ) ;
err0 :
return ret ;
}
static int sh_tmu_probe ( struct platform_device * pdev )
{
struct sh_tmu_device * tmu = platform_get_drvdata ( pdev ) ;
struct sh_timer_config * cfg = pdev - > dev . platform_data ;
int ret ;
if ( ! is_early_platform_device ( pdev ) ) {
@ -580,7 +662,7 @@ static int sh_tmu_probe(struct platform_device *pdev)
return 0 ;
out :
if ( cfg - > clockevent_rating | | cfg - > clocksource_rating )
if ( tmu - > has_ clockevent | | tmu - > has_ clocksource)
pm_runtime_irq_safe ( & pdev - > dev ) ;
else
pm_runtime_idle ( & pdev - > dev ) ;
@ -593,12 +675,21 @@ static int sh_tmu_remove(struct platform_device *pdev)
return - EBUSY ; /* cannot unregister clockevent and clocksource */
}
static const struct platform_device_id sh_tmu_id_table [ ] = {
{ " sh_tmu " , SH_TMU_LEGACY } ,
{ " sh-tmu " , SH_TMU } ,
{ " sh-tmu-sh3 " , SH_TMU_SH3 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( platform , sh_tmu_id_table ) ;
static struct platform_driver sh_tmu_device_driver = {
. probe = sh_tmu_probe ,
. remove = sh_tmu_remove ,
. driver = {
. name = " sh_tmu " ,
}
} ,
. id_table = sh_tmu_id_table ,
} ;
static int __init sh_tmu_init ( void )