Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/arm/allwinner/aw_nmi.c
39507 views
1
/*-
2
* Copyright (c) 2016 Emmanuel Vadot <[email protected]>
3
*
4
* Redistribution and use in source and binary forms, with or without
5
* modification, are permitted provided that the following conditions
6
* are met:
7
* 1. Redistributions of source code must retain the above copyright
8
* notice, this list of conditions and the following disclaimer.
9
* 2. Redistributions in binary form must reproduce the above copyright
10
* notice, this list of conditions and the following disclaimer in the
11
* documentation and/or other materials provided with the distribution.
12
*
13
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23
* SUCH DAMAGE.
24
*/
25
26
#include <sys/cdefs.h>
27
#include "opt_platform.h"
28
29
#include <sys/param.h>
30
#include <sys/systm.h>
31
#include <sys/bus.h>
32
#include <sys/kernel.h>
33
#include <sys/module.h>
34
#include <sys/proc.h>
35
#include <sys/rman.h>
36
#include <machine/bus.h>
37
#include <machine/intr.h>
38
39
#include <dev/fdt/fdt_intr.h>
40
#include <dev/ofw/openfirm.h>
41
#include <dev/ofw/ofw_bus.h>
42
#include <dev/ofw/ofw_bus_subr.h>
43
44
#include "pic_if.h"
45
46
#define NMI_IRQ_CTRL_REG 0x0
47
#define NMI_IRQ_LOW_LEVEL 0x0
48
#define NMI_IRQ_LOW_EDGE 0x1
49
#define NMI_IRQ_HIGH_LEVEL 0x2
50
#define NMI_IRQ_HIGH_EDGE 0x3
51
#define NMI_IRQ_PENDING_REG 0x4
52
#define NMI_IRQ_ACK (1U << 0)
53
#define A20_NMI_IRQ_ENABLE_REG 0x8
54
#define A31_NMI_IRQ_ENABLE_REG 0x34
55
#define NMI_IRQ_ENABLE (1U << 0)
56
57
#define R_NMI_IRQ_CTRL_REG 0x0c
58
#define R_NMI_IRQ_PENDING_REG 0x10
59
#define R_NMI_IRQ_ENABLE_REG 0x40
60
61
#define SC_NMI_READ(_sc, _reg) bus_read_4(_sc->res[0], _reg)
62
#define SC_NMI_WRITE(_sc, _reg, _val) bus_write_4(_sc->res[0], _reg, _val)
63
64
static struct resource_spec aw_nmi_res_spec[] = {
65
{ SYS_RES_MEMORY, 0, RF_ACTIVE },
66
{ SYS_RES_IRQ, 0, RF_ACTIVE },
67
{ -1, 0, 0 }
68
};
69
70
struct aw_nmi_intr {
71
struct intr_irqsrc isrc;
72
u_int irq;
73
enum intr_polarity pol;
74
enum intr_trigger tri;
75
};
76
77
struct aw_nmi_reg_cfg {
78
uint8_t ctrl_reg;
79
uint8_t pending_reg;
80
uint8_t enable_reg;
81
};
82
83
struct aw_nmi_softc {
84
device_t dev;
85
struct resource * res[2];
86
void * intrcookie;
87
struct aw_nmi_intr intr;
88
struct aw_nmi_reg_cfg * cfg;
89
};
90
91
static struct aw_nmi_reg_cfg a20_nmi_cfg = {
92
.ctrl_reg = NMI_IRQ_CTRL_REG,
93
.pending_reg = NMI_IRQ_PENDING_REG,
94
.enable_reg = A20_NMI_IRQ_ENABLE_REG,
95
};
96
97
static struct aw_nmi_reg_cfg a31_nmi_cfg = {
98
.ctrl_reg = NMI_IRQ_CTRL_REG,
99
.pending_reg = NMI_IRQ_PENDING_REG,
100
.enable_reg = A31_NMI_IRQ_ENABLE_REG,
101
};
102
103
static struct aw_nmi_reg_cfg a83t_r_nmi_cfg = {
104
.ctrl_reg = R_NMI_IRQ_CTRL_REG,
105
.pending_reg = R_NMI_IRQ_PENDING_REG,
106
.enable_reg = R_NMI_IRQ_ENABLE_REG,
107
};
108
109
static struct ofw_compat_data compat_data[] = {
110
{"allwinner,sun7i-a20-sc-nmi", (uintptr_t)&a20_nmi_cfg},
111
{"allwinner,sun6i-a31-sc-nmi", (uintptr_t)&a31_nmi_cfg},
112
{"allwinner,sun6i-a31-r-intc", (uintptr_t)&a83t_r_nmi_cfg},
113
{"allwinner,sun8i-a83t-r-intc", (uintptr_t)&a83t_r_nmi_cfg},
114
{NULL, 0},
115
};
116
117
static int
118
aw_nmi_intr(void *arg)
119
{
120
struct aw_nmi_softc *sc;
121
122
sc = arg;
123
124
if (SC_NMI_READ(sc, sc->cfg->pending_reg) == 0) {
125
device_printf(sc->dev, "Spurious interrupt\n");
126
return (FILTER_HANDLED);
127
}
128
129
if (intr_isrc_dispatch(&sc->intr.isrc, curthread->td_intr_frame) != 0) {
130
SC_NMI_WRITE(sc, sc->cfg->enable_reg, ~NMI_IRQ_ENABLE);
131
device_printf(sc->dev, "Stray interrupt, NMI disabled\n");
132
}
133
134
return (FILTER_HANDLED);
135
}
136
137
static void
138
aw_nmi_enable_intr(device_t dev, struct intr_irqsrc *isrc)
139
{
140
struct aw_nmi_softc *sc;
141
142
sc = device_get_softc(dev);
143
144
SC_NMI_WRITE(sc, sc->cfg->enable_reg, NMI_IRQ_ENABLE);
145
}
146
147
static void
148
aw_nmi_disable_intr(device_t dev, struct intr_irqsrc *isrc)
149
{
150
struct aw_nmi_softc *sc;
151
152
sc = device_get_softc(dev);
153
154
SC_NMI_WRITE(sc, sc->cfg->enable_reg, ~NMI_IRQ_ENABLE);
155
}
156
157
static int
158
aw_nmi_map_fdt(device_t dev, u_int ncells, pcell_t *cells, u_int *irqp,
159
enum intr_polarity *polp, enum intr_trigger *trigp)
160
{
161
u_int irq, tripol;
162
enum intr_polarity pol;
163
enum intr_trigger trig;
164
165
if (ncells != 2) {
166
device_printf(dev, "Invalid #interrupt-cells\n");
167
return (EINVAL);
168
}
169
170
irq = cells[0];
171
if (irq != 0) {
172
device_printf(dev, "Controller only support irq 0\n");
173
return (EINVAL);
174
}
175
176
tripol = cells[1];
177
178
switch (tripol) {
179
case FDT_INTR_EDGE_RISING:
180
trig = INTR_TRIGGER_EDGE;
181
pol = INTR_POLARITY_HIGH;
182
break;
183
case FDT_INTR_EDGE_FALLING:
184
trig = INTR_TRIGGER_EDGE;
185
pol = INTR_POLARITY_LOW;
186
break;
187
case FDT_INTR_LEVEL_HIGH:
188
trig = INTR_TRIGGER_LEVEL;
189
pol = INTR_POLARITY_HIGH;
190
break;
191
case FDT_INTR_LEVEL_LOW:
192
trig = INTR_TRIGGER_LEVEL;
193
pol = INTR_POLARITY_LOW;
194
break;
195
default:
196
device_printf(dev, "unsupported trigger/polarity 0x%2x\n",
197
tripol);
198
return (ENOTSUP);
199
}
200
201
*irqp = irq;
202
if (polp != NULL)
203
*polp = pol;
204
if (trigp != NULL)
205
*trigp = trig;
206
return (0);
207
}
208
209
static int
210
aw_nmi_map_intr(device_t dev, struct intr_map_data *data,
211
struct intr_irqsrc **isrcp)
212
{
213
struct intr_map_data_fdt *daf;
214
struct aw_nmi_softc *sc;
215
int error;
216
u_int irq;
217
218
if (data->type != INTR_MAP_DATA_FDT)
219
return (ENOTSUP);
220
221
sc = device_get_softc(dev);
222
daf = (struct intr_map_data_fdt *)data;
223
224
error = aw_nmi_map_fdt(dev, daf->ncells, daf->cells, &irq, NULL, NULL);
225
if (error == 0)
226
*isrcp = &sc->intr.isrc;
227
228
return (error);
229
}
230
231
static int
232
aw_nmi_setup_intr(device_t dev, struct intr_irqsrc *isrc,
233
struct resource *res, struct intr_map_data *data)
234
{
235
struct intr_map_data_fdt *daf;
236
struct aw_nmi_softc *sc;
237
struct aw_nmi_intr *nmi_intr;
238
int error, icfg;
239
u_int irq;
240
enum intr_trigger trig;
241
enum intr_polarity pol;
242
243
/* Get config for interrupt. */
244
if (data == NULL || data->type != INTR_MAP_DATA_FDT)
245
return (ENOTSUP);
246
247
sc = device_get_softc(dev);
248
nmi_intr = (struct aw_nmi_intr *)isrc;
249
daf = (struct intr_map_data_fdt *)data;
250
251
error = aw_nmi_map_fdt(dev, daf->ncells, daf->cells, &irq, &pol, &trig);
252
if (error != 0)
253
return (error);
254
if (nmi_intr->irq != irq)
255
return (EINVAL);
256
257
/* Compare config if this is not first setup. */
258
if (isrc->isrc_handlers != 0) {
259
if (pol != nmi_intr->pol || trig != nmi_intr->tri)
260
return (EINVAL);
261
else
262
return (0);
263
}
264
265
nmi_intr->pol = pol;
266
nmi_intr->tri = trig;
267
268
if (trig == INTR_TRIGGER_LEVEL) {
269
if (pol == INTR_POLARITY_LOW)
270
icfg = NMI_IRQ_LOW_LEVEL;
271
else
272
icfg = NMI_IRQ_HIGH_LEVEL;
273
} else {
274
if (pol == INTR_POLARITY_HIGH)
275
icfg = NMI_IRQ_HIGH_EDGE;
276
else
277
icfg = NMI_IRQ_LOW_EDGE;
278
}
279
280
SC_NMI_WRITE(sc, sc->cfg->ctrl_reg, icfg);
281
282
return (0);
283
}
284
285
static int
286
aw_nmi_teardown_intr(device_t dev, struct intr_irqsrc *isrc,
287
struct resource *res, struct intr_map_data *data)
288
{
289
struct aw_nmi_softc *sc;
290
291
sc = device_get_softc(dev);
292
293
if (isrc->isrc_handlers == 0) {
294
sc->intr.pol = INTR_POLARITY_CONFORM;
295
sc->intr.tri = INTR_TRIGGER_CONFORM;
296
297
SC_NMI_WRITE(sc, sc->cfg->enable_reg, ~NMI_IRQ_ENABLE);
298
}
299
300
return (0);
301
}
302
303
static void
304
aw_nmi_pre_ithread(device_t dev, struct intr_irqsrc *isrc)
305
{
306
struct aw_nmi_softc *sc;
307
308
sc = device_get_softc(dev);
309
aw_nmi_disable_intr(dev, isrc);
310
SC_NMI_WRITE(sc, sc->cfg->pending_reg, NMI_IRQ_ACK);
311
}
312
313
static void
314
aw_nmi_post_ithread(device_t dev, struct intr_irqsrc *isrc)
315
{
316
317
arm_irq_memory_barrier(0);
318
aw_nmi_enable_intr(dev, isrc);
319
}
320
321
static void
322
aw_nmi_post_filter(device_t dev, struct intr_irqsrc *isrc)
323
{
324
struct aw_nmi_softc *sc;
325
326
sc = device_get_softc(dev);
327
328
arm_irq_memory_barrier(0);
329
SC_NMI_WRITE(sc, sc->cfg->pending_reg, NMI_IRQ_ACK);
330
}
331
332
static int
333
aw_nmi_probe(device_t dev)
334
{
335
336
if (!ofw_bus_status_okay(dev))
337
return (ENXIO);
338
339
if (ofw_bus_search_compatible(dev, compat_data)->ocd_data == 0)
340
return (ENXIO);
341
device_set_desc(dev, "Allwinner NMI Controller");
342
return (BUS_PROBE_DEFAULT);
343
}
344
345
static int
346
aw_nmi_attach(device_t dev)
347
{
348
struct aw_nmi_softc *sc;
349
phandle_t xref;
350
351
sc = device_get_softc(dev);
352
sc->dev = dev;
353
sc->cfg = (struct aw_nmi_reg_cfg *)
354
ofw_bus_search_compatible(dev, compat_data)->ocd_data;
355
356
if (bus_alloc_resources(dev, aw_nmi_res_spec, sc->res) != 0) {
357
device_printf(dev, "can't allocate device resources\n");
358
return (ENXIO);
359
}
360
if ((bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC,
361
aw_nmi_intr, NULL, sc, &sc->intrcookie))) {
362
device_printf(dev, "unable to register interrupt handler\n");
363
bus_release_resources(dev, aw_nmi_res_spec, sc->res);
364
return (ENXIO);
365
}
366
367
/* Disable and clear interrupts */
368
SC_NMI_WRITE(sc, sc->cfg->enable_reg, ~NMI_IRQ_ENABLE);
369
SC_NMI_WRITE(sc, sc->cfg->pending_reg, NMI_IRQ_ACK);
370
371
xref = OF_xref_from_node(ofw_bus_get_node(dev));
372
/* Register our isrc */
373
sc->intr.irq = 0;
374
sc->intr.pol = INTR_POLARITY_CONFORM;
375
sc->intr.tri = INTR_TRIGGER_CONFORM;
376
if (intr_isrc_register(&sc->intr.isrc, sc->dev, 0, "%s,%u",
377
device_get_nameunit(sc->dev), sc->intr.irq) != 0)
378
goto error;
379
380
if (intr_pic_register(dev, (intptr_t)xref) == NULL) {
381
device_printf(dev, "could not register pic\n");
382
goto error;
383
}
384
return (0);
385
386
error:
387
bus_teardown_intr(dev, sc->res[1], sc->intrcookie);
388
bus_release_resources(dev, aw_nmi_res_spec, sc->res);
389
return (ENXIO);
390
}
391
392
static device_method_t aw_nmi_methods[] = {
393
DEVMETHOD(device_probe, aw_nmi_probe),
394
DEVMETHOD(device_attach, aw_nmi_attach),
395
396
/* Interrupt controller interface */
397
DEVMETHOD(pic_disable_intr, aw_nmi_disable_intr),
398
DEVMETHOD(pic_enable_intr, aw_nmi_enable_intr),
399
DEVMETHOD(pic_map_intr, aw_nmi_map_intr),
400
DEVMETHOD(pic_setup_intr, aw_nmi_setup_intr),
401
DEVMETHOD(pic_teardown_intr, aw_nmi_teardown_intr),
402
DEVMETHOD(pic_post_filter, aw_nmi_post_filter),
403
DEVMETHOD(pic_post_ithread, aw_nmi_post_ithread),
404
DEVMETHOD(pic_pre_ithread, aw_nmi_pre_ithread),
405
406
{0, 0},
407
};
408
409
static driver_t aw_nmi_driver = {
410
"aw_nmi",
411
aw_nmi_methods,
412
sizeof(struct aw_nmi_softc),
413
};
414
415
EARLY_DRIVER_MODULE(aw_nmi, simplebus, aw_nmi_driver, 0, 0,
416
BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
417
418