Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c40c4028 authored by Arnd Bergmann's avatar Arnd Bergmann
Browse files

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: default avatarArnd 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
parents 32dc5ca0 d07a1ecd
Loading
Loading
Loading
Loading
+5 −10
Original line number Diff line number Diff line
@@ -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;
+44 −57
Original line number Diff line number Diff line
@@ -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)
+1 −1
Original line number Diff line number Diff line
@@ -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;
+10 −3
Original line number Diff line number Diff line
@@ -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