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
55struct 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
78static struct uart_opal_softc	*console_sc = NULL;
79static struct consdev *stdout_cp;
80
81enum {
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
95static int uart_opal_probe(device_t dev);
96static int uart_opal_attach(device_t dev);
97static void uart_opal_intr(void *v);
98
99static 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
107static driver_t uart_opal_driver = {
108	"uart",
109	uart_opal_methods,
110	sizeof(struct uart_opal_softc),
111};
112
113DRIVER_MODULE(uart_opal, opalcons, uart_opal_driver, 0, 0);
114
115static int uart_opal_getc(struct uart_opal_softc *sc);
116static cn_probe_t uart_opal_cnprobe;
117static cn_init_t uart_opal_cninit;
118static cn_term_t uart_opal_cnterm;
119static cn_getc_t uart_opal_cngetc;
120static cn_putc_t uart_opal_cnputc;
121static cn_grab_t uart_opal_cngrab;
122static cn_ungrab_t uart_opal_cnungrab;
123
124CONSOLE_DRIVER(uart_opal);
125
126static void uart_opal_ttyoutwakeup(struct tty *tp);
127
128static struct ttydevsw uart_opal_tty_class = {
129	.tsw_flags	= TF_INITLOCK|TF_CALLOUT,
130	.tsw_outwakeup	= uart_opal_ttyoutwakeup,
131};
132
133static struct {
134	char tmpbuf[16];
135	uint64_t size;
136	struct mtx mtx;
137} opalcons_buffer;
138
139static void
140uart_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
160static void
161uart_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
172static int64_t
173uart_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
190static int
191uart_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
225static int
226uart_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
241static void
242uart_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
274fail:
275	cp->cn_pri = CN_DEAD;
276	return;
277}
278
279static int
280uart_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
324static void
325uart_opal_cninit(struct consdev *cp)
326{
327
328	strcpy(cp->cn_name, "opalcons");
329}
330
331static void
332uart_opal_cnterm(struct consdev *cp)
333{
334}
335
336static int
337uart_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
397static int
398uart_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
447static int
448uart_opal_cngetc(struct consdev *cp)
449{
450	return (uart_opal_getc(cp->cn_arg));
451}
452
453static int
454uart_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
469static void
470uart_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
484static void
485uart_opal_cngrab(struct consdev *cp)
486{
487}
488
489static void
490uart_opal_cnungrab(struct consdev *cp)
491{
492}
493
494static void
495uart_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
524static void
525uart_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
543static int
544opalcons_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
556static int
557opalcons_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, -1);
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	return (bus_generic_attach(dev));
582}
583
584static const struct ofw_bus_devinfo *
585opalcons_get_devinfo(device_t dev, device_t child)
586{
587        return (device_get_ivars(child));
588}
589
590static device_method_t opalcons_methods[] = {
591	/* Device interface */
592	DEVMETHOD(device_probe,		opalcons_probe),
593	DEVMETHOD(device_attach,	opalcons_attach),
594
595	/* ofw_bus interface */
596	DEVMETHOD(ofw_bus_get_devinfo,	opalcons_get_devinfo),
597	DEVMETHOD(ofw_bus_get_compat,	ofw_bus_gen_get_compat),
598	DEVMETHOD(ofw_bus_get_model,	ofw_bus_gen_get_model),
599	DEVMETHOD(ofw_bus_get_name,	ofw_bus_gen_get_name),
600	DEVMETHOD(ofw_bus_get_node,	ofw_bus_gen_get_node),
601	DEVMETHOD(ofw_bus_get_type,	ofw_bus_gen_get_type),
602
603	DEVMETHOD_END
604};
605
606static driver_t opalcons_driver = {
607        "opalcons",
608        opalcons_methods,
609        0
610};
611
612DRIVER_MODULE(opalcons, opal, opalcons_driver, 0, 0);
613