diff options
Diffstat (limited to 'arch/arm/plat-omap/dmtimer.c')
| -rw-r--r-- | arch/arm/plat-omap/dmtimer.c | 711 | 
1 files changed, 410 insertions, 301 deletions
diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c index 75a847dd776..de7896fd9b3 100644 --- a/arch/arm/plat-omap/dmtimer.c +++ b/arch/arm/plat-omap/dmtimer.c @@ -3,6 +3,12 @@   *   * OMAP Dual-Mode Timers   * + * Copyright (C) 2010 Texas Instruments Incorporated - http://www.ti.com/ + * Tarun Kanti DebBarma <tarun.kanti@ti.com> + * Thara Gopinath <thara@ti.com> + * + * dmtimer adaptation to platform_driver. + *   * Copyright (C) 2005 Nokia Corporation   * OMAP2 support by Juha Yrjola   * API improvements and OMAP2 clock framework support by Timo Teras @@ -29,168 +35,80 @@   * 675 Mass Ave, Cambridge, MA 02139, USA.   */ -#include <linux/init.h> -#include <linux/spinlock.h> -#include <linux/errno.h> -#include <linux/list.h> -#include <linux/clk.h> -#include <linux/delay.h>  #include <linux/io.h> -#include <linux/module.h> -#include <mach/hardware.h> -#include <plat/dmtimer.h> -#include <mach/irqs.h> - -static int dm_timer_count; - -#ifdef CONFIG_ARCH_OMAP1 -static struct omap_dm_timer omap1_dm_timers[] = { -	{ .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 }, -	{ .phys_base = 0xfffb7400, .irq = INT_1610_GPTIMER7 }, -	{ .phys_base = 0xfffbd400, .irq = INT_1610_GPTIMER8 }, -}; - -static const int omap1_dm_timer_count = ARRAY_SIZE(omap1_dm_timers); - -#else -#define omap1_dm_timers			NULL -#define omap1_dm_timer_count		0 -#endif	/* CONFIG_ARCH_OMAP1 */ - -#ifdef CONFIG_ARCH_OMAP2 -static struct omap_dm_timer omap2_dm_timers[] = { -	{ .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 }, -}; - -static const char *omap2_dm_source_names[] __initdata = { -	"sys_ck", -	"func_32k_ck", -	"alt_ck", -	NULL -}; - -static struct clk *omap2_dm_source_clocks[3]; -static const int omap2_dm_timer_count = ARRAY_SIZE(omap2_dm_timers); +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/pm_runtime.h> -#else -#define omap2_dm_timers			NULL -#define omap2_dm_timer_count		0 -#define omap2_dm_source_names		NULL -#define omap2_dm_source_clocks		NULL -#endif	/* CONFIG_ARCH_OMAP2 */ - -#ifdef CONFIG_ARCH_OMAP3 -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_34XX_GPT12_IRQ }, -}; - -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 omap3_dm_timer_count = ARRAY_SIZE(omap3_dm_timers); - -#else -#define omap3_dm_timers			NULL -#define omap3_dm_timer_count		0 -#define omap3_dm_source_names		NULL -#define omap3_dm_source_clocks		NULL -#endif	/* CONFIG_ARCH_OMAP3 */ - -#ifdef CONFIG_ARCH_OMAP4 -static struct omap_dm_timer omap4_dm_timers[] = { -	{ .phys_base = 0x4a318000, .irq = OMAP44XX_IRQ_GPT1 }, -	{ .phys_base = 0x48032000, .irq = OMAP44XX_IRQ_GPT2 }, -	{ .phys_base = 0x48034000, .irq = OMAP44XX_IRQ_GPT3 }, -	{ .phys_base = 0x48036000, .irq = OMAP44XX_IRQ_GPT4 }, -	{ .phys_base = 0x40138000, .irq = OMAP44XX_IRQ_GPT5 }, -	{ .phys_base = 0x4013a000, .irq = OMAP44XX_IRQ_GPT6 }, -	{ .phys_base = 0x4013a000, .irq = OMAP44XX_IRQ_GPT7 }, -	{ .phys_base = 0x4013e000, .irq = OMAP44XX_IRQ_GPT8 }, -	{ .phys_base = 0x4803e000, .irq = OMAP44XX_IRQ_GPT9 }, -	{ .phys_base = 0x48086000, .irq = OMAP44XX_IRQ_GPT10 }, -	{ .phys_base = 0x48088000, .irq = OMAP44XX_IRQ_GPT11 }, -	{ .phys_base = 0x4a320000, .irq = OMAP44XX_IRQ_GPT12 }, -}; -static const char *omap4_dm_source_names[] __initdata = { -	"sys_clkin_ck", -	"sys_32k_ck", -	NULL -}; -static struct clk *omap4_dm_source_clocks[2]; -static const int omap4_dm_timer_count = ARRAY_SIZE(omap4_dm_timers); - -#else -#define omap4_dm_timers			NULL -#define omap4_dm_timer_count		0 -#define omap4_dm_source_names		NULL -#define omap4_dm_source_clocks		NULL -#endif	/* CONFIG_ARCH_OMAP4 */ - -static struct omap_dm_timer *dm_timers; -static const char **dm_source_names; -static struct clk **dm_source_clocks; +#include <plat/dmtimer.h> -static spinlock_t dm_timer_lock; +static LIST_HEAD(omap_timer_list); +static DEFINE_SPINLOCK(dm_timer_lock); -/* - * 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. +/** + * omap_dm_timer_read_reg - read timer registers in posted and non-posted mode + * @timer:      timer pointer over which read operation to perform + * @reg:        lowest byte holds the register offset + * + * 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)  { -	return __omap_dm_timer_read(timer->io_base, reg, timer->posted); +	WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET); +	return __omap_dm_timer_read(timer, reg, timer->posted);  } -/* - * 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. +/** + * omap_dm_timer_write_reg - write timer registers in posted and non-posted mode + * @timer:      timer pointer over which write operation is to perform + * @reg:        lowest byte holds the register offset + * @value:      data to write into the register + * + * 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)  { -	__omap_dm_timer_write(timer->io_base, reg, value, timer->posted); +	WARN_ON((reg & 0xff) < _OMAP_TIMER_WAKEUP_EN_OFFSET); +	__omap_dm_timer_write(timer, reg, value, timer->posted); +} + +static void omap_timer_restore_context(struct omap_dm_timer *timer) +{ +	omap_dm_timer_write_reg(timer, OMAP_TIMER_OCP_CFG_OFFSET, +				timer->context.tiocp_cfg); +	if (timer->revision > 1) +		__raw_writel(timer->context.tistat, timer->sys_stat); + +	__raw_writel(timer->context.tisr, timer->irq_stat); +	omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG, +				timer->context.twer); +	omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, +				timer->context.tcrr); +	omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, +				timer->context.tldr); +	omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, +				timer->context.tmar); +	omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, +				timer->context.tsicr); +	__raw_writel(timer->context.tier, timer->irq_ena); +	omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, +				timer->context.tclr);  }  static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer)  {  	int c; +	if (!timer->sys_stat) +		return; +  	c = 0; -	while (!(omap_dm_timer_read_reg(timer, OMAP_TIMER_SYS_STAT_REG) & 1)) { +	while (!(__raw_readl(timer->sys_stat) & 1)) {  		c++;  		if (c > 100000) {  			printk(KERN_ERR "Timer failed to reset\n"); @@ -201,53 +119,65 @@ static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer)  static void omap_dm_timer_reset(struct omap_dm_timer *timer)  { -	int autoidle = 0, wakeup = 0; - -	if (!cpu_class_is_omap2() || timer != &dm_timers[0]) { +	omap_dm_timer_enable(timer); +	if (timer->pdev->id != 1) {  		omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06);  		omap_dm_timer_wait_for_reset(timer);  	} -	omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ); - -	/* Enable autoidle on OMAP2+ */ -	if (cpu_class_is_omap2()) -		autoidle = 1; -	/* -	 * Enable wake-up on OMAP2 CPUs. -	 */ -	if (cpu_class_is_omap2()) -		wakeup = 1; - -	__omap_dm_timer_reset(timer->io_base, autoidle, wakeup); +	__omap_dm_timer_reset(timer, 0, 0); +	omap_dm_timer_disable(timer);  	timer->posted = 1;  } -void omap_dm_timer_prepare(struct omap_dm_timer *timer) +int omap_dm_timer_prepare(struct omap_dm_timer *timer)  { -	omap_dm_timer_enable(timer); -	omap_dm_timer_reset(timer); +	struct dmtimer_platform_data *pdata = timer->pdev->dev.platform_data; +	int ret; + +	timer->fclk = clk_get(&timer->pdev->dev, "fck"); +	if (WARN_ON_ONCE(IS_ERR_OR_NULL(timer->fclk))) { +		timer->fclk = NULL; +		dev_err(&timer->pdev->dev, ": No fclk handle.\n"); +		return -EINVAL; +	} + +	if (pdata->needs_manual_reset) +		omap_dm_timer_reset(timer); + +	ret = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ); + +	timer->posted = 1; +	return ret;  }  struct omap_dm_timer *omap_dm_timer_request(void)  { -	struct omap_dm_timer *timer = NULL; +	struct omap_dm_timer *timer = NULL, *t;  	unsigned long flags; -	int i; +	int ret = 0;  	spin_lock_irqsave(&dm_timer_lock, flags); -	for (i = 0; i < dm_timer_count; i++) { -		if (dm_timers[i].reserved) +	list_for_each_entry(t, &omap_timer_list, node) { +		if (t->reserved)  			continue; -		timer = &dm_timers[i]; +		timer = t;  		timer->reserved = 1;  		break;  	} + +	if (timer) { +		ret = omap_dm_timer_prepare(timer); +		if (ret) { +			timer->reserved = 0; +			timer = NULL; +		} +	}  	spin_unlock_irqrestore(&dm_timer_lock, flags); -	if (timer != NULL) -		omap_dm_timer_prepare(timer); +	if (!timer) +		pr_debug("%s: timer request failed!\n", __func__);  	return timer;  } @@ -255,74 +185,65 @@ EXPORT_SYMBOL_GPL(omap_dm_timer_request);  struct omap_dm_timer *omap_dm_timer_request_specific(int id)  { -	struct omap_dm_timer *timer; +	struct omap_dm_timer *timer = NULL, *t;  	unsigned long flags; +	int ret = 0;  	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", -		       __FILE__, __LINE__, __func__, id); -		dump_stack(); -		return NULL; +	list_for_each_entry(t, &omap_timer_list, node) { +		if (t->pdev->id == id && !t->reserved) { +			timer = t; +			timer->reserved = 1; +			break; +		}  	} -	timer = &dm_timers[id-1]; -	timer->reserved = 1; +	if (timer) { +		ret = omap_dm_timer_prepare(timer); +		if (ret) { +			timer->reserved = 0; +			timer = NULL; +		} +	}  	spin_unlock_irqrestore(&dm_timer_lock, flags); -	omap_dm_timer_prepare(timer); +	if (!timer) +		pr_debug("%s: timer%d request failed!\n", __func__, id);  	return timer;  }  EXPORT_SYMBOL_GPL(omap_dm_timer_request_specific); -void omap_dm_timer_free(struct omap_dm_timer *timer) +int omap_dm_timer_free(struct omap_dm_timer *timer)  { -	omap_dm_timer_enable(timer); -	omap_dm_timer_reset(timer); -	omap_dm_timer_disable(timer); +	if (unlikely(!timer)) +		return -EINVAL; + +	clk_put(timer->fclk);  	WARN_ON(!timer->reserved);  	timer->reserved = 0; +	return 0;  }  EXPORT_SYMBOL_GPL(omap_dm_timer_free);  void omap_dm_timer_enable(struct omap_dm_timer *timer)  { -	if (timer->enabled) -		return; - -#ifdef CONFIG_ARCH_OMAP2PLUS -	if (cpu_class_is_omap2()) { -		clk_enable(timer->fclk); -		clk_enable(timer->iclk); -	} -#endif - -	timer->enabled = 1; +	pm_runtime_get_sync(&timer->pdev->dev);  }  EXPORT_SYMBOL_GPL(omap_dm_timer_enable);  void omap_dm_timer_disable(struct omap_dm_timer *timer)  { -	if (!timer->enabled) -		return; - -#ifdef CONFIG_ARCH_OMAP2PLUS -	if (cpu_class_is_omap2()) { -		clk_disable(timer->iclk); -		clk_disable(timer->fclk); -	} -#endif - -	timer->enabled = 0; +	pm_runtime_put(&timer->pdev->dev);  }  EXPORT_SYMBOL_GPL(omap_dm_timer_disable);  int omap_dm_timer_get_irq(struct omap_dm_timer *timer)  { -	return timer->irq; +	if (timer) +		return timer->irq; +	return -EINVAL;  }  EXPORT_SYMBOL_GPL(omap_dm_timer_get_irq); @@ -334,24 +255,29 @@ EXPORT_SYMBOL_GPL(omap_dm_timer_get_irq);   */  __u32 omap_dm_timer_modify_idlect_mask(__u32 inputmask)  { -	int i; +	int i = 0; +	struct omap_dm_timer *timer = NULL; +	unsigned long flags;  	/* 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 */ -	for (i = 0; i < dm_timer_count; i++) { +	spin_lock_irqsave(&dm_timer_lock, flags); +	list_for_each_entry(timer, &omap_timer_list, node) {  		u32 l; -		l = omap_dm_timer_read_reg(&dm_timers[i], OMAP_TIMER_CTRL_REG); +		l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);  		if (l & OMAP_TIMER_CTRL_ST) {  			if (((omap_readl(MOD_CONF_CTRL_1) >> (i * 2)) & 0x03) == 0)  				inputmask &= ~(1 << 1);  			else  				inputmask &= ~(1 << 2);  		} +		i++;  	} +	spin_unlock_irqrestore(&dm_timer_lock, flags);  	return inputmask;  } @@ -361,7 +287,9 @@ EXPORT_SYMBOL_GPL(omap_dm_timer_modify_idlect_mask);  struct clk *omap_dm_timer_get_fclk(struct omap_dm_timer *timer)  { -	return timer->fclk; +	if (timer) +		return timer->fclk; +	return NULL;  }  EXPORT_SYMBOL_GPL(omap_dm_timer_get_fclk); @@ -375,70 +303,91 @@ EXPORT_SYMBOL_GPL(omap_dm_timer_modify_idlect_mask);  #endif -void omap_dm_timer_trigger(struct omap_dm_timer *timer) +int omap_dm_timer_trigger(struct omap_dm_timer *timer)  { +	if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) { +		pr_err("%s: timer not available or enabled.\n", __func__); +		return -EINVAL; +	} +  	omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0); +	return 0;  }  EXPORT_SYMBOL_GPL(omap_dm_timer_trigger); -void omap_dm_timer_start(struct omap_dm_timer *timer) +int omap_dm_timer_start(struct omap_dm_timer *timer)  {  	u32 l; +	if (unlikely(!timer)) +		return -EINVAL; + +	omap_dm_timer_enable(timer); + +	if (timer->loses_context) { +		u32 ctx_loss_cnt_after = +			timer->get_context_loss_count(&timer->pdev->dev); +		if (ctx_loss_cnt_after != timer->ctx_loss_count) +			omap_timer_restore_context(timer); +	} +  	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);  	} + +	/* Save the context */ +	timer->context.tclr = l; +	return 0;  }  EXPORT_SYMBOL_GPL(omap_dm_timer_start); -void omap_dm_timer_stop(struct omap_dm_timer *timer) +int omap_dm_timer_stop(struct omap_dm_timer *timer)  {  	unsigned long rate = 0; +	struct dmtimer_platform_data *pdata = timer->pdev->dev.platform_data; -#ifdef CONFIG_ARCH_OMAP2PLUS -	rate = clk_get_rate(timer->fclk); -#endif +	if (unlikely(!timer)) +		return -EINVAL; -	__omap_dm_timer_stop(timer->io_base, timer->posted, rate); +	if (!pdata->needs_manual_reset) +		rate = clk_get_rate(timer->fclk); + +	__omap_dm_timer_stop(timer, timer->posted, rate); + +	return 0;  }  EXPORT_SYMBOL_GPL(omap_dm_timer_stop); -#ifdef CONFIG_ARCH_OMAP1 -  int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source)  { -	int n = (timer - dm_timers) << 1; -	u32 l; - -	l = omap_readl(MOD_CONF_CTRL_1) & ~(0x03 << n); -	l |= source << n; -	omap_writel(l, MOD_CONF_CTRL_1); +	int ret; +	struct dmtimer_platform_data *pdata; -	return 0; -} -EXPORT_SYMBOL_GPL(omap_dm_timer_set_source); +	if (unlikely(!timer)) +		return -EINVAL; -#else +	pdata = timer->pdev->dev.platform_data; -int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) -{  	if (source < 0 || source >= 3)  		return -EINVAL; -	return __omap_dm_timer_set_source(timer->fclk, -						dm_source_clocks[source]); +	ret = pdata->set_timer_src(timer->pdev, source); + +	return ret;  }  EXPORT_SYMBOL_GPL(omap_dm_timer_set_source); -#endif - -void omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload, +int omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload,  			    unsigned int load)  {  	u32 l; +	if (unlikely(!timer)) +		return -EINVAL; + +	omap_dm_timer_enable(timer);  	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);  	if (autoreload)  		l |= OMAP_TIMER_CTRL_AR; @@ -448,15 +397,32 @@ void omap_dm_timer_set_load(struct omap_dm_timer *timer, int autoreload,  	omap_dm_timer_write_reg(timer, OMAP_TIMER_LOAD_REG, load);  	omap_dm_timer_write_reg(timer, OMAP_TIMER_TRIGGER_REG, 0); +	/* Save the context */ +	timer->context.tclr = l; +	timer->context.tldr = load; +	omap_dm_timer_disable(timer); +	return 0;  }  EXPORT_SYMBOL_GPL(omap_dm_timer_set_load);  /* 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, +int omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload,                              unsigned int load)  {  	u32 l; +	if (unlikely(!timer)) +		return -EINVAL; + +	omap_dm_timer_enable(timer); + +	if (timer->loses_context) { +		u32 ctx_loss_cnt_after = +			timer->get_context_loss_count(&timer->pdev->dev); +		if (ctx_loss_cnt_after != timer->ctx_loss_count) +			omap_timer_restore_context(timer); +	} +  	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);  	if (autoreload) {  		l |= OMAP_TIMER_CTRL_AR; @@ -466,15 +432,25 @@ void omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload,  	}  	l |= OMAP_TIMER_CTRL_ST; -	__omap_dm_timer_load_start(timer->io_base, l, load, timer->posted); +	__omap_dm_timer_load_start(timer, l, load, timer->posted); + +	/* Save the context */ +	timer->context.tclr = l; +	timer->context.tldr = load; +	timer->context.tcrr = load; +	return 0;  }  EXPORT_SYMBOL_GPL(omap_dm_timer_set_load_start); -void omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable, +int omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable,  			     unsigned int match)  {  	u32 l; +	if (unlikely(!timer)) +		return -EINVAL; + +	omap_dm_timer_enable(timer);  	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);  	if (enable)  		l |= OMAP_TIMER_CTRL_CE; @@ -482,14 +458,24 @@ void omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable,  		l &= ~OMAP_TIMER_CTRL_CE;  	omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l);  	omap_dm_timer_write_reg(timer, OMAP_TIMER_MATCH_REG, match); + +	/* Save the context */ +	timer->context.tclr = l; +	timer->context.tmar = match; +	omap_dm_timer_disable(timer); +	return 0;  }  EXPORT_SYMBOL_GPL(omap_dm_timer_set_match); -void omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on, +int omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on,  			   int toggle, int trigger)  {  	u32 l; +	if (unlikely(!timer)) +		return -EINVAL; + +	omap_dm_timer_enable(timer);  	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);  	l &= ~(OMAP_TIMER_CTRL_GPOCFG | OMAP_TIMER_CTRL_SCPWM |  	       OMAP_TIMER_CTRL_PT | (0x03 << 10)); @@ -499,13 +485,22 @@ void omap_dm_timer_set_pwm(struct omap_dm_timer *timer, int def_on,  		l |= OMAP_TIMER_CTRL_PT;  	l |= trigger << 10;  	omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); + +	/* Save the context */ +	timer->context.tclr = l; +	omap_dm_timer_disable(timer); +	return 0;  }  EXPORT_SYMBOL_GPL(omap_dm_timer_set_pwm); -void omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler) +int omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler)  {  	u32 l; +	if (unlikely(!timer)) +		return -EINVAL; + +	omap_dm_timer_enable(timer);  	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG);  	l &= ~(OMAP_TIMER_CTRL_PRE | (0x07 << 2));  	if (prescaler >= 0x00 && prescaler <= 0x07) { @@ -513,13 +508,28 @@ void omap_dm_timer_set_prescaler(struct omap_dm_timer *timer, int prescaler)  		l |= prescaler << 2;  	}  	omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); + +	/* Save the context */ +	timer->context.tclr = l; +	omap_dm_timer_disable(timer); +	return 0;  }  EXPORT_SYMBOL_GPL(omap_dm_timer_set_prescaler); -void omap_dm_timer_set_int_enable(struct omap_dm_timer *timer, +int omap_dm_timer_set_int_enable(struct omap_dm_timer *timer,  				  unsigned int value)  { -	__omap_dm_timer_int_enable(timer->io_base, value); +	if (unlikely(!timer)) +		return -EINVAL; + +	omap_dm_timer_enable(timer); +	__omap_dm_timer_int_enable(timer, value); + +	/* Save the context */ +	timer->context.tier = value; +	timer->context.twer = value; +	omap_dm_timer_disable(timer); +	return 0;  }  EXPORT_SYMBOL_GPL(omap_dm_timer_set_int_enable); @@ -527,40 +537,61 @@ unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer)  {  	unsigned int l; -	l = omap_dm_timer_read_reg(timer, OMAP_TIMER_STAT_REG); +	if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) { +		pr_err("%s: timer not available or enabled.\n", __func__); +		return 0; +	} + +	l = __raw_readl(timer->irq_stat);  	return l;  }  EXPORT_SYMBOL_GPL(omap_dm_timer_read_status); -void omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value) +int omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value)  { -	__omap_dm_timer_write_status(timer->io_base, value); +	if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) +		return -EINVAL; + +	__omap_dm_timer_write_status(timer, value); +	/* Save the context */ +	timer->context.tisr = value; +	return 0;  }  EXPORT_SYMBOL_GPL(omap_dm_timer_write_status);  unsigned int omap_dm_timer_read_counter(struct omap_dm_timer *timer)  { -	return __omap_dm_timer_read_counter(timer->io_base, timer->posted); +	if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) { +		pr_err("%s: timer not iavailable or enabled.\n", __func__); +		return 0; +	} + +	return __omap_dm_timer_read_counter(timer, timer->posted);  }  EXPORT_SYMBOL_GPL(omap_dm_timer_read_counter); -void omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value) +int omap_dm_timer_write_counter(struct omap_dm_timer *timer, unsigned int value)  { +	if (unlikely(!timer || pm_runtime_suspended(&timer->pdev->dev))) { +		pr_err("%s: timer not available or enabled.\n", __func__); +		return -EINVAL; +	} +  	omap_dm_timer_write_reg(timer, OMAP_TIMER_COUNTER_REG, value); + +	/* Save the context */ +	timer->context.tcrr = value; +	return 0;  }  EXPORT_SYMBOL_GPL(omap_dm_timer_write_counter);  int omap_dm_timers_active(void)  { -	int i; - -	for (i = 0; i < dm_timer_count; i++) { -		struct omap_dm_timer *timer; - -		timer = &dm_timers[i]; +	struct omap_dm_timer *timer; -		if (!timer->enabled) +	list_for_each_entry(timer, &omap_timer_list, node) { +		if (!timer->reserved)  			continue;  		if (omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG) & @@ -572,69 +603,147 @@ int omap_dm_timers_active(void)  }  EXPORT_SYMBOL_GPL(omap_dm_timers_active); -static int __init omap_dm_timer_init(void) +/** + * omap_dm_timer_probe - probe function called for every registered device + * @pdev:	pointer to current timer platform device + * + * Called by driver framework at the end of device registration for all + * timer devices. + */ +static int __devinit omap_dm_timer_probe(struct platform_device *pdev)  { +	int ret; +	unsigned long flags;  	struct omap_dm_timer *timer; -	int i, map_size = SZ_8K;	/* Module 4KB + L4 4KB except on omap1 */ +	struct resource *mem, *irq, *ioarea; +	struct dmtimer_platform_data *pdata = pdev->dev.platform_data; -	if (!(cpu_is_omap16xx() || cpu_class_is_omap2())) +	if (!pdata) { +		dev_err(&pdev->dev, "%s: no platform data.\n", __func__);  		return -ENODEV; +	} -	spin_lock_init(&dm_timer_lock); +	irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); +	if (unlikely(!irq)) { +		dev_err(&pdev->dev, "%s: no IRQ resource.\n", __func__); +		return -ENODEV; +	} -	if (cpu_class_is_omap1()) { -		dm_timers = omap1_dm_timers; -		dm_timer_count = omap1_dm_timer_count; -		map_size = SZ_2K; -	} else if (cpu_is_omap24xx()) { -		dm_timers = omap2_dm_timers; -		dm_timer_count = omap2_dm_timer_count; -		dm_source_names = omap2_dm_source_names; -		dm_source_clocks = omap2_dm_source_clocks; -	} else if (cpu_is_omap34xx()) { -		dm_timers = omap3_dm_timers; -		dm_timer_count = omap3_dm_timer_count; -		dm_source_names = omap3_dm_source_names; -		dm_source_clocks = omap3_dm_source_clocks; -	} else if (cpu_is_omap44xx()) { -		dm_timers = omap4_dm_timers; -		dm_timer_count = omap4_dm_timer_count; -		dm_source_names = omap4_dm_source_names; -		dm_source_clocks = omap4_dm_source_clocks; +	mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); +	if (unlikely(!mem)) { +		dev_err(&pdev->dev, "%s: no memory resource.\n", __func__); +		return -ENODEV;  	} -	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]); +	ioarea = request_mem_region(mem->start, resource_size(mem), +			pdev->name); +	if (!ioarea) { +		dev_err(&pdev->dev, "%s: region already claimed.\n", __func__); +		return -EBUSY; +	} -	if (cpu_is_omap243x()) -		dm_timers[0].phys_base = 0x49018000; +	timer = kzalloc(sizeof(struct omap_dm_timer), GFP_KERNEL); +	if (!timer) { +		dev_err(&pdev->dev, "%s: no memory for omap_dm_timer.\n", +			__func__); +		ret = -ENOMEM; +		goto err_free_ioregion; +	} -	for (i = 0; i < dm_timer_count; i++) { -		timer = &dm_timers[i]; +	timer->io_base = ioremap(mem->start, resource_size(mem)); +	if (!timer->io_base) { +		dev_err(&pdev->dev, "%s: ioremap failed.\n", __func__); +		ret = -ENOMEM; +		goto err_free_mem; +	} -		/* Static mapping, never released */ -		timer->io_base = ioremap(timer->phys_base, map_size); -		BUG_ON(!timer->io_base); +	timer->id = pdev->id; +	timer->irq = irq->start; +	timer->reserved = pdata->reserved; +	timer->pdev = pdev; +	timer->loses_context = pdata->loses_context; +	timer->get_context_loss_count = pdata->get_context_loss_count; -#ifdef CONFIG_ARCH_OMAP2PLUS -		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); -		} +	/* Skip pm_runtime_enable for OMAP1 */ +	if (!pdata->needs_manual_reset) { +		pm_runtime_enable(&pdev->dev); +		pm_runtime_irq_safe(&pdev->dev); +	} -		/* One or two timers may be set up early for sys_timer */ -		if (sys_timer_reserved & (1  << i)) { -			timer->reserved = 1; -			timer->posted = 1; -		} -#endif +	if (!timer->reserved) { +		pm_runtime_get_sync(&pdev->dev); +		__omap_dm_timer_init_regs(timer); +		pm_runtime_put(&pdev->dev);  	} +	/* add the timer element to the list */ +	spin_lock_irqsave(&dm_timer_lock, flags); +	list_add_tail(&timer->node, &omap_timer_list); +	spin_unlock_irqrestore(&dm_timer_lock, flags); + +	dev_dbg(&pdev->dev, "Device Probed.\n"); +  	return 0; + +err_free_mem: +	kfree(timer); + +err_free_ioregion: +	release_mem_region(mem->start, resource_size(mem)); + +	return ret; +} + +/** + * omap_dm_timer_remove - cleanup a registered timer device + * @pdev:	pointer to current timer platform device + * + * Called by driver framework whenever a timer device is unregistered. + * In addition to freeing platform resources it also deletes the timer + * entry from the local list. + */ +static int __devexit omap_dm_timer_remove(struct platform_device *pdev) +{ +	struct omap_dm_timer *timer; +	unsigned long flags; +	int ret = -EINVAL; + +	spin_lock_irqsave(&dm_timer_lock, flags); +	list_for_each_entry(timer, &omap_timer_list, node) +		if (timer->pdev->id == pdev->id) { +			list_del(&timer->node); +			kfree(timer); +			ret = 0; +			break; +		} +	spin_unlock_irqrestore(&dm_timer_lock, flags); + +	return ret;  } -arch_initcall(omap_dm_timer_init); +static struct platform_driver omap_dm_timer_driver = { +	.probe  = omap_dm_timer_probe, +	.remove = omap_dm_timer_remove, +	.driver = { +		.name   = "omap_timer", +	}, +}; + +static int __init omap_dm_timer_driver_init(void) +{ +	return platform_driver_register(&omap_dm_timer_driver); +} + +static void __exit omap_dm_timer_driver_exit(void) +{ +	platform_driver_unregister(&omap_dm_timer_driver); +} + +early_platform_init("earlytimer", &omap_dm_timer_driver); +module_init(omap_dm_timer_driver_init); +module_exit(omap_dm_timer_driver_exit); + +MODULE_DESCRIPTION("OMAP Dual-Mode Timer Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:" DRIVER_NAME); +MODULE_AUTHOR("Texas Instruments Inc");  |