Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/powerpc/powernv/opal_console.c
39534 views
1
/*-
2
* Copyright (C) 2011,2015 by Nathan Whitehorn. All rights reserved.
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 ``AS IS'' AND ANY EXPRESS OR
14
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
15
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
16
* IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
17
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
18
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
19
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
20
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
21
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
22
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23
*/
24
25
#include <sys/cdefs.h>
26
#include <sys/endian.h>
27
#include <sys/param.h>
28
#include <sys/conf.h>
29
#include <sys/cons.h>
30
#include <sys/kdb.h>
31
#include <sys/kernel.h>
32
#include <sys/lock.h>
33
#include <sys/module.h>
34
#include <sys/mutex.h>
35
#include <sys/priv.h>
36
#include <sys/proc.h>
37
#include <sys/systm.h>
38
#include <sys/tty.h>
39
40
#include <vm/vm.h>
41
#include <vm/pmap.h>
42
43
#include <machine/bus.h>
44
45
#include <dev/ofw/openfirm.h>
46
#include <dev/ofw/ofw_bus.h>
47
#include <dev/ofw/ofw_bus_subr.h>
48
#include <dev/uart/uart.h>
49
#include <dev/uart/uart_cpu.h>
50
#include <dev/uart/uart_bus.h>
51
52
#include "opal.h"
53
#include "uart_if.h"
54
55
struct uart_opal_softc {
56
device_t dev;
57
phandle_t node;
58
int vtermid;
59
60
struct tty *tp;
61
struct resource *irqres;
62
int irqrid;
63
struct callout callout;
64
void *sc_icookie;
65
int polltime;
66
67
struct mtx sc_mtx;
68
int protocol;
69
70
char opal_inbuf[16];
71
uint64_t inbuflen;
72
uint8_t outseqno;
73
#if defined(KDB)
74
int alt_break_state;
75
#endif
76
};
77
78
static struct uart_opal_softc *console_sc = NULL;
79
static struct consdev *stdout_cp;
80
81
enum {
82
OPAL_RAW, OPAL_HVSI
83
};
84
85
#define VS_DATA_PACKET_HEADER 0xff
86
#define VS_CONTROL_PACKET_HEADER 0xfe
87
#define VSV_SET_MODEM_CTL 0x01
88
#define VSV_MODEM_CTL_UPDATE 0x02
89
#define VSV_RENEGOTIATE_CONNECTION 0x03
90
#define VS_QUERY_PACKET_HEADER 0xfd
91
#define VSV_SEND_VERSION_NUMBER 0x01
92
#define VSV_SEND_MODEM_CTL_STATUS 0x02
93
#define VS_QUERY_RESPONSE_PACKET_HEADER 0xfc
94
95
static int uart_opal_probe(device_t dev);
96
static int uart_opal_attach(device_t dev);
97
static void uart_opal_intr(void *v);
98
99
static device_method_t uart_opal_methods[] = {
100
/* Device interface */
101
DEVMETHOD(device_probe, uart_opal_probe),
102
DEVMETHOD(device_attach, uart_opal_attach),
103
104
DEVMETHOD_END
105
};
106
107
static driver_t uart_opal_driver = {
108
"uart",
109
uart_opal_methods,
110
sizeof(struct uart_opal_softc),
111
};
112
113
DRIVER_MODULE(uart_opal, opalcons, uart_opal_driver, 0, 0);
114
115
static int uart_opal_getc(struct uart_opal_softc *sc);
116
static cn_probe_t uart_opal_cnprobe;
117
static cn_init_t uart_opal_cninit;
118
static cn_term_t uart_opal_cnterm;
119
static cn_getc_t uart_opal_cngetc;
120
static cn_putc_t uart_opal_cnputc;
121
static cn_grab_t uart_opal_cngrab;
122
static cn_ungrab_t uart_opal_cnungrab;
123
124
CONSOLE_DRIVER(uart_opal);
125
126
static void uart_opal_ttyoutwakeup(struct tty *tp);
127
128
static struct ttydevsw uart_opal_tty_class = {
129
.tsw_flags = TF_INITLOCK|TF_CALLOUT,
130
.tsw_outwakeup = uart_opal_ttyoutwakeup,
131
};
132
133
static struct {
134
char tmpbuf[16];
135
uint64_t size;
136
struct mtx mtx;
137
} opalcons_buffer;
138
139
static void
140
uart_opal_real_map_outbuffer(uint64_t *bufferp, uint64_t *lenp)
141
{
142
143
if (!mtx_initialized(&opalcons_buffer.mtx))
144
mtx_init(&opalcons_buffer.mtx, "uart_opal", NULL,
145
MTX_SPIN | MTX_QUIET | MTX_NOWITNESS);
146
147
if (!pmap_bootstrapped)
148
return;
149
150
mtx_lock_spin(&opalcons_buffer.mtx);
151
152
opalcons_buffer.size = *(uint64_t *)(*lenp) =
153
min(sizeof(opalcons_buffer.tmpbuf), *(uint64_t *)(*lenp));
154
memcpy(opalcons_buffer.tmpbuf, (void *)(*bufferp),
155
*(uint64_t *)(*lenp));
156
*bufferp = (uint64_t)opalcons_buffer.tmpbuf;
157
*lenp = (uint64_t)&opalcons_buffer.size;
158
}
159
160
static void
161
uart_opal_real_unmap_outbuffer(uint64_t *len)
162
{
163
164
if (!pmap_bootstrapped)
165
return;
166
167
mtx_assert(&opalcons_buffer.mtx, MA_OWNED);
168
*len = opalcons_buffer.size;
169
mtx_unlock_spin(&opalcons_buffer.mtx);
170
}
171
172
static int64_t
173
uart_opal_console_write_buffer_space(int vtermid)
174
{
175
int64_t buffer_space_val = 0;
176
vm_paddr_t buffer_space_ptr;
177
178
if (pmap_bootstrapped)
179
buffer_space_ptr = vtophys(&buffer_space_val);
180
else
181
buffer_space_ptr = (vm_paddr_t)&buffer_space_val;
182
183
if (opal_call(OPAL_CONSOLE_WRITE_BUFFER_SPACE, vtermid,
184
buffer_space_ptr) != OPAL_SUCCESS)
185
return (-1);
186
187
return (be64toh(buffer_space_val));
188
}
189
190
static int
191
uart_opal_probe_node(struct uart_opal_softc *sc)
192
{
193
phandle_t node = sc->node;
194
uint32_t reg;
195
char buf[64];
196
197
sc->inbuflen = 0;
198
sc->outseqno = 0;
199
200
if (OF_getprop(node, "device_type", buf, sizeof(buf)) <= 0)
201
return (ENXIO);
202
if (strcmp(buf, "serial") != 0)
203
return (ENXIO);
204
205
reg = -1;
206
OF_getencprop(node, "reg", &reg, sizeof(reg));
207
if (reg == -1)
208
return (ENXIO);
209
sc->vtermid = reg;
210
sc->node = node;
211
212
if (OF_getprop(node, "compatible", buf, sizeof(buf)) <= 0)
213
return (ENXIO);
214
if (strcmp(buf, "ibm,opal-console-raw") == 0) {
215
sc->protocol = OPAL_RAW;
216
return (0);
217
} else if (strcmp(buf, "ibm,opal-console-hvsi") == 0) {
218
sc->protocol = OPAL_HVSI;
219
return (0);
220
}
221
222
return (ENXIO);
223
}
224
225
static int
226
uart_opal_probe(device_t dev)
227
{
228
struct uart_opal_softc sc;
229
int err;
230
231
sc.node = ofw_bus_get_node(dev);
232
err = uart_opal_probe_node(&sc);
233
if (err != 0)
234
return (err);
235
236
device_set_desc(dev, "OPAL Serial Port");
237
238
return (err);
239
}
240
241
static void
242
uart_opal_cnprobe(struct consdev *cp)
243
{
244
char buf[64];
245
phandle_t input, chosen;
246
static struct uart_opal_softc sc;
247
248
if (opal_check() != 0)
249
goto fail;
250
251
if ((chosen = OF_finddevice("/chosen")) == -1)
252
goto fail;
253
254
/* Check if OF has an active stdin/stdout */
255
if (OF_getprop(chosen, "linux,stdout-path", buf, sizeof(buf)) <= 0)
256
goto fail;
257
258
input = OF_finddevice(buf);
259
if (input == -1)
260
goto fail;
261
262
sc.node = input;
263
if (uart_opal_probe_node(&sc) != 0)
264
goto fail;
265
mtx_init(&sc.sc_mtx, "uart_opal", NULL, MTX_SPIN | MTX_QUIET |
266
MTX_NOWITNESS);
267
268
cp->cn_pri = CN_NORMAL;
269
console_sc = &sc;
270
cp->cn_arg = console_sc;
271
stdout_cp = cp;
272
return;
273
274
fail:
275
cp->cn_pri = CN_DEAD;
276
return;
277
}
278
279
static int
280
uart_opal_attach(device_t dev)
281
{
282
struct uart_opal_softc *sc;
283
int unit;
284
285
sc = device_get_softc(dev);
286
sc->node = ofw_bus_get_node(dev);
287
uart_opal_probe_node(sc);
288
289
unit = device_get_unit(dev);
290
mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL,
291
MTX_SPIN | MTX_QUIET | MTX_NOWITNESS);
292
293
if (console_sc != NULL && console_sc->vtermid == sc->vtermid) {
294
device_printf(dev, "console\n");
295
device_set_softc(dev, console_sc);
296
sc = console_sc;
297
sprintf(uart_opal_consdev.cn_name, "ttyu%r", unit);
298
}
299
sc->tp = tty_alloc(&uart_opal_tty_class, sc);
300
301
if (console_sc == sc)
302
tty_init_console(sc->tp, 0);
303
304
sc->dev = dev;
305
sc->irqrid = 0;
306
sc->irqres = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqrid,
307
RF_ACTIVE | RF_SHAREABLE);
308
if (sc->irqres != NULL) {
309
bus_setup_intr(dev, sc->irqres, INTR_TYPE_TTY | INTR_MPSAFE,
310
NULL, uart_opal_intr, sc, &sc->sc_icookie);
311
} else {
312
callout_init(&sc->callout, CALLOUT_MPSAFE);
313
sc->polltime = hz / 20;
314
if (sc->polltime < 1)
315
sc->polltime = 1;
316
callout_reset(&sc->callout, sc->polltime, uart_opal_intr, sc);
317
}
318
319
tty_makedev(sc->tp, NULL, "u%r", unit);
320
321
return (0);
322
}
323
324
static void
325
uart_opal_cninit(struct consdev *cp)
326
{
327
328
strcpy(cp->cn_name, "opalcons");
329
}
330
331
static void
332
uart_opal_cnterm(struct consdev *cp)
333
{
334
}
335
336
static int
337
uart_opal_get(struct uart_opal_softc *sc, void *buffer, size_t bufsize)
338
{
339
int err;
340
int hdr = 0;
341
342
if (sc->protocol == OPAL_RAW) {
343
uint64_t len = htobe64(bufsize);
344
uint64_t olen = (uint64_t)&len;
345
uint64_t obuf = (uint64_t)buffer;
346
347
if (pmap_bootstrapped) {
348
olen = vtophys(&len);
349
obuf = vtophys(buffer);
350
}
351
352
err = opal_call(OPAL_CONSOLE_READ, sc->vtermid, olen, obuf);
353
if (err != OPAL_SUCCESS)
354
return (-1);
355
356
bufsize = be64toh(len);
357
} else {
358
uart_lock(&sc->sc_mtx);
359
if (sc->inbuflen == 0) {
360
err = opal_call(OPAL_CONSOLE_READ, sc->vtermid,
361
&sc->inbuflen, sc->opal_inbuf);
362
if (err != OPAL_SUCCESS) {
363
uart_unlock(&sc->sc_mtx);
364
return (-1);
365
}
366
hdr = 1;
367
sc->inbuflen = be64toh(sc->inbuflen);
368
}
369
370
if (sc->inbuflen == 0) {
371
uart_unlock(&sc->sc_mtx);
372
return (0);
373
}
374
375
if (bufsize > sc->inbuflen)
376
bufsize = sc->inbuflen;
377
378
if (hdr == 1) {
379
sc->inbuflen = sc->inbuflen - 4;
380
/* The HVSI protocol has a 4 byte header, skip it */
381
memmove(&sc->opal_inbuf[0], &sc->opal_inbuf[4],
382
sc->inbuflen);
383
}
384
385
memcpy(buffer, sc->opal_inbuf, bufsize);
386
sc->inbuflen -= bufsize;
387
if (sc->inbuflen > 0)
388
memmove(&sc->opal_inbuf[0], &sc->opal_inbuf[bufsize],
389
sc->inbuflen);
390
391
uart_unlock(&sc->sc_mtx);
392
}
393
394
return (bufsize);
395
}
396
397
static int
398
uart_opal_put(struct uart_opal_softc *sc, void *buffer, size_t bufsize)
399
{
400
uint16_t seqno;
401
uint64_t len;
402
char cbuf[16];
403
int err;
404
uint64_t olen = (uint64_t)&len;
405
uint64_t obuf = (uint64_t)cbuf;
406
407
if (sc->protocol == OPAL_RAW) {
408
obuf = (uint64_t)buffer;
409
len = bufsize;
410
411
uart_opal_real_map_outbuffer(&obuf, &olen);
412
*(uint64_t*)olen = htobe64(*(uint64_t*)olen);
413
err = opal_call(OPAL_CONSOLE_WRITE, sc->vtermid, olen, obuf);
414
*(uint64_t*)olen = be64toh(*(uint64_t*)olen);
415
uart_opal_real_unmap_outbuffer(&len);
416
} else {
417
uart_lock(&sc->sc_mtx);
418
if (bufsize > 12)
419
bufsize = 12;
420
seqno = sc->outseqno++;
421
cbuf[0] = VS_DATA_PACKET_HEADER;
422
cbuf[1] = 4 + bufsize; /* total length */
423
cbuf[2] = (seqno >> 8) & 0xff;
424
cbuf[3] = seqno & 0xff;
425
memcpy(&cbuf[4], buffer, bufsize);
426
len = 4 + bufsize;
427
428
uart_opal_real_map_outbuffer(&obuf, &olen);
429
*(uint64_t*)olen = htobe64(*(uint64_t*)olen);
430
err = opal_call(OPAL_CONSOLE_WRITE, sc->vtermid, olen, obuf);
431
*(uint64_t*)olen = be64toh(*(uint64_t*)olen);
432
uart_opal_real_unmap_outbuffer(&len);
433
434
uart_unlock(&sc->sc_mtx);
435
436
len -= 4;
437
}
438
439
if (err == OPAL_SUCCESS)
440
return (len);
441
else if (err == OPAL_BUSY_EVENT)
442
return(0);
443
444
return (-1);
445
}
446
447
static int
448
uart_opal_cngetc(struct consdev *cp)
449
{
450
return (uart_opal_getc(cp->cn_arg));
451
}
452
453
static int
454
uart_opal_getc(struct uart_opal_softc *sc)
455
{
456
unsigned char c;
457
int retval;
458
459
retval = uart_opal_get(sc, &c, 1);
460
if (retval != 1)
461
return (-1);
462
#if defined(KDB)
463
kdb_alt_break(c, &sc->alt_break_state);
464
#endif
465
466
return (c);
467
}
468
469
static void
470
uart_opal_cnputc(struct consdev *cp, int c)
471
{
472
unsigned char ch = c;
473
int a;
474
475
if (1) {
476
/* Clear FIFO if needed. Must be repeated few times. */
477
for (a = 0; a < 20; a++) {
478
opal_call(OPAL_POLL_EVENTS, NULL);
479
}
480
}
481
uart_opal_put(cp->cn_arg, &ch, 1);
482
}
483
484
static void
485
uart_opal_cngrab(struct consdev *cp)
486
{
487
}
488
489
static void
490
uart_opal_cnungrab(struct consdev *cp)
491
{
492
}
493
494
static void
495
uart_opal_ttyoutwakeup(struct tty *tp)
496
{
497
struct uart_opal_softc *sc;
498
char buffer[8];
499
int len;
500
int64_t buffer_space;
501
502
sc = tty_softc(tp);
503
504
while ((len = ttydisc_getc(tp, buffer, sizeof(buffer))) != 0) {
505
int bytes_written = 0;
506
while (bytes_written == 0) {
507
buffer_space = uart_opal_console_write_buffer_space(sc->vtermid);
508
if (buffer_space == -1)
509
/* OPAL failure or invalid terminal */
510
break;
511
else if (buffer_space >= len)
512
bytes_written = uart_opal_put(sc, buffer, len);
513
514
if (bytes_written == 0)
515
/* OPAL must be busy, poll and retry */
516
opal_call(OPAL_POLL_EVENTS, NULL);
517
else if (bytes_written == -1)
518
/* OPAL failure or invalid terminal */
519
break;
520
}
521
}
522
}
523
524
static void
525
uart_opal_intr(void *v)
526
{
527
struct uart_opal_softc *sc = v;
528
struct tty *tp = sc->tp;
529
int c;
530
531
tty_lock(tp);
532
while ((c = uart_opal_getc(sc)) > 0)
533
ttydisc_rint(tp, c, 0);
534
ttydisc_rint_done(tp);
535
tty_unlock(tp);
536
537
opal_call(OPAL_POLL_EVENTS, NULL);
538
539
if (sc->irqres == NULL)
540
callout_reset(&sc->callout, sc->polltime, uart_opal_intr, sc);
541
}
542
543
static int
544
opalcons_probe(device_t dev)
545
{
546
const char *name;
547
548
name = ofw_bus_get_name(dev);
549
if (name == NULL || strcmp(name, "consoles") != 0)
550
return (ENXIO);
551
552
device_set_desc(dev, "OPAL Consoles");
553
return (BUS_PROBE_SPECIFIC);
554
}
555
556
static int
557
opalcons_attach(device_t dev)
558
{
559
phandle_t child;
560
device_t cdev;
561
struct ofw_bus_devinfo *dinfo;
562
563
for (child = OF_child(ofw_bus_get_node(dev)); child != 0;
564
child = OF_peer(child)) {
565
dinfo = malloc(sizeof(*dinfo), M_DEVBUF, M_WAITOK | M_ZERO);
566
if (ofw_bus_gen_setup_devinfo(dinfo, child) != 0) {
567
free(dinfo, M_DEVBUF);
568
continue;
569
}
570
cdev = device_add_child(dev, NULL, DEVICE_UNIT_ANY);
571
if (cdev == NULL) {
572
device_printf(dev, "<%s>: device_add_child failed\n",
573
dinfo->obd_name);
574
ofw_bus_gen_destroy_devinfo(dinfo);
575
free(dinfo, M_DEVBUF);
576
continue;
577
}
578
device_set_ivars(cdev, dinfo);
579
}
580
581
bus_attach_children(dev);
582
return (0);
583
}
584
585
static const struct ofw_bus_devinfo *
586
opalcons_get_devinfo(device_t dev, device_t child)
587
{
588
return (device_get_ivars(child));
589
}
590
591
static device_method_t opalcons_methods[] = {
592
/* Device interface */
593
DEVMETHOD(device_probe, opalcons_probe),
594
DEVMETHOD(device_attach, opalcons_attach),
595
596
/* ofw_bus interface */
597
DEVMETHOD(ofw_bus_get_devinfo, opalcons_get_devinfo),
598
DEVMETHOD(ofw_bus_get_compat, ofw_bus_gen_get_compat),
599
DEVMETHOD(ofw_bus_get_model, ofw_bus_gen_get_model),
600
DEVMETHOD(ofw_bus_get_name, ofw_bus_gen_get_name),
601
DEVMETHOD(ofw_bus_get_node, ofw_bus_gen_get_node),
602
DEVMETHOD(ofw_bus_get_type, ofw_bus_gen_get_type),
603
604
DEVMETHOD_END
605
};
606
607
static driver_t opalcons_driver = {
608
"opalcons",
609
opalcons_methods,
610
0
611
};
612
613
DRIVER_MODULE(opalcons, opal, opalcons_driver, 0, 0);
614
615