Commit 9725f445 authored by Jon Hunter's avatar Jon Hunter

ARM: OMAP: Add DT support for timer driver

In order to add device-tree support to the timer driver the following changes
were made ...

1. Allocate system timers (used for clock-events and clock-source) based upon
   timer properties rather than using an hard-coded timer instance ID. To allow
   this a new helper function called omap_dmtimer_find_by_property() has been
   added for finding a timer with the particular properties in the device-tree
   blob. Please note that this is an internal helper function for system timers
   only to find a timer in the device-tree blob. This cannot be used by device
   drivers, another API has been added for that (see below). Timers that are
   allocated for system timers are dynamically disabled at boot time by adding
   a status property with the value "disabled" to the timer's device-tree node.

   Please note that when allocating system timers we now pass a timer ID and
   timer property. The timer ID is only be used for allocating a timer when
   booting without device-tree. Once device-tree migration is complete, all
   the timer ID references will be removed.

2. System timer resources (memory and interrupts) are directly obtained from
   the device-tree timer node when booting with device-tree, so that system
   timers are no longer reliant upon the OMAP HWMOD framework to provide these
   resources.

3. If DT blob is present, then let device-tree create the timer devices
   dynamically.

4. When device-tree is present the "id" field in the platform_device structure
   (pdev->id) is initialised to -1 and hence cannot be used to identify a timer
   instance. Due to this the following changes were made ...
   a). The API omap_dm_timer_request_specific() is not supported when using
       device-tree, because it uses the device ID to request a specific timer.
       This function will return an error if called when device-tree is present.
       Users of this API should use omap_dm_timer_request_by_cap() instead.
   b). When removing the DMTIMER driver, the timer "id" was used to identify the
       timer instance. The remove function has been modified to use the device
       name instead of the "id".

5. When device-tree is present the platform_data structure will be NULL and so
   check for this.

6. The OMAP timer device tree binding has the following optional parameters ...
   a). ti,timer-alwon  --> Timer is in an always-on power domain
   b). ti,timer-dsp    --> Timer can generate an interrupt to the on-chip DSP
   c). ti,timer-pwm    --> Timer can generate a PWM output
   d). ti,timer-secure --> Timer is reserved on a secure OMAP device
   Search for the above parameters and set the appropriate timer attribute
   flags.
