summaryrefslogtreecommitdiffstats
path: root/drivers/clocksource/sh_cmt.c
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2010-02-25 08:37:46 +0100
committerPaul Mundt <lethal@linux-sh.org>2010-02-25 08:37:46 +0100
commitda64c2a8dee66ca03f4f3e15d84be7bedf73db3d (patch)
tree8de9d4de358447a80f731a49a689c84bca42abf5 /drivers/clocksource/sh_cmt.c
parentsh: ms7724: modify scan_timing for KEYSC (diff)
downloadlinux-da64c2a8dee66ca03f4f3e15d84be7bedf73db3d.tar.xz
linux-da64c2a8dee66ca03f4f3e15d84be7bedf73db3d.zip
clocksource: Fix up a registration/IRQ race in the sh drivers.
All of the SH clocksource drivers follow the scheme that the IRQ is setup prior to registering the clockevent. The interrupt handler in the clockevent cases looks to the event handler function pointer being filled in by the registration code, permitting us to get in to situations where asserted IRQs step in to the handler before registration has had a chance to complete and hitting a NULL pointer deref. In practice this is not an issue for most platforms, but some of them with fairly special loaders (or that are chain-loading from another kernel) may enter in to this situation. This fixes up the oops reported by Rafael on hp6xx. Reported-and-tested-by: Rafael Ignacio Zurita <rafaelignacio.zurita@gmail.com> Cc: stable@kernel.org Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'drivers/clocksource/sh_cmt.c')
-rw-r--r--drivers/clocksource/sh_cmt.c32
1 files changed, 19 insertions, 13 deletions
diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c
index 6b3e0c2f33e2..6fe4f7701188 100644
--- a/drivers/clocksource/sh_cmt.c
+++ b/drivers/clocksource/sh_cmt.c
@@ -603,18 +603,13 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev)
p->irqaction.handler = sh_cmt_interrupt;
p->irqaction.dev_id = p;
p->irqaction.flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL;
- ret = setup_irq(irq, &p->irqaction);
- if (ret) {
- pr_err("sh_cmt: failed to request irq %d\n", irq);
- goto err1;
- }
/* get hold of clock */
p->clk = clk_get(&p->pdev->dev, cfg->clk);
if (IS_ERR(p->clk)) {
pr_err("sh_cmt: cannot get clock \"%s\"\n", cfg->clk);
ret = PTR_ERR(p->clk);
- goto err2;
+ goto err1;
}
if (resource_size(res) == 6) {
@@ -627,14 +622,25 @@ static int sh_cmt_setup(struct sh_cmt_priv *p, struct platform_device *pdev)
p->clear_bits = ~0xc000;
}
- return sh_cmt_register(p, cfg->name,
- cfg->clockevent_rating,
- cfg->clocksource_rating);
- err2:
- remove_irq(irq, &p->irqaction);
- err1:
+ ret = sh_cmt_register(p, cfg->name,
+ cfg->clockevent_rating,
+ cfg->clocksource_rating);
+ if (ret) {
+ pr_err("sh_cmt: registration failed\n");
+ goto err1;
+ }
+
+ ret = setup_irq(irq, &p->irqaction);
+ if (ret) {
+ pr_err("sh_cmt: failed to request irq %d\n", irq);
+ goto err1;
+ }
+
+ return 0;
+
+err1:
iounmap(p->mapbase);
- err0:
+err0:
return ret;
}