1/*-
2 * Copyright (C) 2011 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__FBSDID("$FreeBSD: releng/11.0/sys/powerpc/pseries/phyp_console.c 290989 2015-11-17 16:07:43Z nwhitehorn $");
27
28#include <sys/param.h>
29#include <sys/kdb.h>
30#include <sys/kernel.h>
31#include <sys/priv.h>
32#include <sys/systm.h>
33#include <sys/module.h>
34#include <sys/types.h>
35#include <sys/conf.h>
36#include <sys/cons.h>
37#include <sys/tty.h>
38#include <machine/bus.h>
39
40#include <dev/ofw/openfirm.h>
41#include <dev/ofw/ofw_bus.h>
42#include <dev/ofw/ofw_bus_subr.h>
43#include <dev/uart/uart.h>
44#include <dev/uart/uart_cpu.h>
45#include <dev/uart/uart_bus.h>
46
47#include "phyp-hvcall.h"
48#include "uart_if.h"
49
50struct uart_phyp_softc {
51	device_t dev;
52	phandle_t node;
53	int vtermid;
54
55	struct tty *tp;
56	struct resource *irqres;
57	int irqrid;
58	struct callout callout;
59	void *sc_icookie;
60	int polltime;
61
62	struct mtx sc_mtx;
63	int protocol;
64
65	union {
66		uint64_t u64[2];
67		char str[16];
68	} phyp_inbuf;
69	uint64_t inbuflen;
70	uint8_t outseqno;
71};
72
73static struct uart_phyp_softc	*console_sc = NULL;
74#if defined(KDB)
75static int			alt_break_state;
76#endif
77
78enum {
79	HVTERM1, HVTERMPROT
80};
81
82#define VS_DATA_PACKET_HEADER		0xff
83#define VS_CONTROL_PACKET_HEADER	0xfe
84#define  VSV_SET_MODEM_CTL		0x01
85#define  VSV_MODEM_CTL_UPDATE		0x02
86#define  VSV_RENEGOTIATE_CONNECTION	0x03
87#define VS_QUERY_PACKET_HEADER		0xfd
88#define  VSV_SEND_VERSION_NUMBER	0x01
89#define  VSV_SEND_MODEM_CTL_STATUS	0x02
90#define VS_QUERY_RESPONSE_PACKET_HEADER	0xfc
91
92static int uart_phyp_probe(device_t dev);
93static int uart_phyp_attach(device_t dev);
94static void uart_phyp_intr(void *v);
95
96static device_method_t uart_phyp_methods[] = {
97	/* Device interface */
98	DEVMETHOD(device_probe,		uart_phyp_probe),
99	DEVMETHOD(device_attach,	uart_phyp_attach),
100
101	DEVMETHOD_END
102};
103
104static driver_t uart_phyp_driver = {
105	"uart",
106	uart_phyp_methods,
107	sizeof(struct uart_phyp_softc),
108};
109
110DRIVER_MODULE(uart_phyp, vdevice, uart_phyp_driver, uart_devclass, 0, 0);
111
112static cn_probe_t uart_phyp_cnprobe;
113static cn_init_t uart_phyp_cninit;
114static cn_term_t uart_phyp_cnterm;
115static cn_getc_t uart_phyp_cngetc;
116static cn_putc_t uart_phyp_cnputc;
117static cn_grab_t uart_phyp_cngrab;
118static cn_ungrab_t uart_phyp_cnungrab;
119
120CONSOLE_DRIVER(uart_phyp);
121
122static void uart_phyp_ttyoutwakeup(struct tty *tp);
123
124static struct ttydevsw uart_phyp_tty_class = {
125	.tsw_flags	= TF_INITLOCK|TF_CALLOUT,
126	.tsw_outwakeup	= uart_phyp_ttyoutwakeup,
127};
128
129static int
130uart_phyp_probe_node(struct uart_phyp_softc *sc)
131{
132	phandle_t node = sc->node;
133	uint32_t reg;
134	char buf[64];
135
136	sc->inbuflen = 0;
137	sc->outseqno = 0;
138
139	if (OF_getprop(node, "name", buf, sizeof(buf)) <= 0)
140		return (ENXIO);
141	if (strcmp(buf, "vty") != 0)
142		return (ENXIO);
143
144	if (OF_getprop(node, "device_type", buf, sizeof(buf)) <= 0)
145		return (ENXIO);
146	if (strcmp(buf, "serial") != 0)
147		return (ENXIO);
148
149	reg = -1;
150	OF_getencprop(node, "reg", &reg, sizeof(reg));
151	if (reg == -1)
152		return (ENXIO);
153	sc->vtermid = reg;
154	sc->node = node;
155
156	if (OF_getprop(node, "compatible", buf, sizeof(buf)) <= 0)
157		return (ENXIO);
158	if (strcmp(buf, "hvterm1") == 0) {
159		sc->protocol = HVTERM1;
160		return (0);
161	} else if (strcmp(buf, "hvterm-protocol") == 0) {
162		sc->protocol = HVTERMPROT;
163		return (0);
164	}
165
166	return (ENXIO);
167}
168
169static int
170uart_phyp_probe(device_t dev)
171{
172	const char *name;
173	struct uart_phyp_softc sc;
174	int err;
175
176	name = ofw_bus_get_name(dev);
177	if (name == NULL || strcmp(name, "vty") != 0)
178		return (ENXIO);
179
180	sc.node = ofw_bus_get_node(dev);
181	err = uart_phyp_probe_node(&sc);
182	if (err != 0)
183		return (err);
184
185	device_set_desc(dev, "POWER Hypervisor Virtual Serial Port");
186
187	return (err);
188}
189
190static void
191uart_phyp_cnprobe(struct consdev *cp)
192{
193	char buf[64];
194	ihandle_t stdout;
195	phandle_t input, chosen;
196	static struct uart_phyp_softc sc;
197
198	if ((chosen = OF_finddevice("/chosen")) == -1)
199		goto fail;
200
201	/* Check if OF has an active stdin/stdout */
202	input = -1;
203	if (OF_getencprop(chosen, "stdout", &stdout,
204	    sizeof(stdout)) == sizeof(stdout) && stdout != 0)
205		input = OF_instance_to_package(stdout);
206	if (input == -1)
207		goto fail;
208
209	if (OF_getprop(input, "device_type", buf, sizeof(buf)) == -1)
210		goto fail;
211	if (strcmp(buf, "serial") != 0)
212		goto fail;
213
214	sc.node = input;
215	if (uart_phyp_probe_node(&sc) != 0)
216		goto fail;
217	mtx_init(&sc.sc_mtx, "uart_phyp", NULL, MTX_SPIN | MTX_QUIET |
218	    MTX_NOWITNESS);
219
220	cp->cn_pri = CN_NORMAL;
221	console_sc = &sc;
222	return;
223
224fail:
225	cp->cn_pri = CN_DEAD;
226	return;
227}
228
229static int
230uart_phyp_attach(device_t dev)
231{
232	struct uart_phyp_softc *sc;
233	int unit;
234
235	sc = device_get_softc(dev);
236	sc->dev = dev;
237	sc->node = ofw_bus_get_node(dev);
238	uart_phyp_probe_node(sc);
239
240	unit = device_get_unit(dev);
241	sc->tp = tty_alloc(&uart_phyp_tty_class, sc);
242	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL,
243	    MTX_SPIN | MTX_QUIET | MTX_NOWITNESS);
244
245	if (console_sc != NULL && console_sc->vtermid == sc->vtermid) {
246		sc->outseqno = console_sc->outseqno;
247		console_sc = sc;
248		sprintf(uart_phyp_consdev.cn_name, "ttyu%r", unit);
249		tty_init_console(sc->tp, 0);
250	}
251
252	sc->irqrid = 0;
253	sc->irqres = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqrid,
254	    RF_ACTIVE | RF_SHAREABLE);
255	if (sc->irqres != NULL) {
256		bus_setup_intr(dev, sc->irqres, INTR_TYPE_TTY | INTR_MPSAFE,
257		    NULL, uart_phyp_intr, sc, &sc->sc_icookie);
258	} else {
259		callout_init(&sc->callout, 1);
260		sc->polltime = hz / 20;
261		if (sc->polltime < 1)
262			sc->polltime = 1;
263		callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc);
264	}
265
266	tty_makedev(sc->tp, NULL, "u%r", unit);
267
268	return (0);
269}
270
271static void
272uart_phyp_cninit(struct consdev *cp)
273{
274
275	strcpy(cp->cn_name, "phypcons");
276}
277
278static void
279uart_phyp_cnterm(struct consdev *cp)
280{
281}
282
283static int
284uart_phyp_get(struct uart_phyp_softc *sc, void *buffer, size_t bufsize)
285{
286	int err;
287	int hdr = 0;
288
289	uart_lock(&sc->sc_mtx);
290	if (sc->inbuflen == 0) {
291		err = phyp_pft_hcall(H_GET_TERM_CHAR, sc->vtermid,
292		    0, 0, 0, &sc->inbuflen, &sc->phyp_inbuf.u64[0],
293		    &sc->phyp_inbuf.u64[1]);
294		if (err != H_SUCCESS) {
295			uart_unlock(&sc->sc_mtx);
296			return (-1);
297		}
298		hdr = 1;
299	}
300
301	if (sc->inbuflen == 0) {
302		uart_unlock(&sc->sc_mtx);
303		return (0);
304	}
305
306	if (bufsize > sc->inbuflen)
307		bufsize = sc->inbuflen;
308
309	if ((sc->protocol == HVTERMPROT) && (hdr == 1)) {
310		sc->inbuflen = sc->inbuflen - 4;
311		/* The VTERM protocol has a 4 byte header, skip it here. */
312		memmove(&sc->phyp_inbuf.str[0], &sc->phyp_inbuf.str[4],
313		    sc->inbuflen);
314	}
315
316	memcpy(buffer, sc->phyp_inbuf.str, bufsize);
317	sc->inbuflen -= bufsize;
318	if (sc->inbuflen > 0)
319		memmove(&sc->phyp_inbuf.str[0], &sc->phyp_inbuf.str[bufsize],
320		    sc->inbuflen);
321
322	uart_unlock(&sc->sc_mtx);
323	return (bufsize);
324}
325
326static int
327uart_phyp_put(struct uart_phyp_softc *sc, void *buffer, size_t bufsize)
328{
329	uint16_t seqno;
330	uint64_t len = 0;
331	int	err;
332
333	union {
334		uint64_t u64[2];
335		char bytes[16];
336	} cbuf;
337
338	uart_lock(&sc->sc_mtx);
339	switch (sc->protocol) {
340	case HVTERM1:
341		if (bufsize > 16)
342			bufsize = 16;
343		memcpy(&cbuf, buffer, bufsize);
344		len = bufsize;
345		break;
346	case HVTERMPROT:
347		if (bufsize > 12)
348			bufsize = 12;
349		seqno = sc->outseqno++;
350		cbuf.bytes[0] = VS_DATA_PACKET_HEADER;
351		cbuf.bytes[1] = 4 + bufsize; /* total length, max 16 bytes */
352		cbuf.bytes[2] = (seqno >> 8) & 0xff;
353		cbuf.bytes[3] = seqno & 0xff;
354		memcpy(&cbuf.bytes[4], buffer, bufsize);
355		len = 4 + bufsize;
356		break;
357	}
358
359	do {
360	    err = phyp_hcall(H_PUT_TERM_CHAR, sc->vtermid, len, cbuf.u64[0],
361			    cbuf.u64[1]);
362		DELAY(100);
363	} while (err == H_BUSY);
364
365	uart_unlock(&sc->sc_mtx);
366
367	return (bufsize);
368}
369
370static int
371uart_phyp_cngetc(struct consdev *cp)
372{
373	unsigned char c;
374	int retval;
375
376	retval = uart_phyp_get(console_sc, &c, 1);
377	if (retval != 1)
378		return (-1);
379#if defined(KDB)
380	kdb_alt_break(c, &alt_break_state);
381#endif
382
383	return (c);
384}
385
386static void
387uart_phyp_cnputc(struct consdev *cp, int c)
388{
389	unsigned char ch = c;
390	uart_phyp_put(console_sc, &ch, 1);
391}
392
393static void
394uart_phyp_cngrab(struct consdev *cp)
395{
396}
397
398static void
399uart_phyp_cnungrab(struct consdev *cp)
400{
401}
402
403static void
404uart_phyp_ttyoutwakeup(struct tty *tp)
405{
406	struct uart_phyp_softc *sc;
407	char buffer[8];
408	int len;
409
410	sc = tty_softc(tp);
411
412	while ((len = ttydisc_getc(tp, buffer, sizeof(buffer))) != 0)
413		uart_phyp_put(sc, buffer, len);
414}
415
416static void
417uart_phyp_intr(void *v)
418{
419	struct uart_phyp_softc *sc = v;
420	struct tty *tp = sc->tp;
421	unsigned char c;
422	int len;
423
424	tty_lock(tp);
425	while ((len = uart_phyp_get(sc, &c, 1)) > 0)
426		ttydisc_rint(tp, c, 0);
427	ttydisc_rint_done(tp);
428	tty_unlock(tp);
429
430	if (sc->irqres == NULL)
431		callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc);
432}
433
434