Signed-off-by: default avatarJon Hunter <jon-hunter@ti.com>
parent 7dd9d502
...@@ -37,6 +37,8 @@ ...@@ -37,6 +37,8 @@
#include <linux/clockchips.h> #include <linux/clockchips.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <asm/mach/time.h> #include <asm/mach/time.h>
#include <asm/smp_twd.h> #include <asm/smp_twd.h>
...@@ -66,11 +68,13 @@ ...@@ -66,11 +68,13 @@
#define OMAP3_CLKEV_SOURCE OMAP3_32K_SOURCE #define OMAP3_CLKEV_SOURCE OMAP3_32K_SOURCE
#define OMAP4_CLKEV_SOURCE OMAP4_32K_SOURCE #define OMAP4_CLKEV_SOURCE OMAP4_32K_SOURCE
#define OMAP3_SECURE_TIMER 12 #define OMAP3_SECURE_TIMER 12
#define TIMER_PROP_SECURE "ti,timer-secure"
#else #else
#define OMAP2_CLKEV_SOURCE OMAP2_MPU_SOURCE #define OMAP2_CLKEV_SOURCE OMAP2_MPU_SOURCE
#define OMAP3_CLKEV_SOURCE OMAP3_MPU_SOURCE #define OMAP3_CLKEV_SOURCE OMAP3_MPU_SOURCE
#define OMAP4_CLKEV_SOURCE OMAP4_MPU_SOURCE #define OMAP4_CLKEV_SOURCE OMAP4_MPU_SOURCE
#define OMAP3_SECURE_TIMER 1 #define OMAP3_SECURE_TIMER 1
#define TIMER_PROP_SECURE "ti,timer-alwon"
#endif #endif
#define REALTIME_COUNTER_BASE 0x48243200 #define REALTIME_COUNTER_BASE 0x48243200
...@@ -155,6 +159,40 @@ static struct of_device_id omap_timer_match[] __initdata = { ...@@ -155,6 +159,40 @@ static struct of_device_id omap_timer_match[] __initdata = {
{ } { }
}; };
/**
* omap_get_timer_dt - get a timer using device-tree
* @match - device-tree match structure for matching a device type
* @property - optional timer property to match
*
* Helper function to get a timer during early boot using device-tree for use
* as kernel system timer. Optionally, the property argument can be used to
* select a timer with a specific property. Once a timer is found then mark
* the timer node in device-tree as disabled, to prevent the kernel from
* registering this timer as a platform device and so no one else can use it.
*/
static struct device_node * __init omap_get_timer_dt(struct of_device_id *match,
const char *property)
{
struct device_node *np;
for_each_matching_node(np, match) {
if (!of_device_is_available(np)) {
of_node_put(np);
continue;
}
if (property && !of_get_property(np, property, NULL)) {
of_node_put(np);
continue;
}
prom_add_property(np, &device_disabled);
return np;
}
return NULL;
}
/** /**
* omap_dmtimer_init - initialisation function when device tree is used * omap_dmtimer_init - initialisation function when device tree is used
* *
...@@ -172,43 +210,74 @@ void __init omap_dmtimer_init(void) ...@@ -172,43 +210,74 @@ void __init omap_dmtimer_init(void)
/* If we are a secure device, remove any secure timer nodes */ /* If we are a secure device, remove any secure timer nodes */
if ((omap_type() != OMAP2_DEVICE_TYPE_GP)) { if ((omap_type() != OMAP2_DEVICE_TYPE_GP)) {
for_each_matching_node(np, omap_timer_match) { np = omap_get_timer_dt(omap_timer_match, "ti,timer-secure");
if (of_get_property(np, "ti,timer-secure", NULL)) if (np)
prom_add_property(np, &device_disabled); of_node_put(np);
}
} }
} }
static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer, static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer,
int gptimer_id, int gptimer_id,
const char *fck_source) const char *fck_source,
const char *property)
{ {
char name[10]; /* 10 = sizeof("gptXX_Xck0") */ char name[10]; /* 10 = sizeof("gptXX_Xck0") */
const char *oh_name;
struct device_node *np;
struct omap_hwmod *oh; struct omap_hwmod *oh;
struct resource irq_rsrc, mem_rsrc; struct resource irq_rsrc, mem_rsrc;
size_t size; size_t size;
int res = 0; int res = 0;
int r; int r;
sprintf(name, "timer%d", gptimer_id); if (of_have_populated_dt()) {
omap_hwmod_setup_one(name); np = omap_get_timer_dt(omap_timer_match, NULL);
oh = omap_hwmod_lookup(name); if (!np)
return -ENODEV;
of_property_read_string_index(np, "ti,hwmods", 0, &oh_name);
if (!oh_name)
return -ENODEV;
timer->irq = irq_of_parse_and_map(np, 0);
if (!timer->irq)
return -ENXIO;
timer->io_base = of_iomap(np, 0);
of_node_put(np);
} else {
if (omap_dm_timer_reserve_systimer(gptimer_id))
return -ENODEV;
sprintf(name, "timer%d", gptimer_id);
oh_name = name;
}
omap_hwmod_setup_one(oh_name);
oh = omap_hwmod_lookup(oh_name);
if (!oh) if (!oh)
return -ENODEV; return -ENODEV;
r = omap_hwmod_get_resource_byname(oh, IORESOURCE_IRQ, NULL, &irq_rsrc); if (!of_have_populated_dt()) {
if (r) r = omap_hwmod_get_resource_byname(oh, IORESOURCE_IRQ, NULL,
return -ENXIO; &irq_rsrc);
timer->irq = irq_rsrc.start; if (r)
return -ENXIO;
r = omap_hwmod_get_resource_byname(oh, IORESOURCE_MEM, NULL, &mem_rsrc); timer->irq = irq_rsrc.start;
if (r)
return -ENXIO; r = omap_hwmod_get_resource_byname(oh, IORESOURCE_MEM, NULL,
timer->phys_base = mem_rsrc.start; &mem_rsrc);
size = mem_rsrc.end - mem_rsrc.start; if (r)
return -ENXIO;
timer->phys_base = mem_rsrc.start;
size = mem_rsrc.end - mem_rsrc.start;
/* Static mapping, never released */
timer->io_base = ioremap(timer->phys_base, size);
}
/* Static mapping, never released */
timer->io_base = ioremap(timer->phys_base, size);
if (!timer->io_base) if (!timer->io_base)
return -ENXIO; return -ENXIO;
...@@ -219,9 +288,7 @@ static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer, ...@@ -219,9 +288,7 @@ static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer,
omap_hwmod_enable(oh); omap_hwmod_enable(oh);
if (omap_dm_timer_reserve_systimer(gptimer_id)) /* FIXME: Need to remove hard-coded test on timer ID */
return -ENODEV;
if (gptimer_id != 12) { if (gptimer_id != 12) {
struct clk *src; struct clk *src;
...@@ -231,8 +298,8 @@ static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer, ...@@ -231,8 +298,8 @@ static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer,
} else { } else {
res = __omap_dm_timer_set_source(timer->fclk, src); res = __omap_dm_timer_set_source(timer->fclk, src);
if (IS_ERR_VALUE(res)) if (IS_ERR_VALUE(res))
pr_warning("%s: timer%i cannot set source\n", pr_warn("%s: %s cannot set source\n",
__func__, gptimer_id); __func__, oh->name);
clk_put(src); clk_put(src);
} }
} }
...@@ -248,11 +315,12 @@ static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer, ...@@ -248,11 +315,12 @@ static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer,
} }
static void __init omap2_gp_clockevent_init(int gptimer_id, static void __init omap2_gp_clockevent_init(int gptimer_id,
const char *fck_source) const char *fck_source,
const char *property)
{ {
int res; int res;
res = omap_dm_timer_init_one(&clkev, gptimer_id, fck_source); res = omap_dm_timer_init_one(&clkev, gptimer_id, fck_source, property);
BUG_ON(res); BUG_ON(res);
omap2_gp_timer_irq.dev_id = &clkev; omap2_gp_timer_irq.dev_id = &clkev;
...@@ -356,7 +424,7 @@ static void __init omap2_gptimer_clocksource_init(int gptimer_id, ...@@ -356,7 +424,7 @@ static void __init omap2_gptimer_clocksource_init(int gptimer_id,
{ {
int res; int res;
res = omap_dm_timer_init_one(&clksrc, gptimer_id, fck_source); res = omap_dm_timer_init_one(&clksrc, gptimer_id, fck_source, NULL);
BUG_ON(res); BUG_ON(res);
__omap_dm_timer_load_start(&clksrc, __omap_dm_timer_load_start(&clksrc,
...@@ -468,12 +536,12 @@ static inline void __init realtime_counter_init(void) ...@@ -468,12 +536,12 @@ static inline void __init realtime_counter_init(void)
{} {}
#endif #endif
#define OMAP_SYS_TIMER_INIT(name, clkev_nr, clkev_src, \ #define OMAP_SYS_TIMER_INIT(name, clkev_nr, clkev_src, clkev_prop, \
clksrc_nr, clksrc_src) \ clksrc_nr, clksrc_src) \
static void __init omap##name##_timer_init(void) \ static void __init omap##name##_timer_init(void) \
{ \ { \
omap_dmtimer_init(); \ omap_dmtimer_init(); \
omap2_gp_clockevent_init((clkev_nr), clkev_src); \ omap2_gp_clockevent_init((clkev_nr), clkev_src, clkev_prop); \
omap2_clocksource_init((clksrc_nr), clksrc_src); \ omap2_clocksource_init((clksrc_nr), clksrc_src); \
} }
...@@ -483,20 +551,23 @@ struct sys_timer omap##name##_timer = { \ ...@@ -483,20 +551,23 @@ struct sys_timer omap##name##_timer = { \
}; };
#ifdef CONFIG_ARCH_OMAP2 #ifdef CONFIG_ARCH_OMAP2
OMAP_SYS_TIMER_INIT(2, 1, OMAP2_CLKEV_SOURCE, 2, OMAP2_MPU_SOURCE) OMAP_SYS_TIMER_INIT(2, 1, OMAP2_CLKEV_SOURCE, "ti,timer-alwon",
2, OMAP2_MPU_SOURCE)
OMAP_SYS_TIMER(2) OMAP_SYS_TIMER(2)
#endif #endif
#ifdef CONFIG_ARCH_OMAP3 #ifdef CONFIG_ARCH_OMAP3
OMAP_SYS_TIMER_INIT(3, 1, OMAP3_CLKEV_SOURCE, 2, OMAP3_MPU_SOURCE) OMAP_SYS_TIMER_INIT(3, 1, OMAP3_CLKEV_SOURCE, "ti,timer-alwon",
2, OMAP3_MPU_SOURCE)
OMAP_SYS_TIMER(3) OMAP_SYS_TIMER(3)
OMAP_SYS_TIMER_INIT(3_secure, OMAP3_SECURE_TIMER, OMAP3_CLKEV_SOURCE, OMAP_SYS_TIMER_INIT(3_secure, OMAP3_SECURE_TIMER, OMAP3_CLKEV_SOURCE,
2, OMAP3_MPU_SOURCE) TIMER_PROP_SECURE, 2, OMAP3_MPU_SOURCE)
OMAP_SYS_TIMER(3_secure) OMAP_SYS_TIMER(3_secure)
#endif #endif
#ifdef CONFIG_SOC_AM33XX #ifdef CONFIG_SOC_AM33XX
OMAP_SYS_TIMER_INIT(3_am33xx, 1, OMAP4_MPU_SOURCE, 2, OMAP4_MPU_SOURCE) OMAP_SYS_TIMER_INIT(3_am33xx, 1, OMAP4_MPU_SOURCE, "ti,timer-alwon",
2, OMAP4_MPU_SOURCE)
OMAP_SYS_TIMER(3_am33xx) OMAP_SYS_TIMER(3_am33xx)
#endif #endif
...@@ -508,7 +579,7 @@ static DEFINE_TWD_LOCAL_TIMER(twd_local_timer, ...@@ -508,7 +579,7 @@ static DEFINE_TWD_LOCAL_TIMER(twd_local_timer,
static void __init omap4_timer_init(void) static void __init omap4_timer_init(void)
{ {
omap2_gp_clockevent_init(1, OMAP4_CLKEV_SOURCE); omap2_gp_clockevent_init(1, OMAP4_CLKEV_SOURCE, "ti,timer-alwon");
omap2_clocksource_init(2, OMAP4_MPU_SOURCE); omap2_clocksource_init(2, OMAP4_MPU_SOURCE);
#ifdef CONFIG_LOCAL_TIMERS #ifdef CONFIG_LOCAL_TIMERS
/* Local timers are not supprted on OMAP4430 ES1.0 */ /* Local timers are not supprted on OMAP4430 ES1.0 */
...@@ -534,7 +605,7 @@ static void __init omap5_timer_init(void) ...@@ -534,7 +605,7 @@ static void __init omap5_timer_init(void)
{ {
int err; int err;
omap2_gp_clockevent_init(1, OMAP4_CLKEV_SOURCE); omap2_gp_clockevent_init(1, OMAP4_CLKEV_SOURCE, "ti,timer-alwon");
omap2_clocksource_init(2, OMAP4_MPU_SOURCE); omap2_clocksource_init(2, OMAP4_MPU_SOURCE);
realtime_counter_init(); realtime_counter_init();
...@@ -619,6 +690,10 @@ static int __init omap2_dm_timer_init(void) ...@@ -619,6 +690,10 @@ static int __init omap2_dm_timer_init(void)
{ {
int ret; int ret;
/* If dtb is there, the devices will be created dynamically */
if (of_have_populated_dt())
return -ENODEV;
ret = omap_hwmod_for_each_by_class("timer", omap_timer_init, NULL); ret = omap_hwmod_for_each_by_class("timer", omap_timer_init, NULL);
if (unlikely(ret)) { if (unlikely(ret)) {
pr_err("%s: device registration failed.\n", __func__); pr_err("%s: device registration failed.\n", __func__);
......
...@@ -40,6 +40,8 @@ ...@@ -40,6 +40,8 @@
#include <linux/device.h> #include <linux/device.h>
#include <linux/err.h> #include <linux/err.h>
#include <linux/pm_runtime.h> #include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <plat/dmtimer.h> #include <plat/dmtimer.h>
#include <plat/omap-pm.h> #include <plat/omap-pm.h>
...@@ -212,6 +214,13 @@ struct omap_dm_timer *omap_dm_timer_request_specific(int id) ...@@ -212,6 +214,13 @@ struct omap_dm_timer *omap_dm_timer_request_specific(int id)
unsigned long flags; unsigned long flags;
int ret = 0; 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); spin_lock_irqsave(&dm_timer_lock, flags);
list_for_each_entry(t, &omap_timer_list, node) { list_for_each_entry(t, &omap_timer_list, node) {
if (t->pdev->id == id && !t->reserved) { if (t->pdev->id == id && !t->reserved) {
...@@ -466,7 +475,7 @@ int omap_dm_timer_set_source(struct omap_dm_timer *timer, int source) ...@@ -466,7 +475,7 @@ 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 * use the clock framework to set the parent clock. To be removed
* once OMAP1 migrated to using clock framework for dmtimers * 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); return pdata->set_timer_src(timer->pdev, source);
fclk = clk_get(&timer->pdev->dev, "fck"); fclk = clk_get(&timer->pdev->dev, "fck");
...@@ -747,7 +756,7 @@ static int __devinit omap_dm_timer_probe(struct platform_device *pdev) ...@@ -747,7 +756,7 @@ static int __devinit omap_dm_timer_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev; struct device *dev = &pdev->dev;
struct dmtimer_platform_data *pdata = pdev->dev.platform_data; 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__); dev_err(dev, "%s: no platform data.\n", __func__);
return -ENODEV; return -ENODEV;
} }
...@@ -776,11 +785,23 @@ static int __devinit omap_dm_timer_probe(struct platform_device *pdev) ...@@ -776,11 +785,23 @@ static int __devinit omap_dm_timer_probe(struct platform_device *pdev)
return -ENOMEM; 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->capability = pdata->timer_capability;
timer->reserved = omap_dm_timer_reserved_systimer(timer->id);
}
timer->irq = irq->start; timer->irq = irq->start;
timer->reserved = omap_dm_timer_reserved_systimer(timer->id);
timer->pdev = pdev; timer->pdev = pdev;
timer->capability = pdata->timer_capability;
/* Skip pm_runtime_enable for OMAP1 */ /* Skip pm_runtime_enable for OMAP1 */
if (!(timer->capability & OMAP_TIMER_NEEDS_RESET)) { if (!(timer->capability & OMAP_TIMER_NEEDS_RESET)) {
...@@ -820,7 +841,8 @@ static int __devexit omap_dm_timer_remove(struct platform_device *pdev) ...@@ -820,7 +841,8 @@ static int __devexit omap_dm_timer_remove(struct platform_device *pdev)
spin_lock_irqsave(&dm_timer_lock, flags); spin_lock_irqsave(&dm_timer_lock, flags);
list_for_each_entry(timer, &omap_timer_list, node) 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); list_del(&timer->node);
ret = 0; ret = 0;
break; break;
...@@ -830,11 +852,18 @@ static int __devexit omap_dm_timer_remove(struct platform_device *pdev) ...@@ -830,11 +852,18 @@ static int __devexit omap_dm_timer_remove(struct platform_device *pdev)
return ret; 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 = { static struct platform_driver omap_dm_timer_driver = {
.probe = omap_dm_timer_probe, .probe = omap_dm_timer_probe,
.remove = __devexit_p(omap_dm_timer_remove), .remove = __devexit_p(omap_dm_timer_remove),
.driver = { .driver = {
.name = "omap_timer", .name = "omap_timer",
.of_match_table = of_match_ptr(omap_timer_match),
}, },
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment