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