Second batch of AT91 cleanup for 3.18:
- 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 -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQEcBAABAgAGBQJUDaNIAAoJEAf03oE53VmQ6lAH/RyGo8yEbupiRy7OQjc/k+Xo LkklJnnLzNSs42Y2GIRQ8XhlZwgVsoSfCWbgJL3M/YVgPzktETRREygOpYN7BN0w ibj3Y2ftQc/94va+QjoUJKcvP4UQULSczTuU0GeEy6XUoPSdiYZNqNRy/O5XdujK w4rTXBlAuVTRv6fppEPAEKcNH6fUW51D3x4/4qI9w0pzLXbvdS0lAjEo1bbbipp7 0PWJmDtkpeGn1wEdVyNkBY2ASEuRqjtWAL41joYGNtrcy43V79ZEn+TkTonfJ02L /qqRyuhHHk+0zOuBIVRlLw5BzddXaqpRXqClM8qdOkDvbz6eL9CcCNCUF3lHcgo= =Ao6T -----END PGP SIGNATURE----- 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
This commit is contained in:
Коммит
c40c4028f0
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -44,12 +44,13 @@ struct atmel_tcb_config {
|
|||
/**
|
||||
* struct atmel_tc - information about a Timer/Counter Block
|
||||
* @pdev: physical device
|
||||
* @iomem: resource associated with the I/O register
|
||||
* @regs: mapping through which the I/O registers can be accessed
|
||||
* @id: block id
|
||||
* @tcb_config: configuration data from SoC
|
||||
* @irq: irq for each of the three channels
|
||||
* @clk: internal clock source for each of the three channels
|
||||
* @node: list node, for tclib internal use
|
||||
* @allocated: if already used, for tclib internal use
|
||||
*
|
||||
* On some platforms, each TC channel has its own clocks and IRQs,
|
||||
* while on others, all TC channels share the same clock and IRQ.
|
||||
|
@ -61,15 +62,16 @@ struct atmel_tcb_config {
|
|||
*/
|
||||
struct atmel_tc {
|
||||
struct platform_device *pdev;
|
||||
struct resource *iomem;
|
||||
void __iomem *regs;
|
||||
int id;
|
||||
const struct atmel_tcb_config *tcb_config;
|
||||
int irq[3];
|
||||
struct clk *clk[3];
|
||||
struct list_head node;
|
||||
bool allocated;
|
||||
};
|
||||
|
||||
extern struct atmel_tc *atmel_tc_alloc(unsigned block, const char *name);
|
||||
extern struct atmel_tc *atmel_tc_alloc(unsigned block);
|
||||
extern void atmel_tc_free(struct atmel_tc *tc);
|
||||
|
||||
/* platform-specific ATMEL_TC_TIMER_CLOCKx divisors (0 means 32KiHz) */
|
||||
|
@ -258,5 +260,10 @@ extern const u8 atmel_tc_divisors[5];
|
|||
#define ATMEL_TC_LDRAS (1 << 5) /* RA loading */
|
||||
#define ATMEL_TC_LDRBS (1 << 6) /* RB loading */
|
||||
#define ATMEL_TC_ETRGS (1 << 7) /* external trigger */
|
||||
#define ATMEL_TC_ALL_IRQ (ATMEL_TC_COVFS | ATMEL_TC_LOVRS | \
|
||||
ATMEL_TC_CPAS | ATMEL_TC_CPBS | \
|
||||
ATMEL_TC_CPCS | ATMEL_TC_LDRAS | \
|
||||
ATMEL_TC_LDRBS | ATMEL_TC_ETRGS) \
|
||||
/* all IRQs */
|
||||
|
||||
#endif
|
||||
|
|
Загрузка…
Ссылка в новой задаче