diff options
Diffstat (limited to 'arch/arm/plat-omap/dmtimer.c')
-rw-r--r-- | arch/arm/plat-omap/dmtimer.c | 255 |
1 files changed, 178 insertions, 77 deletions
diff --git a/arch/arm/plat-omap/dmtimer.c b/arch/arm/plat-omap/dmtimer.c index 938b50a33439..7b433f3bddca 100644 --- a/arch/arm/plat-omap/dmtimer.c +++ b/arch/arm/plat-omap/dmtimer.c @@ -35,16 +35,18 @@ * 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include <linux/clk.h> #include <linux/module.h> #include <linux/io.h> #include <linux/device.h> #include <linux/err.h> #include <linux/pm_runtime.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> +#include <linux/platform_data/dmtimer-omap.h> #include <plat/dmtimer.h> -#include <plat/omap-pm.h> - -#include <mach/hardware.h> static u32 omap_reserved_systimers; static LIST_HEAD(omap_timer_list); @@ -84,10 +86,6 @@ static void omap_dm_timer_write_reg(struct omap_dm_timer *timer, u32 reg, static void omap_timer_restore_context(struct omap_dm_timer *timer) { - 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, @@ -103,39 +101,38 @@ static void omap_timer_restore_context(struct omap_dm_timer *timer) timer->context.tclr); } -static void omap_dm_timer_wait_for_reset(struct omap_dm_timer *timer) +static int omap_dm_timer_reset(struct omap_dm_timer *timer) { - int c; + u32 l, timeout = 100000; - if (!timer->sys_stat) - return; + if (timer->revision != 1) + return -EINVAL; - c = 0; - while (!(__raw_readl(timer->sys_stat) & 1)) { - c++; - if (c > 100000) { - printk(KERN_ERR "Timer failed to reset\n"); - return; - } - } -} + omap_dm_timer_write_reg(timer, OMAP_TIMER_IF_CTRL_REG, 0x06); -static void omap_dm_timer_reset(struct omap_dm_timer *timer) -{ - 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); + do { + l = __omap_dm_timer_read(timer, + OMAP_TIMER_V1_SYS_STAT_OFFSET, 0); + } while (!l && timeout--); + + if (!timeout) { + dev_err(&timer->pdev->dev, "Timer failed to reset\n"); + return -ETIMEDOUT; } - __omap_dm_timer_reset(timer, 0, 0); - omap_dm_timer_disable(timer); - timer->posted = 1; + /* Configure timer for smart-idle mode */ + l = __omap_dm_timer_read(timer, OMAP_TIMER_OCP_CFG_OFFSET, 0); + l |= 0x2 << 0x3; + __omap_dm_timer_write(timer, OMAP_TIMER_OCP_CFG_OFFSET, l, 0); + + timer->posted = 0; + + return 0; } -int omap_dm_timer_prepare(struct omap_dm_timer *timer) +static int omap_dm_timer_prepare(struct omap_dm_timer *timer) { - int ret; + int rc; /* * FIXME: OMAP1 devices do not use the clock framework for dmtimers so @@ -150,13 +147,20 @@ int omap_dm_timer_prepare(struct omap_dm_timer *timer) } } - if (timer->capability & OMAP_TIMER_NEEDS_RESET) - omap_dm_timer_reset(timer); + omap_dm_timer_enable(timer); - ret = omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ); + if (timer->capability & OMAP_TIMER_NEEDS_RESET) { + rc = omap_dm_timer_reset(timer); + if (rc) { + omap_dm_timer_disable(timer); + return rc; + } + } - timer->posted = 1; - return ret; + __omap_dm_timer_enable_posted(timer); + omap_dm_timer_disable(timer); + + return omap_dm_timer_set_source(timer, OMAP_TIMER_SRC_32_KHZ); } static inline u32 omap_dm_timer_reserved_systimer(int id) @@ -212,6 +216,13 @@ struct omap_dm_timer *omap_dm_timer_request_specific(int id) unsigned long flags; int ret = 0; + /* Requesting timer by ID is not supported when device tree is used */ + if (of_have_populated_dt()) { + pr_warn("%s: Please use omap_dm_timer_request_by_cap()\n", + __func__); + return NULL; + } + spin_lock_irqsave(&dm_timer_lock, flags); list_for_each_entry(t, &omap_timer_list, node) { if (t->pdev->id == id && !t->reserved) { @@ -237,6 +248,58 @@ struct omap_dm_timer *omap_dm_timer_request_specific(int id) } EXPORT_SYMBOL_GPL(omap_dm_timer_request_specific); +/** + * omap_dm_timer_request_by_cap - Request a timer by capability + * @cap: Bit mask of capabilities to match + * + * Find a timer based upon capabilities bit mask. Callers of this function + * should use the definitions found in the plat/dmtimer.h file under the + * comment "timer capabilities used in hwmod database". Returns pointer to + * timer handle on success and a NULL pointer on failure. + */ +struct omap_dm_timer *omap_dm_timer_request_by_cap(u32 cap) +{ + struct omap_dm_timer *timer = NULL, *t; + unsigned long flags; + + if (!cap) + return NULL; + + spin_lock_irqsave(&dm_timer_lock, flags); + list_for_each_entry(t, &omap_timer_list, node) { + if ((!t->reserved) && ((t->capability & cap) == cap)) { + /* + * If timer is not NULL, we have already found one timer + * but it was not an exact match because it had more + * capabilites that what was required. Therefore, + * unreserve the last timer found and see if this one + * is a better match. + */ + if (timer) + timer->reserved = 0; + + timer = t; + timer->reserved = 1; + + /* Exit loop early if we find an exact match */ + if (t->capability == cap) + break; + } + } + spin_unlock_irqrestore(&dm_timer_lock, flags); + + if (timer && omap_dm_timer_prepare(timer)) { + timer->reserved = 0; + timer = NULL; + } + + if (!timer) + pr_debug("%s: timer request failed!\n", __func__); + + return timer; +} +EXPORT_SYMBOL_GPL(omap_dm_timer_request_by_cap); + int omap_dm_timer_free(struct omap_dm_timer *timer) { if (unlikely(!timer)) @@ -271,7 +334,7 @@ int omap_dm_timer_get_irq(struct omap_dm_timer *timer) EXPORT_SYMBOL_GPL(omap_dm_timer_get_irq); #if defined(CONFIG_ARCH_OMAP1) - +#include <mach/hardware.h> /** * omap_dm_timer_modify_idlect_mask - Check if any running timers use ARMXOR * @inputmask: current value of idlect mask @@ -348,7 +411,8 @@ int omap_dm_timer_start(struct omap_dm_timer *timer) omap_dm_timer_enable(timer); if (!(timer->capability & OMAP_TIMER_ALWON)) { - if (omap_pm_get_dev_context_loss_count(&timer->pdev->dev) != + if (timer->get_context_loss_count && + timer->get_context_loss_count(&timer->pdev->dev) != timer->ctx_loss_count) omap_timer_restore_context(timer); } @@ -377,9 +441,11 @@ int omap_dm_timer_stop(struct omap_dm_timer *timer) __omap_dm_timer_stop(timer, timer->posted, rate); - if (!(timer->capability & OMAP_TIMER_ALWON)) - timer->ctx_loss_count = - omap_pm_get_dev_context_loss_count(&timer->pdev->dev); + if (!(timer->capability & OMAP_TIMER_ALWON)) { + if (timer->get_context_loss_count) + timer->ctx_loss_count = + timer->get_context_loss_count(&timer->pdev->dev); + } /* * Since the register values are computed and written within @@ -388,7 +454,6 @@ int omap_dm_timer_stop(struct omap_dm_timer *timer) */ timer->context.tclr = omap_dm_timer_read_reg(timer, OMAP_TIMER_CTRL_REG); - timer->context.tisr = __raw_readl(timer->irq_stat); omap_dm_timer_disable(timer); return 0; } @@ -398,7 +463,7 @@ int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) { int ret; char *parent_name = NULL; - struct clk *fclk, *parent; + struct clk *parent; struct dmtimer_platform_data *pdata; if (unlikely(!timer)) @@ -414,14 +479,11 @@ int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) * use the clock framework to set the parent clock. To be removed * once OMAP1 migrated to using clock framework for dmtimers */ - if (pdata->set_timer_src) + if (pdata && pdata->set_timer_src) return pdata->set_timer_src(timer->pdev, source); - fclk = clk_get(&timer->pdev->dev, "fck"); - if (IS_ERR_OR_NULL(fclk)) { - pr_err("%s: fck not found\n", __func__); + if (!timer->fclk) return -EINVAL; - } switch (source) { case OMAP_TIMER_SRC_SYS_CLK: @@ -440,18 +502,15 @@ int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) parent = clk_get(&timer->pdev->dev, parent_name); if (IS_ERR_OR_NULL(parent)) { pr_err("%s: %s not found\n", __func__, parent_name); - ret = -EINVAL; - goto out; + return -EINVAL; } - ret = clk_set_parent(fclk, parent); + ret = clk_set_parent(timer->fclk, parent); if (IS_ERR_VALUE(ret)) pr_err("%s: failed to set %s as parent\n", __func__, parent_name); clk_put(parent); -out: - clk_put(fclk); return ret; } @@ -495,7 +554,8 @@ int omap_dm_timer_set_load_start(struct omap_dm_timer *timer, int autoreload, omap_dm_timer_enable(timer); if (!(timer->capability & OMAP_TIMER_ALWON)) { - if (omap_pm_get_dev_context_loss_count(&timer->pdev->dev) != + if (timer->get_context_loss_count && + timer->get_context_loss_count(&timer->pdev->dev) != timer->ctx_loss_count) omap_timer_restore_context(timer); } @@ -533,8 +593,8 @@ int omap_dm_timer_set_match(struct omap_dm_timer *timer, int enable, l |= OMAP_TIMER_CTRL_CE; else 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); + omap_dm_timer_write_reg(timer, OMAP_TIMER_CTRL_REG, l); /* Save the context */ timer->context.tclr = l; @@ -610,6 +670,37 @@ int omap_dm_timer_set_int_enable(struct omap_dm_timer *timer, } EXPORT_SYMBOL_GPL(omap_dm_timer_set_int_enable); +/** + * omap_dm_timer_set_int_disable - disable timer interrupts + * @timer: pointer to timer handle + * @mask: bit mask of interrupts to be disabled + * + * Disables the specified timer interrupts for a timer. + */ +int omap_dm_timer_set_int_disable(struct omap_dm_timer *timer, u32 mask) +{ + u32 l = mask; + + if (unlikely(!timer)) + return -EINVAL; + + omap_dm_timer_enable(timer); + + if (timer->revision == 1) + l = __raw_readl(timer->irq_ena) & ~mask; + + __raw_writel(l, timer->irq_dis); + l = omap_dm_timer_read_reg(timer, OMAP_TIMER_WAKEUP_EN_REG) & ~mask; + omap_dm_timer_write_reg(timer, OMAP_TIMER_WAKEUP_EN_REG, l); + + /* Save the context */ + timer->context.tier &= ~mask; + timer->context.twer &= ~mask; + omap_dm_timer_disable(timer); + return 0; +} +EXPORT_SYMBOL_GPL(omap_dm_timer_set_int_disable); + unsigned int omap_dm_timer_read_status(struct omap_dm_timer *timer) { unsigned int l; @@ -631,8 +722,7 @@ int omap_dm_timer_write_status(struct omap_dm_timer *timer, unsigned int value) 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); @@ -687,7 +777,7 @@ EXPORT_SYMBOL_GPL(omap_dm_timers_active); * 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) +static int omap_dm_timer_probe(struct platform_device *pdev) { unsigned long flags; struct omap_dm_timer *timer; @@ -695,7 +785,7 @@ static int __devinit omap_dm_timer_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct dmtimer_platform_data *pdata = pdev->dev.platform_data; - if (!pdata) { + if (!pdata && !dev->of_node) { dev_err(dev, "%s: no platform data.\n", __func__); return -ENODEV; } @@ -724,11 +814,25 @@ static int __devinit omap_dm_timer_probe(struct platform_device *pdev) return -ENOMEM; } - timer->id = pdev->id; + if (dev->of_node) { + if (of_find_property(dev->of_node, "ti,timer-alwon", NULL)) + timer->capability |= OMAP_TIMER_ALWON; + if (of_find_property(dev->of_node, "ti,timer-dsp", NULL)) + timer->capability |= OMAP_TIMER_HAS_DSP_IRQ; + if (of_find_property(dev->of_node, "ti,timer-pwm", NULL)) + timer->capability |= OMAP_TIMER_HAS_PWM; + if (of_find_property(dev->of_node, "ti,timer-secure", NULL)) + timer->capability |= OMAP_TIMER_SECURE; + } else { + timer->id = pdev->id; + timer->errata = pdata->timer_errata; + timer->capability = pdata->timer_capability; + timer->reserved = omap_dm_timer_reserved_systimer(timer->id); + timer->get_context_loss_count = pdata->get_context_loss_count; + } + timer->irq = irq->start; - timer->reserved = omap_dm_timer_reserved_systimer(timer->id); timer->pdev = pdev; - timer->capability = pdata->timer_capability; /* Skip pm_runtime_enable for OMAP1 */ if (!(timer->capability & OMAP_TIMER_NEEDS_RESET)) { @@ -760,7 +864,7 @@ static int __devinit omap_dm_timer_probe(struct platform_device *pdev) * 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) +static int omap_dm_timer_remove(struct platform_device *pdev) { struct omap_dm_timer *timer; unsigned long flags; @@ -768,7 +872,8 @@ static int __devexit omap_dm_timer_remove(struct platform_device *pdev) spin_lock_irqsave(&dm_timer_lock, flags); list_for_each_entry(timer, &omap_timer_list, node) - if (timer->pdev->id == pdev->id) { + if (!strcmp(dev_name(&timer->pdev->dev), + dev_name(&pdev->dev))) { list_del(&timer->node); ret = 0; break; @@ -778,27 +883,23 @@ static int __devexit omap_dm_timer_remove(struct platform_device *pdev) return ret; } +static const struct of_device_id omap_timer_match[] = { + { .compatible = "ti,omap2-timer", }, + {}, +}; +MODULE_DEVICE_TABLE(of, omap_timer_match); + static struct platform_driver omap_dm_timer_driver = { .probe = omap_dm_timer_probe, - .remove = __devexit_p(omap_dm_timer_remove), + .remove = omap_dm_timer_remove, .driver = { .name = "omap_timer", + .of_match_table = of_match_ptr(omap_timer_match), }, }; -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_platform_driver(omap_dm_timer_driver); MODULE_DESCRIPTION("OMAP Dual-Mode Timer Driver"); MODULE_LICENSE("GPL"); |