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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
/*
* 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/clk.h>
#include <linux/err.h>
#define TWD_TIMER_LOAD 0x00
#define TWD_TIMER_COUNTER 0x04
#define TWD_TIMER_CONTROL 0x08
#define TWD_TIMER_INTSTAT 0x0C
#define TWD_TIMER_CONTROL_ENABLE (1 << 0)
#define TWD_TIMER_CONTROL_ONESHOT (0 << 1)
#define TWD_TIMER_CONTROL_PERIODIC (1 << 1)
#define TWD_TIMER_CONTROL_IT_ENABLE (1 << 2)
#define TWD_TIMER_CONTROL_PRESC(x) (((x) & 0xff) << 8)
static __iomem void *twd_base;
static struct clk *twd_clk;
static uint64_t smp_twd_read(void)
{
return ~readl(twd_base + TWD_TIMER_COUNTER);
}
static struct clocksource smp_twd_clksrc = {
.read = smp_twd_read,
.shift = 20,
.mask = CLOCKSOURCE_MASK(32),
};
#define SMP_TWD_MAX_FREQ (25 *1000 * 1000)
static int smp_twd_probe(struct device_d *dev)
{
struct resource *iores;
u32 tick_rate;
u32 val;
int ret;
u32 presc = 0;
twd_clk = clk_get(dev, NULL);
if (IS_ERR(twd_clk)) {
ret = PTR_ERR(twd_clk);
dev_err(dev, "clock not found: %d\n", ret);
return ret;
}
ret = clk_enable(twd_clk);
if (ret < 0) {
dev_err(dev, "clock failed to enable: %d\n", ret);
clk_put(twd_clk);
return ret;
}
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores))
return PTR_ERR(iores);
twd_base = IOMEM(iores->start);
tick_rate = clk_get_rate(twd_clk);
if (tick_rate > SMP_TWD_MAX_FREQ) {
presc = tick_rate / SMP_TWD_MAX_FREQ;
if (presc)
presc--;
presc = min((u32)0xff, presc);
tick_rate /= presc + 1;
}
val = TWD_TIMER_CONTROL_PRESC(presc) |
TWD_TIMER_CONTROL_PERIODIC;
writel(val, twd_base + TWD_TIMER_CONTROL);
writel(0xffffffff, twd_base + TWD_TIMER_LOAD);
val = readl(twd_base + TWD_TIMER_CONTROL);
val |= TWD_TIMER_CONTROL_ENABLE;
writel(val, twd_base + TWD_TIMER_CONTROL);
smp_twd_clksrc.mult = clocksource_hz2mult(tick_rate, smp_twd_clksrc.shift);
return init_clock(&smp_twd_clksrc);
}
static __maybe_unused struct of_device_id smp_twd_compatible[] = {
{
.compatible = "arm,cortex-a9-twd-timer",
}, {
/* sentinel */
}
};
static struct driver_d smp_twd_driver = {
.name = "smp_twd",
.probe = smp_twd_probe,
.of_compatible = DRV_OF_COMPAT(smp_twd_compatible),
};
coredevice_platform_driver(smp_twd_driver);
|