summaryrefslogtreecommitdiffstats
path: root/drivers/clocksource/amba-sp804.c
blob: c5ad9947cd9678419475e495ba85e3b6863cdf21 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
/*
 * Copyright (C) 2012 Jean-Christophe PLAGNIOL-VILLARD <plagnio@jcrosoft.com>
 *
 * Under GPL v2
 */
#include <common.h>
#include <init.h>
#include <clock.h>
#include <io.h>
#include <driver.h>
#include <errno.h>
#include <linux/amba/sp804.h>
#include <linux/clk.h>
#include <linux/err.h>

#include <asm/hardware/arm_timer.h>

static __iomem void *sp804_base;
static struct clk *sp804_clk;

static uint64_t sp804_read(void)
{
	return ~readl(sp804_base + TIMER_VALUE);
}

static struct clocksource sp804_clksrc = {
	.read	= sp804_read,
	.shift	= 20,
	.mask	= CLOCKSOURCE_MASK(32),
};

static int sp804_probe(struct amba_device *dev, const struct amba_id *id)
{
	u32 tick_rate;
	int ret;

	if (sp804_base) {
		dev_err(&dev->dev, "single instance driver\n");
		return -EBUSY;
	}

	sp804_clk = clk_get(&dev->dev, NULL);
	if (IS_ERR(sp804_clk)) {
		ret = PTR_ERR(sp804_clk);
		dev_err(&dev->dev, "clock not found: %d\n", ret);
		return ret;
	}

	ret = clk_enable(sp804_clk);
	if (ret < 0) {
		dev_err(&dev->dev, "clock failed to enable: %d\n", ret);
		clk_put(sp804_clk);
		return ret;
	}

	sp804_base = amba_get_mem_region(dev);

	tick_rate = clk_get_rate(sp804_clk);

	/* setup timer 0 as free-running clocksource */
	writel(0, sp804_base + TIMER_CTRL);
	writel(0xffffffff, sp804_base + TIMER_LOAD);
	writel(0xffffffff, sp804_base + TIMER_VALUE);
	writel(TIMER_CTRL_32BIT | TIMER_CTRL_ENABLE | TIMER_CTRL_PERIODIC,
		sp804_base + TIMER_CTRL);

	sp804_clksrc.mult = clocksource_hz2mult(tick_rate, sp804_clksrc.shift);

	return init_clock(&sp804_clksrc);
}

static struct amba_id sp804_ids[] = {
	{
		.id	= AMBA_ARM_SP804_ID,
		.mask	= AMBA_ARM_SP804_ID_MASK,
	},
	{ 0, 0 },
};

struct amba_driver sp804_driver = {
	.drv = {
		.name = "sp804",
	},
	.probe		= sp804_probe,
	.id_table	= sp804_ids,
};

static int sp804_init(void)
{
	return amba_driver_register(&sp804_driver);
}
coredevice_initcall(sp804_init);