Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/arm/nvidia/tegra_rtc.c
39483 views
1
/*-
2
* Copyright (c) 2016 Michal Meloun <[email protected]>
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
* SUCH DAMAGE.
25
*/
26
27
#include <sys/cdefs.h>
28
/*
29
* RTC driver for Tegra SoCs.
30
*/
31
#include <sys/param.h>
32
#include <sys/systm.h>
33
#include <sys/bus.h>
34
#include <sys/clock.h>
35
#include <sys/kernel.h>
36
#include <sys/limits.h>
37
#include <sys/lock.h>
38
#include <sys/mutex.h>
39
#include <sys/module.h>
40
#include <sys/resource.h>
41
42
#include <machine/bus.h>
43
#include <machine/resource.h>
44
#include <sys/rman.h>
45
46
#include <dev/clk/clk.h>
47
#include <dev/ofw/ofw_bus.h>
48
#include <dev/ofw/ofw_bus_subr.h>
49
50
#include "clock_if.h"
51
52
#define RTC_CONTROL 0x00
53
#define RTC_BUSY 0x04
54
#define RTC_BUSY_STATUS (1 << 0)
55
#define RTC_SECONDS 0x08
56
#define RTC_SHADOW_SECONDS 0x0c
57
#define RTC_MILLI_SECONDS 0x10
58
#define RTC_SECONDS_ALARM0 0x14
59
#define RTC_SECONDS_ALARM1 0x18
60
#define RTC_MILLI_SECONDS_ALARM 0x1c
61
#define RTC_SECONDS_COUNTDOWN_ALARM 0x20
62
#define RTC_MILLI_SECONDS_COUNTDOW_ALARM 0x24
63
#define RTC_INTR_MASK 0x28
64
#define RTC_INTR_MSEC_CDN_ALARM (1 << 4)
65
#define RTC_INTR_SEC_CDN_ALARM (1 << 3)
66
#define RTC_INTR_MSEC_ALARM (1 << 2)
67
#define RTC_INTR_SEC_ALARM1 (1 << 1)
68
#define RTC_INTR_SEC_ALARM0 (1 << 0)
69
70
#define RTC_INTR_STATUS 0x2c
71
#define RTC_INTR_SOURCE 0x30
72
#define RTC_INTR_SET 0x34
73
#define RTC_CORRECTION_FACTOR 0x38
74
75
#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v))
76
#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r))
77
78
#define LOCK(_sc) mtx_lock(&(_sc)->mtx)
79
#define UNLOCK(_sc) mtx_unlock(&(_sc)->mtx)
80
#define SLEEP(_sc, timeout) \
81
mtx_sleep(sc, &sc->mtx, 0, "rtcwait", timeout);
82
#define LOCK_INIT(_sc) \
83
mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_rtc", MTX_DEF)
84
#define LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx)
85
#define ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED)
86
#define ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED)
87
88
static struct ofw_compat_data compat_data[] = {
89
{"nvidia,tegra124-rtc", 1},
90
{NULL, 0}
91
};
92
93
struct tegra_rtc_softc {
94
device_t dev;
95
struct mtx mtx;
96
97
struct resource *mem_res;
98
struct resource *irq_res;
99
void *irq_h;
100
101
clk_t clk;
102
uint32_t core_freq;
103
};
104
105
static void
106
tegra_rtc_wait(struct tegra_rtc_softc *sc)
107
{
108
int timeout;
109
110
for (timeout = 500; timeout >0; timeout--) {
111
if ((RD4(sc, RTC_BUSY) & RTC_BUSY_STATUS) == 0)
112
break;
113
DELAY(1);
114
}
115
if (timeout <= 0)
116
device_printf(sc->dev, "Device busy timeouted\n");
117
118
}
119
120
/*
121
* Get the time of day clock and return it in ts.
122
* Return 0 on success, an error number otherwise.
123
*/
124
static int
125
tegra_rtc_gettime(device_t dev, struct timespec *ts)
126
{
127
struct tegra_rtc_softc *sc;
128
struct timeval tv;
129
uint32_t msec, sec;
130
131
sc = device_get_softc(dev);
132
133
LOCK(sc);
134
msec = RD4(sc, RTC_MILLI_SECONDS);
135
sec = RD4(sc, RTC_SHADOW_SECONDS);
136
UNLOCK(sc);
137
tv.tv_sec = sec;
138
tv.tv_usec = msec * 1000;
139
TIMEVAL_TO_TIMESPEC(&tv, ts);
140
return (0);
141
}
142
143
static int
144
tegra_rtc_settime(device_t dev, struct timespec *ts)
145
{
146
struct tegra_rtc_softc *sc;
147
struct timeval tv;
148
149
sc = device_get_softc(dev);
150
151
LOCK(sc);
152
TIMESPEC_TO_TIMEVAL(&tv, ts);
153
tegra_rtc_wait(sc);
154
WR4(sc, RTC_SECONDS, tv.tv_sec);
155
UNLOCK(sc);
156
157
return (0);
158
}
159
160
static void
161
tegra_rtc_intr(void *arg)
162
{
163
struct tegra_rtc_softc *sc;
164
uint32_t status;
165
166
sc = (struct tegra_rtc_softc *)arg;
167
LOCK(sc);
168
status = RD4(sc, RTC_INTR_STATUS);
169
WR4(sc, RTC_INTR_STATUS, status);
170
UNLOCK(sc);
171
}
172
173
static int
174
tegra_rtc_probe(device_t dev)
175
{
176
if (!ofw_bus_status_okay(dev))
177
return (ENXIO);
178
179
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
180
return (ENXIO);
181
182
return (BUS_PROBE_DEFAULT);
183
}
184
185
static int
186
tegra_rtc_attach(device_t dev)
187
{
188
int rv, rid;
189
struct tegra_rtc_softc *sc;
190
191
sc = device_get_softc(dev);
192
sc->dev = dev;
193
194
LOCK_INIT(sc);
195
196
/* Get the memory resource for the register mapping. */
197
rid = 0;
198
sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
199
RF_ACTIVE);
200
if (sc->mem_res == NULL) {
201
device_printf(dev, "Cannot map registers.\n");
202
rv = ENXIO;
203
goto fail;
204
}
205
206
/* Allocate our IRQ resource. */
207
rid = 0;
208
sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
209
RF_ACTIVE);
210
if (sc->irq_res == NULL) {
211
device_printf(dev, "Cannot allocate interrupt.\n");
212
rv = ENXIO;
213
goto fail;
214
}
215
216
/* OFW resources. */
217
rv = clk_get_by_ofw_index(dev, 0, 0, &sc->clk);
218
if (rv != 0) {
219
device_printf(dev, "Cannot get i2c clock: %d\n", rv);
220
goto fail;
221
}
222
rv = clk_enable(sc->clk);
223
if (rv != 0) {
224
device_printf(dev, "Cannot enable clock: %d\n", rv);
225
goto fail;
226
}
227
228
/* Init hardware. */
229
WR4(sc, RTC_SECONDS_ALARM0, 0);
230
WR4(sc, RTC_SECONDS_ALARM1, 0);
231
WR4(sc, RTC_INTR_STATUS, 0xFFFFFFFF);
232
WR4(sc, RTC_INTR_MASK, 0);
233
234
/* Setup interrupt */
235
rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
236
NULL, tegra_rtc_intr, sc, &sc->irq_h);
237
if (rv) {
238
device_printf(dev, "Cannot setup interrupt.\n");
239
goto fail;
240
}
241
242
/*
243
* Register as a time of day clock with 1-second resolution.
244
*
245
* XXXX Not yet, we don't have support for multiple RTCs
246
*/
247
/* clock_register(dev, 1000000); */
248
249
bus_attach_children(dev);
250
return (0);
251
252
fail:
253
if (sc->clk != NULL)
254
clk_release(sc->clk);
255
if (sc->irq_h != NULL)
256
bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
257
if (sc->irq_res != NULL)
258
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
259
if (sc->mem_res != NULL)
260
bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
261
LOCK_DESTROY(sc);
262
263
return (rv);
264
}
265
266
static int
267
tegra_rtc_detach(device_t dev)
268
{
269
struct tegra_rtc_softc *sc;
270
int error;
271
272
error = bus_generic_detach(dev);
273
if (error != 0)
274
return (error);
275
276
sc = device_get_softc(dev);
277
if (sc->irq_h != NULL)
278
bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
279
if (sc->irq_res != NULL)
280
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
281
if (sc->mem_res != NULL)
282
bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
283
284
LOCK_DESTROY(sc);
285
return (0);
286
}
287
288
static device_method_t tegra_rtc_methods[] = {
289
/* Device interface */
290
DEVMETHOD(device_probe, tegra_rtc_probe),
291
DEVMETHOD(device_attach, tegra_rtc_attach),
292
DEVMETHOD(device_detach, tegra_rtc_detach),
293
294
/* clock interface */
295
DEVMETHOD(clock_gettime, tegra_rtc_gettime),
296
DEVMETHOD(clock_settime, tegra_rtc_settime),
297
298
DEVMETHOD_END
299
};
300
301
static DEFINE_CLASS_0(rtc, tegra_rtc_driver, tegra_rtc_methods,
302
sizeof(struct tegra_rtc_softc));
303
DRIVER_MODULE(tegra_rtc, simplebus, tegra_rtc_driver, NULL, NULL);
304
305