2005-09-07 17:20:26 +01:00
/*
* linux / arch / arm / plat - omap / dmtimer . c
*
* OMAP Dual - Mode Timers
*
* Copyright ( C ) 2005 Nokia Corporation
2006-06-26 16:16:12 -07:00
* OMAP2 support by Juha Yrjola
* API improvements and OMAP2 clock framework support by Timo Teras
2005-09-07 17:20:26 +01:00
*
* 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 , or ( at your
* option ) any later version .
*
* THIS SOFTWARE IS PROVIDED ` ` AS IS ' ' AND ANY EXPRESS OR IMPLIED
* WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED . IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT , INDIRECT ,
* INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT
* ANY THEORY OF LIABILITY , WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT
* ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE .
*
* You should have received a copy of the GNU General Public License along
* with this program ; if not , write to the Free Software Foundation , Inc . ,
* 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
# include <linux/init.h>
2006-06-26 16:16:12 -07:00
# include <linux/spinlock.h>
# include <linux/errno.h>
# include <linux/list.h>
# include <linux/clk.h>
# include <linux/delay.h>
2008-08-04 10:41:28 +01:00
# include <asm/arch/hardware.h>
2005-09-07 17:20:26 +01:00
# include <asm/arch/dmtimer.h>
# include <asm/io.h>
# include <asm/arch/irqs.h>
2006-06-26 16:16:12 -07:00
/* register offsets */
2008-07-03 12:24:30 +03:00
# define _OMAP_TIMER_ID_OFFSET 0x00
# define _OMAP_TIMER_OCP_CFG_OFFSET 0x10
# define _OMAP_TIMER_SYS_STAT_OFFSET 0x14
# define _OMAP_TIMER_STAT_OFFSET 0x18
# define _OMAP_TIMER_INT_EN_OFFSET 0x1c
# define _OMAP_TIMER_WAKEUP_EN_OFFSET 0x20
# define _OMAP_TIMER_CTRL_OFFSET 0x24
# define OMAP_TIMER_CTRL_GPOCFG (1 << 14)
# define OMAP_TIMER_CTRL_CAPTMODE (1 << 13)
# define OMAP_TIMER_CTRL_PT (1 << 12)
# define OMAP_TIMER_CTRL_TCM_LOWTOHIGH (0x1 << 8)
# define OMAP_TIMER_CTRL_TCM_HIGHTOLOW (0x2 << 8)
# define OMAP_TIMER_CTRL_TCM_BOTHEDGES (0x3 << 8)
# define OMAP_TIMER_CTRL_SCPWM (1 << 7)
# define OMAP_TIMER_CTRL_CE (1 << 6) /* compare enable */
# define OMAP_TIMER_CTRL_PRE (1 << 5) /* prescaler enable */
# define OMAP_TIMER_CTRL_PTV_SHIFT 2 /* prescaler value shift */
# define OMAP_TIMER_CTRL_POSTED (1 << 2)
# define OMAP_TIMER_CTRL_AR (1 << 1) /* auto-reload enable */
# define OMAP_TIMER_CTRL_ST (1 << 0) /* start timer */
# define _OMAP_TIMER_COUNTER_OFFSET 0x28
# define _OMAP_TIMER_LOAD_OFFSET 0x2c
# define _OMAP_TIMER_TRIGGER_OFFSET 0x30
# define _OMAP_TIMER_WRITE_PEND_OFFSET 0x34
# define WP_NONE 0 /* no write pending bit */
# define WP_TCLR (1 << 0)
# define WP_TCRR (1 << 1)
# define WP_TLDR (1 << 2)
# define WP_TTGR (1 << 3)
# define WP_TMAR (1 << 4)
# define WP_TPIR (1 << 5)
# define WP_TNIR (1 << 6)
# define WP_TCVR (1 << 7)
# define WP_TOCR (1 << 8)
# define WP_TOWR (1 << 9)
# define _OMAP_TIMER_MATCH_OFFSET 0x38
# define _OMAP_TIMER_CAPTURE_OFFSET 0x3c
# define _OMAP_TIMER_IF_CTRL_OFFSET 0x40
# define _OMAP_TIMER_CAPTURE2_OFFSET 0x44 /* TCAR2, 34xx only */
# define _OMAP_TIMER_TICK_POS_OFFSET 0x48 /* TPIR, 34xx only */
# define _OMAP_TIMER_TICK_NEG_OFFSET 0x4c /* TNIR, 34xx only */
# define _OMAP_TIMER_TICK_COUNT_OFFSET 0x50 /* TCVR, 34xx only */
# define _OMAP_TIMER_TICK_INT_MASK_SET_OFFSET 0x54 /* TOCR, 34xx only */
# define _OMAP_TIMER_TICK_INT_MASK_COUNT_OFFSET 0x58 /* TOWR, 34xx only */
/* register offsets with the write pending bit encoded */
# define WPSHIFT 16
# define OMAP_TIMER_ID_REG (_OMAP_TIMER_ID_OFFSET \
| ( WP_NONE < < WPSHIFT ) )
# define OMAP_TIMER_OCP_CFG_REG (_OMAP_TIMER_OCP_CFG_OFFSET \
| ( WP_NONE < < WPSHIFT ) )
# define OMAP_TIMER_SYS_STAT_REG (_OMAP_TIMER_SYS_STAT_OFFSET \
| ( WP_NONE < < WPSHIFT ) )
# define OMAP_TIMER_STAT_REG (_OMAP_TIMER_STAT_OFFSET \
| ( WP_NONE < < WPSHIFT ) )
# define OMAP_TIMER_INT_EN_REG (_OMAP_TIMER_INT_EN_OFFSET \
| ( WP_NONE < < WPSHIFT ) )
# define OMAP_TIMER_WAKEUP_EN_REG (_OMAP_TIMER_WAKEUP_EN_OFFSET \
| ( WP_NONE < < WPSHIFT ) )
# define OMAP_TIMER_CTRL_REG (_OMAP_TIMER_CTRL_OFFSET \
| ( WP_TCLR < < WPSHIFT ) )
# define OMAP_TIMER_COUNTER_REG (_OMAP_TIMER_COUNTER_OFFSET \
| ( WP_TCRR < < WPSHIFT ) )
# define OMAP_TIMER_LOAD_REG (_OMAP_TIMER_LOAD_OFFSET \
| ( WP_TLDR < < WPSHIFT ) )
# define OMAP_TIMER_TRIGGER_REG (_OMAP_TIMER_TRIGGER_OFFSET \
| ( WP_TTGR < < WPSHIFT ) )
# define OMAP_TIMER_WRITE_PEND_REG (_OMAP_TIMER_WRITE_PEND_OFFSET \
| ( WP_NONE < < WPSHIFT ) )
# define OMAP_TIMER_MATCH_REG (_OMAP_TIMER_MATCH_OFFSET \
| ( WP_TMAR < < WPSHIFT ) )
# define OMAP_TIMER_CAPTURE_REG (_OMAP_TIMER_CAPTURE_OFFSET \
| ( WP_NONE < < WPSHIFT ) )
# define OMAP_TIMER_IF_CTRL_REG (_OMAP_TIMER_IF_CTRL_OFFSET \
| ( WP_NONE < < WPSHIFT ) )
# define OMAP_TIMER_CAPTURE2_REG (_OMAP_TIMER_CAPTURE2_OFFSET \
| ( WP_NONE < < WPSHIFT ) )
# define OMAP_TIMER_TICK_POS_REG (_OMAP_TIMER_TICK_POS_OFFSET \
| ( WP_TPIR < < WPSHIFT ) )
# define OMAP_TIMER_TICK_NEG_REG (_OMAP_TIMER_TICK_NEG_OFFSET \
| ( WP_TNIR < < WPSHIFT ) )
# define OMAP_TIMER_TICK_COUNT_REG (_OMAP_TIMER_TICK_COUNT_OFFSET \
| ( WP_TCVR < < WPSHIFT ) )
# define OMAP_TIMER_TICK_INT_MASK_SET_REG \
( _OMAP_TIMER_TICK_INT_MASK_SET_OFFSET | ( WP_TOCR < < WPSHIFT ) )
# define OMAP_TIMER_TICK_INT_MASK_COUNT_REG \
( _OMAP_TIMER_TICK_INT_MASK_COUNT_OFFSET | ( WP_TOWR < < WPSHIFT ) )
2006-06-26 16:16:12 -07:00
struct omap_dm_timer {
unsigned long phys_base ;
int irq ;
2007-06-25 22:55:39 -07:00
# if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
2006-06-26 16:16:12 -07:00
struct clk * iclk , * fclk ;
# endif
void __iomem * io_base ;
unsigned reserved : 1 ;
2006-09-25 12:41:42 +03:00
unsigned enabled : 1 ;
2008-07-03 12:24:30 +03:00
unsigned posted : 1 ;
2006-06-26 16:16:12 -07:00
} ;
# ifdef CONFIG_ARCH_OMAP1
2006-09-25 12:41:35 +03:00
# define omap_dm_clk_enable(x)
# define omap_dm_clk_disable(x)
2007-06-21 21:48:07 -07:00
# define omap2_dm_timers NULL
# define omap2_dm_source_names NULL
# define omap2_dm_source_clocks NULL
2007-06-25 22:55:39 -07:00
# define omap3_dm_timers NULL
# define omap3_dm_source_names NULL
# define omap3_dm_source_clocks NULL
2006-09-25 12:41:35 +03:00
2007-06-21 21:48:07 -07:00
static struct omap_dm_timer omap1_dm_timers [ ] = {
2006-06-26 16:16:12 -07:00
{ . phys_base = 0xfffb1400 , . irq = INT_1610_GPTIMER1 } ,
{ . phys_base = 0xfffb1c00 , . irq = INT_1610_GPTIMER2 } ,
{ . phys_base = 0xfffb2400 , . irq = INT_1610_GPTIMER3 } ,
{ . phys_base = 0xfffb2c00 , . irq = INT_1610_GPTIMER4 } ,
{ . phys_base = 0xfffb3400 , . irq = INT_1610_GPTIMER5 } ,
{ . phys_base = 0xfffb3c00 , . irq = INT_1610_GPTIMER6 } ,
2007-01-25 16:24:29 -08:00
{ . phys_base = 0xfffb7400 , . irq = INT_1610_GPTIMER7 } ,
{ . phys_base = 0xfffbd400 , . irq = INT_1610_GPTIMER8 } ,
2006-06-26 16:16:12 -07:00
} ;
2005-09-07 17:20:26 +01:00
2007-06-21 21:48:07 -07:00
static const int dm_timer_count = ARRAY_SIZE ( omap1_dm_timers ) ;
2006-06-26 16:16:12 -07:00
# elif defined(CONFIG_ARCH_OMAP2)
2005-09-07 17:20:26 +01:00
2007-06-21 21:48:07 -07:00
# define omap_dm_clk_enable(x) clk_enable(x)
# define omap_dm_clk_disable(x) clk_disable(x)
# define omap1_dm_timers NULL
2007-06-25 22:55:39 -07:00
# define omap3_dm_timers NULL
# define omap3_dm_source_names NULL
# define omap3_dm_source_clocks NULL
2006-09-25 12:41:35 +03:00
2007-06-21 21:48:07 -07:00
static struct omap_dm_timer omap2_dm_timers [ ] = {
2006-06-26 16:16:12 -07:00
{ . phys_base = 0x48028000 , . irq = INT_24XX_GPTIMER1 } ,
{ . phys_base = 0x4802a000 , . irq = INT_24XX_GPTIMER2 } ,
{ . phys_base = 0x48078000 , . irq = INT_24XX_GPTIMER3 } ,
{ . phys_base = 0x4807a000 , . irq = INT_24XX_GPTIMER4 } ,
{ . phys_base = 0x4807c000 , . irq = INT_24XX_GPTIMER5 } ,
{ . phys_base = 0x4807e000 , . irq = INT_24XX_GPTIMER6 } ,
{ . phys_base = 0x48080000 , . irq = INT_24XX_GPTIMER7 } ,
{ . phys_base = 0x48082000 , . irq = INT_24XX_GPTIMER8 } ,
{ . phys_base = 0x48084000 , . irq = INT_24XX_GPTIMER9 } ,
{ . phys_base = 0x48086000 , . irq = INT_24XX_GPTIMER10 } ,
{ . phys_base = 0x48088000 , . irq = INT_24XX_GPTIMER11 } ,
{ . phys_base = 0x4808a000 , . irq = INT_24XX_GPTIMER12 } ,
2005-09-07 17:20:26 +01:00
} ;
2007-06-21 21:48:07 -07:00
static const char * omap2_dm_source_names [ ] __initdata = {
2006-06-26 16:16:23 -07:00
" sys_ck " ,
" func_32k_ck " ,
2007-06-21 21:48:07 -07:00
" alt_ck " ,
NULL
2006-06-26 16:16:23 -07:00
} ;
2007-06-21 21:48:07 -07:00
static struct clk * * omap2_dm_source_clocks [ 3 ] ;
static const int dm_timer_count = ARRAY_SIZE ( omap2_dm_timers ) ;
2006-06-26 16:16:23 -07:00
2007-06-25 22:55:39 -07:00
# elif defined(CONFIG_ARCH_OMAP3)
# define omap_dm_clk_enable(x) clk_enable(x)
# define omap_dm_clk_disable(x) clk_disable(x)
# define omap1_dm_timers NULL
# define omap2_dm_timers NULL
# define omap2_dm_source_names NULL
# define omap2_dm_source_clocks NULL
static struct omap_dm_timer omap3_dm_timers [ ] = {
{ . phys_base = 0x48318000 , . irq = INT_24XX_GPTIMER1 } ,
{ . phys_base = 0x49032000 , . irq = INT_24XX_GPTIMER2 } ,
{ . phys_base = 0x49034000 , . irq = INT_24XX_GPTIMER3 } ,
{ . phys_base = 0x49036000 , . irq = INT_24XX_GPTIMER4 } ,
{ . phys_base = 0x49038000 , . irq = INT_24XX_GPTIMER5 } ,
{ . phys_base = 0x4903A000 , . irq = INT_24XX_GPTIMER6 } ,
{ . phys_base = 0x4903C000 , . irq = INT_24XX_GPTIMER7 } ,
{ . phys_base = 0x4903E000 , . irq = INT_24XX_GPTIMER8 } ,
{ . phys_base = 0x49040000 , . irq = INT_24XX_GPTIMER9 } ,
{ . phys_base = 0x48086000 , . irq = INT_24XX_GPTIMER10 } ,
{ . phys_base = 0x48088000 , . irq = INT_24XX_GPTIMER11 } ,
{ . phys_base = 0x48304000 , . irq = INT_24XX_GPTIMER12 } ,
} ;
static const char * omap3_dm_source_names [ ] __initdata = {
" sys_ck " ,
" omap_32k_fck " ,
NULL
} ;
static struct clk * * omap3_dm_source_clocks [ 2 ] ;
static const int dm_timer_count = ARRAY_SIZE ( omap3_dm_timers ) ;
2006-06-26 16:16:12 -07:00
# else
# error OMAP architecture not supported!
# endif
2007-06-21 21:48:07 -07:00
static struct omap_dm_timer * dm_timers ;
static char * * dm_source_names ;
static struct clk * * dm_source_clocks ;
2005-09-07 17:20:26 +01:00
static spinlock_t dm_timer_lock ;
2008-07-03 12:24:30 +03:00
/*
* Reads timer registers in posted and non - posted mode . The posted mode bit
* is encoded in reg . Note that in posted mode write pending bit must be
* checked . Otherwise a read of a non completed write will produce an error .
*/
static inline u32 omap_dm_timer_read_reg ( struct omap_dm_timer * timer , u32 reg )
2006-06-26 16:16:12 -07:00
{
2008-07-03 12:24:30 +03:00
if ( timer - > posted )
while ( readl ( timer - > io_base + ( OMAP_TIMER_WRITE_PEND_REG & 0xff ) )
& ( reg > > WPSHIFT ) )
cpu_relax ( ) ;
return readl ( timer - > io_base + ( reg & 0xff ) ) ;
2006-06-26 16:16:12 -07:00
}
2005-09-07 17:20:26 +01:00
2008-07-03 12:24:30 +03:00
/*
* Writes timer registers in posted and non - posted mode . The posted mode bit
* is encoded in reg . Note that in posted mode the write pending bit must be
* checked . Otherwise a write on a register which has a pending write will be
* lost .
*/
static void omap_dm_timer_write_reg ( struct omap_dm_timer * timer , u32 reg ,
u32 value )
2005-09-07 17:20:26 +01:00
{
2008-07-03 12:24:30 +03:00
if ( timer - > posted )
while ( readl ( timer - > io_base + ( OMAP_TIMER_WRITE_PEND_REG & 0xff ) )
& ( reg > > WPSHIFT ) )
cpu_relax ( ) ;
writel ( value , timer - > io_base + ( reg & 0xff ) ) ;
2005-09-07 17:20:26 +01:00
}
2006-06-26 16:16:12 -07:00
static void omap_dm_timer_wait_for_reset ( struct omap_dm_timer * timer )
2005-09-07 17:20:26 +01:00
{
2006-06-26 16:16:12 -07:00
int c ;
c = 0 ;
while ( ! ( omap_dm_timer_read_reg ( timer , OMAP_TIMER_SYS_STAT_REG ) & 1 ) ) {
c + + ;
if ( c > 100000 ) {
printk ( KERN_ERR " Timer failed to reset \n " ) ;
return ;
}
}
2005-09-07 17:20:26 +01:00
}
2006-06-26 16:16:12 -07:00
static void omap_dm_timer_reset ( struct omap_dm_timer * timer )
{
u32 l ;
2006-09-25 12:41:44 +03:00
if ( ! cpu_class_is_omap2 ( ) | | timer ! = & dm_timers [ 0 ] ) {
2006-06-26 16:16:13 -07:00
omap_dm_timer_write_reg ( timer , OMAP_TIMER_IF_CTRL_REG , 0x06 ) ;
omap_dm_timer_wait_for_reset ( timer ) ;
}
2006-09-25 12:41:42 +03:00
omap_dm_timer_set_source ( timer , OMAP_TIMER_SRC_32_KHZ ) ;
2006-06-26 16:16:12 -07:00
l = omap_dm_timer_read_reg ( timer , OMAP_TIMER_OCP_CFG_REG ) ;
2008-07-03 12:24:30 +03:00
l | = 0x02 < < 3 ; /* Set to smart-idle mode */
l | = 0x2 < < 8 ; /* Set clock activity to perserve f-clock on idle */
/*
* Enable wake - up only for GPT1 on OMAP2 CPUs .
* FIXME : All timers should have wake - up enabled and clear
* PRCM status .
*/
if ( cpu_class_is_omap2 ( ) & & ( timer = = & dm_timers [ 0 ] ) )
2006-09-25 12:41:44 +03:00
l | = 1 < < 2 ;
2006-06-26 16:16:12 -07:00
omap_dm_timer_write_reg ( timer , OMAP_TIMER_OCP_CFG_REG , l ) ;
2008-07-03 12:24:30 +03:00
/* Match hardware reset default of posted mode */
omap_dm_timer_write_reg ( timer , OMAP_TIMER_IF_CTRL_REG ,
OMAP_TIMER_CTRL_POSTED ) ;
timer - > posted = 1 ;
2006-06-26 16:16:12 -07:00
}
2006-06-26 16:16:23 -07:00
static void omap_dm_timer_prepare ( struct omap_dm_timer * timer )
2006-06-26 16:16:12 -07:00
{
2006-09-25 12:41:42 +03:00
omap_dm_timer_enable ( timer ) ;
2006-06-26 16:16:12 -07:00
omap_dm_timer_reset ( timer ) ;
}
struct omap_dm_timer * omap_dm_timer_request ( void )
{
struct omap_dm_timer * timer = NULL ;
unsigned long flags ;
int i ;
spin_lock_irqsave ( & dm_timer_lock , flags ) ;
for ( i = 0 ; i < dm_timer_count ; i + + ) {
if ( dm_timers [ i ] . reserved )
continue ;
timer = & dm_timers [ i ] ;
2006-06-26 16:16:23 -07:00
timer - > reserved = 1 ;
2006-06-26 16:16:12 -07:00
break ;
}
spin_unlock_irqrestore ( & dm_timer_lock , flags ) ;
2006-06-26 16:16:23 -07:00
if ( timer ! = NULL )
omap_dm_timer_prepare ( timer ) ;
2006-06-26 16:16:12 -07:00
return timer ;
}
struct omap_dm_timer * omap_dm_timer_request_specific ( int id )
2005-09-07 17:20:26 +01:00
{
struct omap_dm_timer * timer ;
2006-06-26 16:16:12 -07:00
unsigned long flags ;
2005-09-07 17:20:26 +01:00
2006-06-26 16:16:12 -07:00
spin_lock_irqsave ( & dm_timer_lock , flags ) ;
if ( id < = 0 | | id > dm_timer_count | | dm_timers [ id - 1 ] . reserved ) {
spin_unlock_irqrestore ( & dm_timer_lock , flags ) ;
printk ( " BUG: warning at %s:%d/%s(): unable to get timer %d \n " ,
2008-03-04 15:08:02 -08:00
__FILE__ , __LINE__ , __func__ , id ) ;
2006-06-26 16:16:12 -07:00
dump_stack ( ) ;
return NULL ;
}
2005-09-07 17:20:26 +01:00
2006-06-26 16:16:12 -07:00
timer = & dm_timers [ id - 1 ] ;
2006-06-26 16:16:23 -07:00
timer - > reserved = 1 ;
2006-06-26 16:16:12 -07:00
spin_unlock_irqrestore ( & dm_timer_lock , flags ) ;
2006-06-26 16:16:23 -07:00
omap_dm_timer_prepare ( timer ) ;
2006-06-26 16:16:12 -07:00
return timer ;
2005-09-07 17:20:26 +01:00
}
2006-06-26 16:16:12 -07:00
void omap_dm_timer_free ( struct omap_dm_timer * timer )
{
2006-09-25 12:41:42 +03:00
omap_dm_timer_enable ( timer ) ;
2006-06-26 16:16:12 -07:00
omap_dm_timer_reset ( timer ) ;
2006-09-25 12:41:42 +03:00
omap_dm_timer_disable ( timer ) ;
2006-09-25 12:41:35 +03:00
2006-06-26 16:16:12 -07:00
WARN_ON ( ! timer - > reserved ) ;
timer - > reserved = 0 ;
}
2006-09-25 12:41:42 +03:00
void omap_dm_timer_enable ( struct omap_dm_timer * timer )
{
if ( timer - > enabled )
return ;
omap_dm_clk_enable ( timer - > fclk ) ;
omap_dm_clk_enable ( timer - > iclk ) ;
timer - > enabled = 1 ;
}
void omap_dm_timer_disable ( struct omap_dm_timer * timer )
{
if ( ! timer - > enabled )
return ;
omap_dm_clk_disable ( timer - > iclk ) ;
omap_dm_clk_disable ( timer - > fclk ) ;
timer - > enabled = 0 ;
}
2006-06-26 16:16:12 -07:00
int omap_dm_timer_get_irq ( struct omap_dm_timer * timer )
{
return timer - > irq ;
}
# if defined(CONFIG_ARCH_OMAP1)
2006-04-02 17:46:21 +01:00
/**
* omap_dm_timer_modify_idlect_mask - Check if any running timers use ARMXOR
* @ inputmask : current value of idlect mask
*/
__u32 omap_dm_timer_modify_idlect_mask ( __u32 inputmask )
{
2006-06-26 16:16:12 -07:00
int i ;
2006-04-02 17:46:21 +01:00
/* If ARMXOR cannot be idled this function call is unnecessary */
if ( ! ( inputmask & ( 1 < < 1 ) ) )
return inputmask ;
/* If any active timer is using ARMXOR return modified mask */
2006-06-26 16:16:12 -07:00
for ( i = 0 ; i < dm_timer_count ; i + + ) {
u32 l ;
2006-07-01 19:56:42 +01:00
l = omap_dm_timer_read_reg ( & dm_timers [ i ] , OMAP_TIMER_CTRL_REG ) ;
2006-06-26 16:16:12 -07:00
if ( l & OMAP_TIMER_CTRL_ST ) {
if ( ( ( omap_readl ( MOD_CONF_CTRL_1 ) > > ( i * 2 ) ) & 0x03 ) = = 0 )
2006-04-02 17:46:21 +01:00
inputmask & = ~ ( 1 < < 1 ) ;
else
inputmask & = ~ ( 1 < < 2 ) ;
}
2006-06-26 16:16:12 -07:00
}
2006-04-02 17:46:21 +01:00
return inputmask ;
}
2007-06-25 22:55:39 -07:00
# elif defined(CONFIG_ARCH_OMAP2) || defined (CONFIG_ARCH_OMAP3)
2006-04-02 17:46:21 +01:00
2006-06-26 16:16:12 -07:00
struct clk * omap_dm_timer_get_fclk ( struct omap_dm_timer * timer )
2005-09-07 17:20:26 +01:00
{
2006-09-25 12:41:35 +03:00
return timer - > fclk ;
2006-06-26 16:16:12 -07:00
}
2005-09-07 17:20:26 +01:00
2006-06-26 16:16:12 -07:00
__u32 omap_dm_timer_modify_idlect_mask ( __u32 inputmask )
{
BUG ( ) ;
2006-12-06 17:14:00 -08:00
return 0 ;
2005-09-07 17:20:26 +01:00
}
2006-06-26 16:16:12 -07:00
# endif
2005-09-07 17:20:26 +01:00
2006-06-26 16:16:12 -07:00
void omap_dm_timer_trigger ( struct omap_dm_timer * timer )
2005-09-07 17:20:26 +01:00
{
2006-06-26 16:16:12 -07:00
omap_dm_timer_write_reg ( timer , OMAP_TIMER_TRIGGER_REG , 0 ) ;
2005-09-07 17:20:26 +01:00
}
2006-06-26 16:16:12 -07:00
void omap_dm_timer_start ( struct omap_dm_timer * timer )
{
u32 l ;
2005-09-07 17:20:26 +01:00
2006-06-26 16:16:12 -07:00
l = omap_dm_timer_read_reg ( timer , OMAP_TIMER_CTRL_REG ) ;
if ( ! ( l & OMAP_TIMER_CTRL_ST ) ) {
l | = OMAP_TIMER_CTRL_ST ;
omap_dm_timer_write_reg ( timer , OMAP_TIMER_CTRL_REG , l ) ;
}
}
2005-09-07 17:20:26 +01:00
2006-06-26 16:16:12 -07:00
void omap_dm_timer_stop ( struct omap_dm_timer * timer )
2005-09-07 17:20:26 +01:00
{
2006-06-26 16:16:12 -07:00
u32 l ;
2005-09-07 17:20:26 +01:00
2006-06-26 16:16:12 -07:00
l = omap_dm_timer_read_reg ( timer , OMAP_TIMER_CTRL_REG ) ;
if ( l & OMAP_TIMER_CTRL_ST ) {
l & = ~ 0x1 ;
omap_dm_timer_write_reg ( timer , OMAP_TIMER_CTRL_REG , l ) ;
2005-09-07 17:20:26 +01:00
}
}
2006-06-26 16:16:12 -07:00
# ifdef CONFIG_ARCH_OMAP1
2005-09-07 17:20:26 +01:00
2006-06-26 16:16:12 -07:00
void omap_dm_timer_set_source ( struct omap_dm_timer * timer , int source )
2005-09-07 17:20:26 +01:00
{
2006-06-26 16:16:12 -07:00
int n = ( timer - dm_timers ) < < 1 ;
u32 l ;
2005-09-07 17:20:26 +01:00
2006-06-26 16:16:12 -07:00
l = omap_readl ( MOD_CONF_CTRL_1 ) & ~ ( 0x03 < < n ) ;
l | = source < < n ;
omap_writel ( l , MOD_CONF_CTRL_1 ) ;
2005-09-07 17:20:26 +01:00
}
2006-06-26 16:16:12 -07:00
# else
2005-09-07 17:20:26 +01:00
2006-06-26 16:16:12 -07:00
void omap_dm_timer_set_source ( struct omap_dm_timer * timer , int source )
2005-09-07 17:20:26 +01:00
{
2006-06-26 16:16:12 -07:00
if ( source < 0 | | source > = 3 )
return ;
clk_disable ( timer - > fclk ) ;
2006-06-26 16:16:23 -07:00
clk_set_parent ( timer - > fclk , dm_source_clocks [ source ] ) ;
2006-06-26 16:16:12 -07:00
clk_enable ( timer - > fclk ) ;
/* When the functional clock disappears, too quick writes seem to
* cause an abort . */
2006-12-07 13:58:10 -08:00
__delay ( 150000 ) ;
2005-09-07 17:20:26 +01:00
}
2006-06-26 16:16:12 -07:00
# endif
2005-09-07 17:20:26 +01:00
2006-06-26 16:16:12 -07:00
void omap_dm_timer_set_load ( struct omap_dm_timer * timer , int autoreload ,
unsigned int load )
2005-09-07 17:20:26 +01:00
{
u32 l ;
2006-06-26 16:16:12 -07:00
2005-09-07 17:20:26 +01:00
l = omap_dm_timer_read_reg ( timer , OMAP_TIMER_CTRL_REG ) ;
2006-06-26 16:16:12 -07:00
if ( autoreload )
l | = OMAP_TIMER_CTRL_AR ;
else
l & = ~ OMAP_TIMER_CTRL_AR ;
2005-09-07 17:20:26 +01:00
omap_dm_timer_write_reg ( timer , OMAP_TIMER_CTRL_REG , l ) ;
2006-06-26 16:16:12 -07:00
omap_dm_timer_write_reg ( timer , OMAP_TIMER_LOAD_REG , load ) ;
2008-07-03 12:24:30 +03:00
/* REVISIT: hw feature, ttgr overtaking tldr? */
while ( readl ( timer - > io_base + ( OMAP_TIMER_WRITE_PEND_REG & 0xff ) ) )
cpu_relax ( ) ;
2006-06-26 16:16:12 -07:00
omap_dm_timer_write_reg ( timer , OMAP_TIMER_TRIGGER_REG , 0 ) ;
2005-09-07 17:20:26 +01:00
}
2008-07-03 12:24:30 +03:00
/* Optimized set_load which removes costly spin wait in timer_start */
void omap_dm_timer_set_load_start ( struct omap_dm_timer * timer , int autoreload ,
unsigned int load )
{
u32 l ;
l = omap_dm_timer_read_reg ( timer , OMAP_TIMER_CTRL_REG ) ;
if ( autoreload )
l | = OMAP_TIMER_CTRL_AR ;
else
l & = ~ OMAP_TIMER_CTRL_AR ;
l | = OMAP_TIMER_CTRL_ST ;
omap_dm_timer_write_reg ( timer , OMAP_TIMER_COUNTER_REG , load ) ;
omap_dm_timer_write_reg ( timer , OMAP_TIMER_LOAD_REG , load ) ;
omap_dm_timer_write_reg ( timer , OMAP_TIMER_CTRL_REG , l ) ;
}
2006-06-26 16:16:12 -07:00
void omap_dm_timer_set_match ( struct omap_dm_timer * timer , int enable ,
unsigned int match )
2005-09-07 17:20:26 +01:00
{
u32 l ;
l = omap_dm_timer_read_reg ( timer , OMAP_TIMER_CTRL_REG ) ;
2006-06-26 16:16:23 -07:00
if ( enable )
2006-06-26 16:16:12 -07:00
l | = OMAP_TIMER_CTRL_CE ;
else
l & = ~ OMAP_TIMER_CTRL_CE ;
2005-09-07 17:20:26 +01:00
omap_dm_timer_write_reg ( timer , OMAP_TIMER_CTRL_REG , l ) ;
2006-06-26 16:16:12 -07:00
omap_dm_timer_write_reg ( timer , OMAP_TIMER_MATCH_REG , match ) ;
2005-09-07 17:20:26 +01:00
}
2006-06-26 16:16:12 -07:00
void omap_dm_timer_set_pwm ( struct omap_dm_timer * timer , int def_on ,
int toggle , int trigger )
2005-09-07 17:20:26 +01:00
{
u32 l ;
l = omap_dm_timer_read_reg ( timer , OMAP_TIMER_CTRL_REG ) ;
2006-06-26 16:16:12 -07:00
l & = ~ ( OMAP_TIMER_CTRL_GPOCFG | OMAP_TIMER_CTRL_SCPWM |
OMAP_TIMER_CTRL_PT | ( 0x03 < < 10 ) ) ;
if ( def_on )
l | = OMAP_TIMER_CTRL_SCPWM ;
if ( toggle )
l | = OMAP_TIMER_CTRL_PT ;
l | = trigger < < 10 ;
2005-09-07 17:20:26 +01:00
omap_dm_timer_write_reg ( timer , OMAP_TIMER_CTRL_REG , l ) ;
}
2006-06-26 16:16:12 -07:00
void omap_dm_timer_set_prescaler ( struct omap_dm_timer * timer , int prescaler )
2005-09-07 17:20:26 +01:00
{
u32 l ;
l = omap_dm_timer_read_reg ( timer , OMAP_TIMER_CTRL_REG ) ;
2006-06-26 16:16:12 -07:00
l & = ~ ( OMAP_TIMER_CTRL_PRE | ( 0x07 < < 2 ) ) ;
if ( prescaler > = 0x00 & & prescaler < = 0x07 ) {
l | = OMAP_TIMER_CTRL_PRE ;
l | = prescaler < < 2 ;
}
2005-09-07 17:20:26 +01:00
omap_dm_timer_write_reg ( timer , OMAP_TIMER_CTRL_REG , l ) ;
}
2006-06-26 16:16:12 -07:00
void omap_dm_timer_set_int_enable ( struct omap_dm_timer * timer ,
unsigned int value )
2005-09-07 17:20:26 +01:00
{
2006-06-26 16:16:12 -07:00
omap_dm_timer_write_reg ( timer , OMAP_TIMER_INT_EN_REG , value ) ;
2006-09-25 12:41:44 +03:00
omap_dm_timer_write_reg ( timer , OMAP_TIMER_WAKEUP_EN_REG , value ) ;
2005-09-07 17:20:26 +01:00
}
2006-06-26 16:16:12 -07:00
unsigned int omap_dm_timer_read_status ( struct omap_dm_timer * timer )
2005-09-07 17:20:26 +01:00
{
2006-09-25 12:41:35 +03:00
unsigned int l ;
l = omap_dm_timer_read_reg ( timer , OMAP_TIMER_STAT_REG ) ;
return l ;
2005-09-07 17:20:26 +01:00
}
2006-06-26 16:16:12 -07:00
void omap_dm_timer_write_status ( struct omap_dm_timer * timer , unsigned int value )
2005-09-07 17:20:26 +01:00
{
2006-06-26 16:16:12 -07:00
omap_dm_timer_write_reg ( timer , OMAP_TIMER_STAT_REG , value ) ;
2005-09-07 17:20:26 +01:00
}
2006-06-26 16:16:12 -07:00
unsigned int omap_dm_timer_read_counter ( struct omap_dm_timer * timer )
2005-09-07 17:20:26 +01:00
{
2006-09-25 12:41:35 +03:00
unsigned int l ;
l = omap_dm_timer_read_reg ( timer , OMAP_TIMER_COUNTER_REG ) ;
return l ;
2005-09-07 17:20:26 +01:00
}
2006-06-26 16:16:23 -07:00
void omap_dm_timer_write_counter ( struct omap_dm_timer * timer , unsigned int value )
{
2006-09-25 12:41:35 +03:00
omap_dm_timer_write_reg ( timer , OMAP_TIMER_COUNTER_REG , value ) ;
2006-06-26 16:16:23 -07:00
}
2006-06-26 16:16:12 -07:00
int omap_dm_timers_active ( void )
2005-09-07 17:20:26 +01:00
{
2006-06-26 16:16:12 -07:00
int i ;
2005-09-07 17:20:26 +01:00
2006-06-26 16:16:12 -07:00
for ( i = 0 ; i < dm_timer_count ; i + + ) {
struct omap_dm_timer * timer ;
2005-09-07 17:20:26 +01:00
2006-06-26 16:16:12 -07:00
timer = & dm_timers [ i ] ;
2006-09-25 12:41:42 +03:00
if ( ! timer - > enabled )
continue ;
2006-06-26 16:16:12 -07:00
if ( omap_dm_timer_read_reg ( timer , OMAP_TIMER_CTRL_REG ) &
2006-09-25 12:41:35 +03:00
OMAP_TIMER_CTRL_ST ) {
2006-06-26 16:16:12 -07:00
return 1 ;
2006-09-25 12:41:35 +03:00
}
2006-06-26 16:16:12 -07:00
}
return 0 ;
}
2005-09-07 17:20:26 +01:00
2007-06-21 21:48:07 -07:00
int __init omap_dm_timer_init ( void )
2005-09-07 17:20:26 +01:00
{
struct omap_dm_timer * timer ;
2006-06-26 16:16:12 -07:00
int i ;
2007-06-25 22:55:39 -07:00
if ( ! ( cpu_is_omap16xx ( ) | | cpu_class_is_omap2 ( ) ) )
2006-06-26 16:16:12 -07:00
return - ENODEV ;
2005-09-07 17:20:26 +01:00
spin_lock_init ( & dm_timer_lock ) ;
2007-06-21 21:48:07 -07:00
if ( cpu_class_is_omap1 ( ) )
dm_timers = omap1_dm_timers ;
else if ( cpu_is_omap24xx ( ) ) {
dm_timers = omap2_dm_timers ;
dm_source_names = ( char * * ) omap2_dm_source_names ;
dm_source_clocks = ( struct clk * * ) omap2_dm_source_clocks ;
2007-06-25 22:55:39 -07:00
} else if ( cpu_is_omap34xx ( ) ) {
dm_timers = omap3_dm_timers ;
dm_source_names = ( char * * ) omap3_dm_source_names ;
dm_source_clocks = ( struct clk * * ) omap3_dm_source_clocks ;
2006-06-26 16:16:23 -07:00
}
2007-06-21 21:48:07 -07:00
if ( cpu_class_is_omap2 ( ) )
for ( i = 0 ; dm_source_names [ i ] ! = NULL ; i + + )
dm_source_clocks [ i ] = clk_get ( NULL , dm_source_names [ i ] ) ;
2006-12-06 17:14:08 -08:00
if ( cpu_is_omap243x ( ) )
dm_timers [ 0 ] . phys_base = 0x49018000 ;
2006-06-26 16:16:23 -07:00
2006-06-26 16:16:12 -07:00
for ( i = 0 ; i < dm_timer_count ; i + + ) {
timer = & dm_timers [ i ] ;
2007-06-21 21:48:07 -07:00
timer - > io_base = ( void __iomem * ) io_p2v ( timer - > phys_base ) ;
2007-06-25 22:55:39 -07:00
# if defined(CONFIG_ARCH_OMAP2) || defined(CONFIG_ARCH_OMAP3)
2007-06-21 21:48:07 -07:00
if ( cpu_class_is_omap2 ( ) ) {
char clk_name [ 16 ] ;
sprintf ( clk_name , " gpt%d_ick " , i + 1 ) ;
timer - > iclk = clk_get ( NULL , clk_name ) ;
sprintf ( clk_name , " gpt%d_fck " , i + 1 ) ;
timer - > fclk = clk_get ( NULL , clk_name ) ;
}
2006-06-26 16:16:12 -07:00
# endif
2005-09-07 17:20:26 +01:00
}
return 0 ;
}