static int __init sun5i_setup_clockevent()

in timer-sun5i.c [264:335]


static int __init sun5i_setup_clockevent(struct device_node *node, void __iomem *base,
					 struct clk *clk, int irq)
{
	struct sun5i_timer_clkevt *ce;
	unsigned long rate;
	int ret;
	u32 val;

	ce = kzalloc(sizeof(*ce), GFP_KERNEL);
	if (!ce)
		return -ENOMEM;

	ret = clk_prepare_enable(clk);
	if (ret) {
		pr_err("Couldn't enable parent clock\n");
		goto err_free;
	}

	rate = clk_get_rate(clk);
	if (!rate) {
		pr_err("Couldn't get parent clock rate\n");
		ret = -EINVAL;
		goto err_disable_clk;
	}

	ce->timer.base = base;
	ce->timer.ticks_per_jiffy = DIV_ROUND_UP(rate, HZ);
	ce->timer.clk = clk;
	ce->timer.clk_rate_cb.notifier_call = sun5i_rate_cb_clkevt;
	ce->timer.clk_rate_cb.next = NULL;

	ret = clk_notifier_register(clk, &ce->timer.clk_rate_cb);
	if (ret) {
		pr_err("Unable to register clock notifier.\n");
		goto err_disable_clk;
	}

	ce->clkevt.name = node->name;
	ce->clkevt.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
	ce->clkevt.set_next_event = sun5i_clkevt_next_event;
	ce->clkevt.set_state_shutdown = sun5i_clkevt_shutdown;
	ce->clkevt.set_state_periodic = sun5i_clkevt_set_periodic;
	ce->clkevt.set_state_oneshot = sun5i_clkevt_set_oneshot;
	ce->clkevt.tick_resume = sun5i_clkevt_shutdown;
	ce->clkevt.rating = 340;
	ce->clkevt.irq = irq;
	ce->clkevt.cpumask = cpu_possible_mask;

	/* Enable timer0 interrupt */
	val = readl(base + TIMER_IRQ_EN_REG);
	writel(val | TIMER_IRQ_EN(0), base + TIMER_IRQ_EN_REG);

	clockevents_config_and_register(&ce->clkevt, rate,
					TIMER_SYNC_TICKS, 0xffffffff);

	ret = request_irq(irq, sun5i_timer_interrupt, IRQF_TIMER | IRQF_IRQPOLL,
			  "sun5i_timer0", ce);
	if (ret) {
		pr_err("Unable to register interrupt\n");
		goto err_remove_notifier;
	}

	return 0;

err_remove_notifier:
	clk_notifier_unregister(clk, &ce->timer.clk_rate_cb);
err_disable_clk:
	clk_disable_unprepare(clk);
err_free:
	kfree(ce);
	return ret;
}