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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
|
/*
* linux/arch/arm/mach-nomadik/timer.c
*
* Copyright (C) 2008 STMicroelectronics
* Copyright (C) 2009 Alessandro Rubini, somewhat based on at91sam926x
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2, as
* published by the Free Software Foundation.
*/
#include <common.h>
#include <clock.h>
#include <io.h>
#include <init.h>
#include <driver.h>
#include <errno.h>
#include <linux/clk.h>
#include <linux/err.h>
/*
* The MTU device hosts four different counters, with 4 set of
* registers. These are register names.
*/
#define MTU_IMSC 0x00 /* Interrupt mask set/clear */
#define MTU_RIS 0x04 /* Raw interrupt status */
#define MTU_MIS 0x08 /* Masked interrupt status */
#define MTU_ICR 0x0C /* Interrupt clear register */
/* per-timer registers take 0..3 as argument */
#define MTU_LR(x) (0x10 + 0x10 * (x) + 0x00) /* Load value */
#define MTU_VAL(x) (0x10 + 0x10 * (x) + 0x04) /* Current value */
#define MTU_CR(x) (0x10 + 0x10 * (x) + 0x08) /* Control reg */
#define MTU_BGLR(x) (0x10 + 0x10 * (x) + 0x0c) /* At next overflow */
/* bits for the control register */
#define MTU_CRn_ENA 0x80
#define MTU_CRn_PERIODIC 0x40 /* if 0 = free-running */
#define MTU_CRn_PRESCALE_MASK 0x0c
#define MTU_CRn_PRESCALE_1 0x00
#define MTU_CRn_PRESCALE_16 0x04
#define MTU_CRn_PRESCALE_256 0x08
#define MTU_CRn_32BITS 0x02
#define MTU_CRn_ONESHOT 0x01 /* if 0 = wraps reloading from BGLR*/
/* Other registers are usual amba/primecell registers, currently not used */
#define MTU_ITCR 0xff0
#define MTU_ITOP 0xff4
#define MTU_PERIPH_ID0 0xfe0
#define MTU_PERIPH_ID1 0xfe4
#define MTU_PERIPH_ID2 0xfe8
#define MTU_PERIPH_ID3 0xfeC
#define MTU_PCELL0 0xff0
#define MTU_PCELL1 0xff4
#define MTU_PCELL2 0xff8
#define MTU_PCELL3 0xffC
static __iomem void *mtu_base;
static u32 clk_prescale;
static u32 nmdk_cycle; /* write-once */
/*
* clocksource: the MTU device is a decrementing counters, so we negate
* the value being read.
*/
static uint64_t nmdk_read_timer(void)
{
return nmdk_cycle - readl(mtu_base + MTU_VAL(0));
}
static struct clocksource nmdk_clksrc = {
.read = nmdk_read_timer,
.shift = 20,
.mask = CLOCKSOURCE_MASK(32),
};
static void nmdk_timer_reset(void)
{
u32 cr;
/* Disable */
writel(0, mtu_base + MTU_CR(0)); /* off */
/* configure load and background-load, and fire it up */
writel(nmdk_cycle, mtu_base + MTU_LR(0));
writel(nmdk_cycle, mtu_base + MTU_BGLR(0));
cr = clk_prescale | MTU_CRn_32BITS;
writel(cr, mtu_base + MTU_CR(0));
writel(cr | MTU_CRn_ENA, mtu_base + MTU_CR(0));
}
static int nmdk_mtu_probe(struct device_d *dev)
{
struct resource *iores;
static struct clk *mtu_clk;
u32 rate;
int ret;
mtu_clk = clk_get(dev, NULL);
if (IS_ERR(mtu_clk)) {
ret = PTR_ERR(mtu_clk);
dev_err(dev, "clock not found: %d\n", ret);
return ret;
}
ret = clk_enable(mtu_clk);
if (ret < 0) {
dev_err(dev, "clock failed to enable: %d\n", ret);
clk_put(mtu_clk);
return ret;
}
rate = clk_get_rate(mtu_clk);
if (rate > 32000000) {
rate /= 16;
clk_prescale = MTU_CRn_PRESCALE_16;
} else {
clk_prescale = MTU_CRn_PRESCALE_1;
}
nmdk_cycle = (rate + 1000 / 2) / 1000;
/* Save global pointer to mtu, used by functions above */
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores))
return PTR_ERR(iores);
mtu_base = IOMEM(iores->start);
/* Init the timer and register clocksource */
nmdk_timer_reset();
nmdk_clksrc.mult = clocksource_hz2mult(rate, nmdk_clksrc.shift);
return init_clock(&nmdk_clksrc);
}
static struct driver_d nmdk_mtu_driver = {
.name = "nomadik_mtu",
.probe = nmdk_mtu_probe,
};
static int nmdk_mtu_init(void)
{
return platform_driver_register(&nmdk_mtu_driver);
}
coredevice_initcall(nmdk_mtu_init);
|