Skip to content

Commit

Permalink
ARM: OMAP: Add DT support for timer driver
Browse files Browse the repository at this point in the history
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: Jon Hunter <jon-hunter@ti.com>
  • Loading branch information
Jon Hunter committed Nov 2, 2012
1 parent 7dd9d50 commit 9725f44
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 42 deletions.
147 changes: 111 additions & 36 deletions arch/arm/mach-omap2/timer.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
#include <linux/clockchips.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>

#include <asm/mach/time.h>
#include <asm/smp_twd.h>
Expand Down Expand Up @@ -66,11 +68,13 @@
#define OMAP3_CLKEV_SOURCE OMAP3_32K_SOURCE
#define OMAP4_CLKEV_SOURCE OMAP4_32K_SOURCE
#define OMAP3_SECURE_TIMER 12
#define TIMER_PROP_SECURE "ti,timer-secure"
#else
#define OMAP2_CLKEV_SOURCE OMAP2_MPU_SOURCE
#define OMAP3_CLKEV_SOURCE OMAP3_MPU_SOURCE
#define OMAP4_CLKEV_SOURCE OMAP4_MPU_SOURCE
#define OMAP3_SECURE_TIMER 1
#define TIMER_PROP_SECURE "ti,timer-alwon"
#endif

#define REALTIME_COUNTER_BASE 0x48243200
Expand Down Expand Up @@ -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
*
Expand All @@ -172,43 +210,74 @@ void __init omap_dmtimer_init(void)

/* If we are a secure device, remove any secure timer nodes */
if ((omap_type() != OMAP2_DEVICE_TYPE_GP)) {
for_each_matching_node(np, omap_timer_match) {
if (of_get_property(np, "ti,timer-secure", NULL))
prom_add_property(np, &device_disabled);
}
np = omap_get_timer_dt(omap_timer_match, "ti,timer-secure");
if (np)
of_node_put(np);
}
}

static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer,
int gptimer_id,
const char *fck_source)
const char *fck_source,
const char *property)
{
char name[10]; /* 10 = sizeof("gptXX_Xck0") */
const char *oh_name;
struct device_node *np;
struct omap_hwmod *oh;
struct resource irq_rsrc, mem_rsrc;
size_t size;
int res = 0;
int r;

sprintf(name, "timer%d", gptimer_id);
omap_hwmod_setup_one(name);
oh = omap_hwmod_lookup(name);
if (of_have_populated_dt()) {
np = omap_get_timer_dt(omap_timer_match, NULL);
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)
return -ENODEV;

r = omap_hwmod_get_resource_byname(oh, IORESOURCE_IRQ, NULL, &irq_rsrc);
if (r)
return -ENXIO;
timer->irq = irq_rsrc.start;

r = omap_hwmod_get_resource_byname(oh, IORESOURCE_MEM, NULL, &mem_rsrc);
if (r)
return -ENXIO;
timer->phys_base = mem_rsrc.start;
size = mem_rsrc.end - mem_rsrc.start;
if (!of_have_populated_dt()) {
r = omap_hwmod_get_resource_byname(oh, IORESOURCE_IRQ, NULL,
&irq_rsrc);
if (r)
return -ENXIO;
timer->irq = irq_rsrc.start;

r = omap_hwmod_get_resource_byname(oh, IORESOURCE_MEM, NULL,
&mem_rsrc);
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)
return -ENXIO;

Expand All @@ -219,9 +288,7 @@ static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer,

omap_hwmod_enable(oh);

if (omap_dm_timer_reserve_systimer(gptimer_id))
return -ENODEV;

/* FIXME: Need to remove hard-coded test on timer ID */
if (gptimer_id != 12) {
struct clk *src;

Expand All @@ -231,8 +298,8 @@ static int __init omap_dm_timer_init_one(struct omap_dm_timer *timer,
} else {
res = __omap_dm_timer_set_source(timer->fclk, src);
if (IS_ERR_VALUE(res))
pr_warning("%s: timer%i cannot set source\n",
__func__, gptimer_id);
pr_warn("%s: %s cannot set source\n",
__func__, oh->name);
clk_put(src);
}
}
Expand All @@ -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,
const char *fck_source)
const char *fck_source,
const char *property)
{
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);

omap2_gp_timer_irq.dev_id = &clkev;
Expand Down Expand Up @@ -356,7 +424,7 @@ static void __init omap2_gptimer_clocksource_init(int gptimer_id,
{
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);

__omap_dm_timer_load_start(&clksrc,
Expand Down Expand Up @@ -468,12 +536,12 @@ static inline void __init realtime_counter_init(void)
{}
#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) \
static void __init omap##name##_timer_init(void) \
{ \
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); \
}

Expand All @@ -483,20 +551,23 @@ struct sys_timer omap##name##_timer = { \
};

#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)
#endif

#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_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)
#endif

#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)
#endif

Expand All @@ -508,7 +579,7 @@ static DEFINE_TWD_LOCAL_TIMER(twd_local_timer,

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);
#ifdef CONFIG_LOCAL_TIMERS
/* Local timers are not supprted on OMAP4430 ES1.0 */
Expand All @@ -534,7 +605,7 @@ static void __init omap5_timer_init(void)
{
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);
realtime_counter_init();

Expand Down Expand Up @@ -619,6 +690,10 @@ static int __init omap2_dm_timer_init(void)
{
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);
if (unlikely(ret)) {
pr_err("%s: device registration failed.\n", __func__);
Expand Down
41 changes: 35 additions & 6 deletions arch/arm/plat-omap/dmtimer.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@
#include <linux/device.h>
#include <linux/err.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/of_device.h>

#include <plat/dmtimer.h>
#include <plat/omap-pm.h>
Expand Down Expand Up @@ -212,6 +214,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) {
Expand Down Expand Up @@ -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
* 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");
Expand Down Expand Up @@ -747,7 +756,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;
}
Expand Down Expand Up @@ -776,11 +785,23 @@ 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->capability = pdata->timer_capability;
timer->reserved = omap_dm_timer_reserved_systimer(timer->id);
}

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)) {
Expand Down Expand Up @@ -820,7 +841,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;
Expand All @@ -830,11 +852,18 @@ 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),
.driver = {
.name = "omap_timer",
.of_match_table = of_match_ptr(omap_timer_match),
},
};

Expand Down

0 comments on commit 9725f44

Please sign in to comment.