Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/arm/nvidia/tegra_mc.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
* Memory controller driver for Tegra SoCs.
30
*/
31
#include <sys/param.h>
32
#include <sys/systm.h>
33
#include <sys/bus.h>
34
#include <sys/kernel.h>
35
#include <sys/limits.h>
36
#include <sys/lock.h>
37
#include <sys/mutex.h>
38
#include <sys/module.h>
39
#include <sys/resource.h>
40
41
#include <machine/bus.h>
42
#include <machine/resource.h>
43
#include <sys/rman.h>
44
45
#include <dev/clk/clk.h>
46
#include <dev/ofw/ofw_bus.h>
47
#include <dev/ofw/ofw_bus_subr.h>
48
49
#include "clock_if.h"
50
51
#define MC_INTSTATUS 0x000
52
#define MC_INTMASK 0x004
53
#define MC_INT_DECERR_MTS (1 << 16)
54
#define MC_INT_SECERR_SEC (1 << 13)
55
#define MC_INT_DECERR_VPR (1 << 12)
56
#define MC_INT_INVALID_APB_ASID_UPDATE (1 << 11)
57
#define MC_INT_INVALID_SMMU_PAGE (1 << 10)
58
#define MC_INT_ARBITRATION_EMEM (1 << 9)
59
#define MC_INT_SECURITY_VIOLATION (1 << 8)
60
#define MC_INT_DECERR_EMEM (1 << 6)
61
#define MC_INT_INT_MASK (MC_INT_DECERR_MTS | \
62
MC_INT_SECERR_SEC | \
63
MC_INT_DECERR_VPR | \
64
MC_INT_INVALID_APB_ASID_UPDATE | \
65
MC_INT_INVALID_SMMU_PAGE | \
66
MC_INT_ARBITRATION_EMEM | \
67
MC_INT_SECURITY_VIOLATION | \
68
MC_INT_DECERR_EMEM)
69
70
#define MC_ERR_STATUS 0x008
71
#define MC_ERR_TYPE(x) (((x) >> 28) & 0x7)
72
#define MC_ERR_TYPE_DECERR_EMEM 2
73
#define MC_ERR_TYPE_SECURITY_TRUSTZONE 3
74
#define MC_ERR_TYPE_SECURITY_CARVEOUT 4
75
#define MC_ERR_TYPE_INVALID_SMMU_PAGE 6
76
#define MC_ERR_INVALID_SMMU_PAGE_READABLE (1 << 27)
77
#define MC_ERR_INVALID_SMMU_PAGE_WRITABLE (1 << 26)
78
#define MC_ERR_INVALID_SMMU_PAGE_NONSECURE (1 << 25)
79
#define MC_ERR_ADR_HI(x) (((x) >> 20) & 0x3)
80
#define MC_ERR_SWAP (1 << 18)
81
#define MC_ERR_SECURITY (1 << 17)
82
#define MC_ERR_RW (1 << 16)
83
#define MC_ERR_ADR1(x) (((x) >> 12) & 0x7)
84
#define MC_ERR_ID(x) (((x) >> 0) & 07F)
85
86
#define MC_ERR_ADDR 0x00C
87
#define MC_EMEM_CFG 0x050
88
#define MC_EMEM_ADR_CFG 0x054
89
#define MC_EMEM_NUMDEV(x) (((x) >> 0 ) & 0x1)
90
91
#define MC_EMEM_ADR_CFG_DEV0 0x058
92
#define MC_EMEM_ADR_CFG_DEV1 0x05C
93
#define EMEM_DEV_DEVSIZE(x) (((x) >> 16) & 0xF)
94
#define EMEM_DEV_BANKWIDTH(x) (((x) >> 8) & 0x3)
95
#define EMEM_DEV_COLWIDTH(x) (((x) >> 8) & 0x3)
96
97
#define WR4(_sc, _r, _v) bus_write_4((_sc)->mem_res, (_r), (_v))
98
#define RD4(_sc, _r) bus_read_4((_sc)->mem_res, (_r))
99
100
#define LOCK(_sc) mtx_lock(&(_sc)->mtx)
101
#define UNLOCK(_sc) mtx_unlock(&(_sc)->mtx)
102
#define SLEEP(_sc, timeout) mtx_sleep(sc, &sc->mtx, 0, "tegra_mc", timeout);
103
#define LOCK_INIT(_sc) \
104
mtx_init(&_sc->mtx, device_get_nameunit(_sc->dev), "tegra_mc", MTX_DEF)
105
#define LOCK_DESTROY(_sc) mtx_destroy(&_sc->mtx)
106
#define ASSERT_LOCKED(_sc) mtx_assert(&_sc->mtx, MA_OWNED)
107
#define ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->mtx, MA_NOTOWNED)
108
109
static struct ofw_compat_data compat_data[] = {
110
{"nvidia,tegra124-mc", 1},
111
{"nvidia,tegra210-mc", 1},
112
{NULL, 0}
113
};
114
115
struct tegra_mc_softc {
116
device_t dev;
117
struct mtx mtx;
118
119
struct resource *mem_res;
120
struct resource *irq_res;
121
void *irq_h;
122
123
clk_t clk;
124
};
125
126
static char *smmu_err_tbl[16] = {
127
"reserved", /* 0 */
128
"reserved", /* 1 */
129
"DRAM decode", /* 2 */
130
"Trustzome Security", /* 3 */
131
"Security carveout", /* 4 */
132
"reserved", /* 5 */
133
"Invalid SMMU page", /* 6 */
134
"reserved", /* 7 */
135
};
136
137
static void
138
tegra_mc_intr(void *arg)
139
{
140
struct tegra_mc_softc *sc;
141
uint32_t stat, err;
142
uint64_t addr;
143
144
sc = (struct tegra_mc_softc *)arg;
145
146
stat = RD4(sc, MC_INTSTATUS);
147
if ((stat & MC_INT_INT_MASK) == 0) {
148
WR4(sc, MC_INTSTATUS, stat);
149
return;
150
}
151
152
device_printf(sc->dev, "Memory Controller Interrupt:\n");
153
if (stat & MC_INT_DECERR_MTS)
154
printf(" - MTS carveout violation\n");
155
if (stat & MC_INT_SECERR_SEC)
156
printf(" - SEC carveout violation\n");
157
if (stat & MC_INT_DECERR_VPR)
158
printf(" - VPR requirements violated\n");
159
if (stat & MC_INT_INVALID_APB_ASID_UPDATE)
160
printf(" - ivalid APB ASID update\n");
161
if (stat & MC_INT_INVALID_SMMU_PAGE)
162
printf(" - SMMU address translation error\n");
163
if (stat & MC_INT_ARBITRATION_EMEM)
164
printf(" - arbitration deadlock-prevention threshold hit\n");
165
if (stat & MC_INT_SECURITY_VIOLATION)
166
printf(" - SMMU address translation security error\n");
167
if (stat & MC_INT_DECERR_EMEM)
168
printf(" - SMMU address decode error\n");
169
170
if ((stat & (MC_INT_INVALID_SMMU_PAGE | MC_INT_SECURITY_VIOLATION |
171
MC_INT_DECERR_EMEM)) != 0) {
172
err = RD4(sc, MC_ERR_STATUS);
173
addr = RD4(sc, MC_ERR_STATUS);
174
addr |= (uint64_t)(MC_ERR_ADR_HI(err)) << 32;
175
printf(" at 0x%012jX [%s %s %s] - %s error.\n",
176
(uintmax_t)addr,
177
stat & MC_ERR_SWAP ? "Swap, " : "",
178
stat & MC_ERR_SECURITY ? "Sec, " : "",
179
stat & MC_ERR_RW ? "Write" : "Read",
180
smmu_err_tbl[MC_ERR_TYPE(err)]);
181
}
182
WR4(sc, MC_INTSTATUS, stat);
183
}
184
185
static void
186
tegra_mc_init_hw(struct tegra_mc_softc *sc)
187
{
188
189
/* Disable and acknowledge all interrupts */
190
WR4(sc, MC_INTMASK, 0);
191
WR4(sc, MC_INTSTATUS, MC_INT_INT_MASK);
192
}
193
194
static int
195
tegra_mc_probe(device_t dev)
196
{
197
if (!ofw_bus_status_okay(dev))
198
return (ENXIO);
199
200
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
201
return (ENXIO);
202
device_set_desc(dev, "Tegra Memory Controller");
203
return (BUS_PROBE_DEFAULT);
204
}
205
206
static int
207
tegra_mc_attach(device_t dev)
208
{
209
int rv, rid;
210
struct tegra_mc_softc *sc;
211
212
sc = device_get_softc(dev);
213
sc->dev = dev;
214
215
LOCK_INIT(sc);
216
217
/* Get the memory resource for the register mapping. */
218
rid = 0;
219
sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
220
RF_ACTIVE);
221
if (sc->mem_res == NULL) {
222
device_printf(dev, "Cannot map registers.\n");
223
rv = ENXIO;
224
goto fail;
225
}
226
227
/* Allocate our IRQ resource. */
228
rid = 0;
229
sc->irq_res = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
230
RF_ACTIVE);
231
if (sc->irq_res == NULL) {
232
device_printf(dev, "Cannot allocate interrupt.\n");
233
rv = ENXIO;
234
goto fail;
235
}
236
237
/* OFW resources. */
238
rv = clk_get_by_ofw_name(dev, 0, "mc", &sc->clk);
239
if (rv != 0) {
240
device_printf(dev, "Cannot get mc clock: %d\n", rv);
241
goto fail;
242
}
243
rv = clk_enable(sc->clk);
244
if (rv != 0) {
245
device_printf(dev, "Cannot enable clock: %d\n", rv);
246
goto fail;
247
}
248
249
/* Init hardware. */
250
tegra_mc_init_hw(sc);
251
252
/* Setup interrupt */
253
rv = bus_setup_intr(dev, sc->irq_res, INTR_TYPE_MISC | INTR_MPSAFE,
254
NULL, tegra_mc_intr, sc, &sc->irq_h);
255
if (rv) {
256
device_printf(dev, "Cannot setup interrupt.\n");
257
goto fail;
258
}
259
260
/* Enable Interrupts */
261
WR4(sc, MC_INTMASK, MC_INT_INT_MASK);
262
263
bus_attach_children(dev);
264
return (0);
265
266
fail:
267
if (sc->clk != NULL)
268
clk_release(sc->clk);
269
if (sc->irq_h != NULL)
270
bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
271
if (sc->irq_res != NULL)
272
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
273
if (sc->mem_res != NULL)
274
bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
275
LOCK_DESTROY(sc);
276
277
return (rv);
278
}
279
280
static int
281
tegra_mc_detach(device_t dev)
282
{
283
struct tegra_mc_softc *sc;
284
int error;
285
286
error = bus_generic_detach(dev);
287
if (error != 0)
288
return (error);
289
290
sc = device_get_softc(dev);
291
if (sc->irq_h != NULL)
292
bus_teardown_intr(dev, sc->irq_res, sc->irq_h);
293
if (sc->irq_res != NULL)
294
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->irq_res);
295
if (sc->mem_res != NULL)
296
bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->mem_res);
297
298
LOCK_DESTROY(sc);
299
return (0);
300
}
301
302
static device_method_t tegra_mc_methods[] = {
303
/* Device interface */
304
DEVMETHOD(device_probe, tegra_mc_probe),
305
DEVMETHOD(device_attach, tegra_mc_attach),
306
DEVMETHOD(device_detach, tegra_mc_detach),
307
308
DEVMETHOD_END
309
};
310
311
static DEFINE_CLASS_0(mc, tegra_mc_driver, tegra_mc_methods,
312
sizeof(struct tegra_mc_softc));
313
DRIVER_MODULE(tegra_mc, simplebus, tegra_mc_driver, NULL, NULL);
314
315