diff options
author | Arnd Bergmann <arnd@arndb.de> | 2014-09-09 17:03:17 +0200 |
---|---|---|
committer | Arnd Bergmann <arnd@arndb.de> | 2014-09-09 17:03:17 +0200 |
commit | c40c4028f048a077a8950fd95e5d8901f773a63f (patch) | |
tree | 4d1abd711def0811b3c2af8e3c89322235bb9e10 /drivers | |
parent | Merge tag 'at91-cleanup' of git://github.com/at91linux/linux-at91 into next/c... (diff) | |
parent | clocksource: tcb_clksrc: sanitize IRQ request (diff) | |
download | linux-c40c4028f048a077a8950fd95e5d8901f773a63f.tar.xz linux-c40c4028f048a077a8950fd95e5d8901f773a63f.zip |
Merge tag 'at91-cleanup2' of git://github.com/at91linux/linux-at91 into next/cleanup
Pull "Second batch of AT91 cleanup for 3.18" from Nicolas Ferre:
- Timer Counter (TC) fixup and cleanup:
- fix segmentation fault when kexec-ing a kernel by masking
TC interrupts at shutdown and probe time
- use modern driver model: devm_*, probe function, sanitize IRQ request
Signed-off-by: Arnd Bergmann <arnd@arndb.de>
* tag 'at91-cleanup2' of git://github.com/at91linux/linux-at91:
clocksource: tcb_clksrc: sanitize IRQ request
ARM: at91/tclib: mask interruptions at shutdown and probe
ARM: at91/tclib: move initialization from alloc to probe
ARM: at91/tclib: prefer using of devm_* functions
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/clocksource/tcb_clksrc.c | 15 | ||||
-rw-r--r-- | drivers/misc/atmel_tclib.c | 101 | ||||
-rw-r--r-- | drivers/pwm/pwm-atmel-tcb.c | 2 |
3 files changed, 50 insertions, 68 deletions
diff --git a/drivers/clocksource/tcb_clksrc.c b/drivers/clocksource/tcb_clksrc.c index a8d7ea14f183..8bdbc45c6dad 100644 --- a/drivers/clocksource/tcb_clksrc.c +++ b/drivers/clocksource/tcb_clksrc.c @@ -178,12 +178,6 @@ static irqreturn_t ch2_irq(int irq, void *handle) return IRQ_NONE; } -static struct irqaction tc_irqaction = { - .name = "tc_clkevt", - .flags = IRQF_TIMER, - .handler = ch2_irq, -}; - static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) { int ret; @@ -198,15 +192,16 @@ static int __init setup_clkevents(struct atmel_tc *tc, int clk32k_divisor_idx) clkevt.regs = tc->regs; clkevt.clk = t2_clk; - tc_irqaction.dev_id = &clkevt; timer_clock = clk32k_divisor_idx; clkevt.clkevt.cpumask = cpumask_of(0); - ret = setup_irq(irq, &tc_irqaction); - if (ret) + ret = request_irq(irq, ch2_irq, IRQF_TIMER, "tc_clkevt", &clkevt); + if (ret) { + clk_disable_unprepare(t2_clk); return ret; + } clockevents_config_and_register(&clkevt.clkevt, 32768, 1, 0xffff); @@ -279,7 +274,7 @@ static int __init tcb_clksrc_init(void) int i; int ret; - tc = atmel_tc_alloc(CONFIG_ATMEL_TCB_CLKSRC_BLOCK, clksrc.name); + tc = atmel_tc_alloc(CONFIG_ATMEL_TCB_CLKSRC_BLOCK); if (!tc) { pr_debug("can't alloc TC for clocksource\n"); return -ENODEV; diff --git a/drivers/misc/atmel_tclib.c b/drivers/misc/atmel_tclib.c index c8d8e38d0d8a..0ca05c3ec8d6 100644 --- a/drivers/misc/atmel_tclib.c +++ b/drivers/misc/atmel_tclib.c @@ -35,60 +35,31 @@ static LIST_HEAD(tc_list); /** * atmel_tc_alloc - allocate a specified TC block * @block: which block to allocate - * @name: name to be associated with the iomem resource * * Caller allocates a block. If it is available, a pointer to a * pre-initialized struct atmel_tc is returned. The caller can access * the registers directly through the "regs" field. */ -struct atmel_tc *atmel_tc_alloc(unsigned block, const char *name) +struct atmel_tc *atmel_tc_alloc(unsigned block) { struct atmel_tc *tc; struct platform_device *pdev = NULL; - struct resource *r; - size_t size; spin_lock(&tc_list_lock); list_for_each_entry(tc, &tc_list, node) { - if (tc->pdev->dev.of_node) { - if (of_alias_get_id(tc->pdev->dev.of_node, "tcb") - == block) { - pdev = tc->pdev; - break; - } - } else if (tc->pdev->id == block) { + if (tc->allocated) + continue; + + if ((tc->pdev->dev.of_node && tc->id == block) || + (tc->pdev->id == block)) { pdev = tc->pdev; + tc->allocated = true; break; } } - - if (!pdev || tc->iomem) - goto fail; - - r = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!r) - goto fail; - - size = resource_size(r); - r = request_mem_region(r->start, size, name); - if (!r) - goto fail; - - tc->regs = ioremap(r->start, size); - if (!tc->regs) - goto fail_ioremap; - - tc->iomem = r; - -out: spin_unlock(&tc_list_lock); - return tc; -fail_ioremap: - release_mem_region(r->start, size); -fail: - tc = NULL; - goto out; + return pdev ? tc : NULL; } EXPORT_SYMBOL_GPL(atmel_tc_alloc); @@ -96,19 +67,14 @@ EXPORT_SYMBOL_GPL(atmel_tc_alloc); * atmel_tc_free - release a specified TC block * @tc: Timer/counter block that was returned by atmel_tc_alloc() * - * This reverses the effect of atmel_tc_alloc(), unmapping the I/O - * registers, invalidating the resource returned by that routine and - * making the TC available to other drivers. + * This reverses the effect of atmel_tc_alloc(), invalidating the resource + * returned by that routine and making the TC available to other drivers. */ void atmel_tc_free(struct atmel_tc *tc) { spin_lock(&tc_list_lock); - if (tc->regs) { - iounmap(tc->regs); - release_mem_region(tc->iomem->start, resource_size(tc->iomem)); - tc->regs = NULL; - tc->iomem = NULL; - } + if (tc->allocated) + tc->allocated = false; spin_unlock(&tc_list_lock); } EXPORT_SYMBOL_GPL(atmel_tc_free); @@ -142,25 +108,27 @@ static int __init tc_probe(struct platform_device *pdev) struct atmel_tc *tc; struct clk *clk; int irq; - - if (!platform_get_resource(pdev, IORESOURCE_MEM, 0)) - return -EINVAL; + struct resource *r; + unsigned int i; irq = platform_get_irq(pdev, 0); if (irq < 0) return -EINVAL; - tc = kzalloc(sizeof(struct atmel_tc), GFP_KERNEL); + tc = devm_kzalloc(&pdev->dev, sizeof(struct atmel_tc), GFP_KERNEL); if (!tc) return -ENOMEM; tc->pdev = pdev; - clk = clk_get(&pdev->dev, "t0_clk"); - if (IS_ERR(clk)) { - kfree(tc); - return -EINVAL; - } + clk = devm_clk_get(&pdev->dev, "t0_clk"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + tc->regs = devm_ioremap_resource(&pdev->dev, r); + if (IS_ERR(tc->regs)) + return PTR_ERR(tc->regs); /* Now take SoC information if available */ if (pdev->dev.of_node) { @@ -168,13 +136,17 @@ static int __init tc_probe(struct platform_device *pdev) match = of_match_node(atmel_tcb_dt_ids, pdev->dev.of_node); if (match) tc->tcb_config = match->data; + + tc->id = of_alias_get_id(tc->pdev->dev.of_node, "tcb"); + } else { + tc->id = pdev->id; } tc->clk[0] = clk; - tc->clk[1] = clk_get(&pdev->dev, "t1_clk"); + tc->clk[1] = devm_clk_get(&pdev->dev, "t1_clk"); if (IS_ERR(tc->clk[1])) tc->clk[1] = clk; - tc->clk[2] = clk_get(&pdev->dev, "t2_clk"); + tc->clk[2] = devm_clk_get(&pdev->dev, "t2_clk"); if (IS_ERR(tc->clk[2])) tc->clk[2] = clk; @@ -186,18 +158,33 @@ static int __init tc_probe(struct platform_device *pdev) if (tc->irq[2] < 0) tc->irq[2] = irq; + for (i = 0; i < 3; i++) + writel(ATMEL_TC_ALL_IRQ, tc->regs + ATMEL_TC_REG(i, IDR)); + spin_lock(&tc_list_lock); list_add_tail(&tc->node, &tc_list); spin_unlock(&tc_list_lock); + platform_set_drvdata(pdev, tc); + return 0; } +static void tc_shutdown(struct platform_device *pdev) +{ + int i; + struct atmel_tc *tc = platform_get_drvdata(pdev); + + for (i = 0; i < 3; i++) + writel(ATMEL_TC_ALL_IRQ, tc->regs + ATMEL_TC_REG(i, IDR)); +} + static struct platform_driver tc_driver = { .driver = { .name = "atmel_tcb", .of_match_table = of_match_ptr(atmel_tcb_dt_ids), }, + .shutdown = tc_shutdown, }; static int __init tc_init(void) diff --git a/drivers/pwm/pwm-atmel-tcb.c b/drivers/pwm/pwm-atmel-tcb.c index f3dcd02390f1..d56e5b717431 100644 --- a/drivers/pwm/pwm-atmel-tcb.c +++ b/drivers/pwm/pwm-atmel-tcb.c @@ -379,7 +379,7 @@ static int atmel_tcb_pwm_probe(struct platform_device *pdev) return err; } - tc = atmel_tc_alloc(tcblock, "tcb-pwm"); + tc = atmel_tc_alloc(tcblock); if (tc == NULL) { dev_err(&pdev->dev, "failed to allocate Timer Counter Block\n"); return -ENOMEM; |