xref: /freebsd/sys/powerpc/pseries/phyp_console.c (revision ce3adf4362fcca6a43e500b2531f0038adbfbd21)
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$");
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 
50 struct 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 
73 static struct uart_phyp_softc	*console_sc = NULL;
74 #if defined(KDB)
75 static int			alt_break_state;
76 #endif
77 
78 enum {
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 
92 static int uart_phyp_probe(device_t dev);
93 static int uart_phyp_attach(device_t dev);
94 static void uart_phyp_intr(void *v);
95 
96 static 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 
104 static driver_t uart_phyp_driver = {
105 	"uart",
106 	uart_phyp_methods,
107 	sizeof(struct uart_phyp_softc),
108 };
109 
110 DRIVER_MODULE(uart_phyp, vdevice, uart_phyp_driver, uart_devclass, 0, 0);
111 
112 static cn_probe_t uart_phyp_cnprobe;
113 static cn_init_t uart_phyp_cninit;
114 static cn_term_t uart_phyp_cnterm;
115 static cn_getc_t uart_phyp_cngetc;
116 static cn_putc_t uart_phyp_cnputc;
117 static cn_grab_t uart_phyp_cngrab;
118 static cn_ungrab_t uart_phyp_cnungrab;
119 
120 CONSOLE_DRIVER(uart_phyp);
121 
122 static void uart_phyp_ttyoutwakeup(struct tty *tp);
123 
124 static struct ttydevsw uart_phyp_tty_class = {
125 	.tsw_flags	= TF_INITLOCK|TF_CALLOUT,
126 	.tsw_outwakeup	= uart_phyp_ttyoutwakeup,
127 };
128 
129 static int
130 uart_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_getprop(node, "reg", &reg, sizeof(reg));
151 	if (reg == -1)
152 		return (ENXIO);
153 	sc->node = node;
154 
155 	if (OF_getprop(node, "compatible", buf, sizeof(buf)) <= 0)
156 		return (ENXIO);
157 	if (strcmp(buf, "hvterm1") == 0) {
158 		sc->protocol = HVTERM1;
159 		return (0);
160 	} else if (strcmp(buf, "hvterm-protocol") == 0) {
161 		sc->protocol = HVTERMPROT;
162 		return (0);
163 	}
164 
165 	return (ENXIO);
166 }
167 
168 static int
169 uart_phyp_probe(device_t dev)
170 {
171 	const char *name;
172 	struct uart_phyp_softc sc;
173 	int err;
174 
175 	name = ofw_bus_get_name(dev);
176 	if (name == NULL || strcmp(name, "vty") != 0)
177 		return (ENXIO);
178 
179 	sc.node = ofw_bus_get_node(dev);
180 	err = uart_phyp_probe_node(&sc);
181 	if (err != 0)
182 		return (err);
183 
184 	device_set_desc(dev, "POWER Hypervisor Virtual Serial Port");
185 
186 	return (err);
187 }
188 
189 static void
190 uart_phyp_cnprobe(struct consdev *cp)
191 {
192 	char buf[64];
193 	ihandle_t stdout;
194 	phandle_t input, opts, chosen;
195 	static struct uart_phyp_softc sc;
196 
197 	if ((opts = OF_finddevice("/options")) == -1)
198 		goto fail;
199 	if ((chosen = OF_finddevice("/chosen")) == -1)
200 		goto fail;
201 
202 	/* Check if OF has an active stdin/stdout */
203 	input = -1;
204 	if (OF_getprop(chosen, "stdout", &stdout,
205 	    sizeof(stdout)) == sizeof(stdout) && stdout != 0)
206 		input = OF_instance_to_package(stdout);
207 	if (input == -1)
208 		goto fail;
209 
210 	if (OF_getprop(input, "device_type", buf, sizeof(buf)) == -1)
211 		goto fail;
212 	if (strcmp(buf, "serial") != 0)
213 		goto fail;
214 
215 	sc.node = input;
216 	if (uart_phyp_probe_node(&sc) != 0)
217 		goto fail;
218 	mtx_init(&sc.sc_mtx, "uart_phyp", NULL, MTX_SPIN | MTX_QUIET |
219 	    MTX_NOWITNESS);
220 
221 	cp->cn_pri = CN_NORMAL;
222 	console_sc = &sc;
223 	return;
224 
225 fail:
226 	cp->cn_pri = CN_DEAD;
227 	return;
228 }
229 
230 static int
231 uart_phyp_attach(device_t dev)
232 {
233 	struct uart_phyp_softc *sc;
234 	int unit;
235 
236 	sc = device_get_softc(dev);
237 	sc->dev = dev;
238 	sc->node = ofw_bus_get_node(dev);
239 	uart_phyp_probe_node(sc);
240 
241 	unit = device_get_unit(dev);
242 	sc->tp = tty_alloc(&uart_phyp_tty_class, sc);
243 	mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL,
244 	    MTX_SPIN | MTX_QUIET | MTX_NOWITNESS);
245 
246 	if (console_sc != NULL && console_sc->vtermid == sc->vtermid) {
247 		sc->outseqno = console_sc->outseqno;
248 		console_sc = sc;
249 		sprintf(uart_phyp_consdev.cn_name, "ttyu%r", unit);
250 		tty_init_console(sc->tp, 0);
251 	}
252 
253 	sc->irqrid = 0;
254 #ifdef NOTYET
255 	sc->irqres = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->irqrid,
256 	    RF_ACTIVE | RF_SHAREABLE);
257 #else
258 	sc->irqres = NULL;
259 #endif
260 	if (sc->irqres != NULL) {
261 		bus_setup_intr(dev, sc->irqres, INTR_TYPE_TTY | INTR_MPSAFE,
262 		    NULL, uart_phyp_intr, sc, &sc->sc_icookie);
263 	} else {
264 		callout_init(&sc->callout, CALLOUT_MPSAFE);
265 		sc->polltime = hz / 20;
266 		if (sc->polltime < 1)
267 			sc->polltime = 1;
268 		callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc);
269 	}
270 
271 	tty_makedev(sc->tp, NULL, "u%r", unit);
272 
273 	return (0);
274 }
275 
276 static void
277 uart_phyp_cninit(struct consdev *cp)
278 {
279 
280 	strcpy(cp->cn_name, "phypcons");
281 }
282 
283 static void
284 uart_phyp_cnterm(struct consdev *cp)
285 {
286 }
287 
288 static int
289 uart_phyp_get(struct uart_phyp_softc *sc, void *buffer, size_t bufsize)
290 {
291 	int err;
292 
293 	uart_lock(&sc->sc_mtx);
294 	if (sc->inbuflen == 0) {
295 		err = phyp_pft_hcall(H_GET_TERM_CHAR, sc->vtermid,
296 		    0, 0, 0, &sc->inbuflen, &sc->phyp_inbuf.u64[0],
297 		    &sc->phyp_inbuf.u64[1]);
298 		if (err != H_SUCCESS) {
299 			uart_unlock(&sc->sc_mtx);
300 			return (-1);
301 		}
302 	}
303 
304 	if (sc->inbuflen == 0) {
305 		uart_unlock(&sc->sc_mtx);
306 		return (0);
307 	}
308 
309 	if (bufsize > sc->inbuflen)
310 		bufsize = sc->inbuflen;
311 	memcpy(buffer, sc->phyp_inbuf.str, bufsize);
312 	sc->inbuflen -= bufsize;
313 	if (sc->inbuflen > 0)
314 		memmove(&sc->phyp_inbuf.str[0], &sc->phyp_inbuf.str[bufsize],
315 		    sc->inbuflen);
316 
317 	uart_unlock(&sc->sc_mtx);
318 	return (bufsize);
319 }
320 
321 static int
322 uart_phyp_put(struct uart_phyp_softc *sc, void *buffer, size_t bufsize)
323 {
324 	uint16_t seqno;
325 	uint64_t len = 0;
326 	union {
327 		uint64_t u64;
328 		char bytes[8];
329 	} cbuf;
330 
331 	uart_lock(&sc->sc_mtx);
332 	switch (sc->protocol) {
333 	case HVTERM1:
334 		if (bufsize > 8)
335 			bufsize = 8;
336 		memcpy(&cbuf, buffer, bufsize);
337 		len = bufsize;
338 		break;
339 	case HVTERMPROT:
340 		if (bufsize > 4)
341 			bufsize = 4;
342 		seqno = sc->outseqno++;
343 		cbuf.bytes[0] = VS_DATA_PACKET_HEADER;
344 		cbuf.bytes[1] = 4 + bufsize; /* total length */
345 		cbuf.bytes[2] = (seqno >> 8) & 0xff;
346 		cbuf.bytes[3] = seqno & 0xff;
347 		memcpy(&cbuf.bytes[4], buffer, bufsize);
348 		len = 4 + bufsize;
349 		break;
350 	}
351 	phyp_hcall(H_PUT_TERM_CHAR, sc->vtermid, len, cbuf.u64, 0);
352 	uart_unlock(&sc->sc_mtx);
353 
354 	return (bufsize);
355 }
356 
357 static int
358 uart_phyp_cngetc(struct consdev *cp)
359 {
360 	unsigned char c;
361 	int retval;
362 
363 	retval = uart_phyp_get(console_sc, &c, 1);
364 	if (retval != 1)
365 		return (-1);
366 #if defined(KDB)
367 	kdb_alt_break(c, &alt_break_state);
368 #endif
369 
370 	return (c);
371 }
372 
373 static void
374 uart_phyp_cnputc(struct consdev *cp, int c)
375 {
376 	unsigned char ch = c;
377 	uart_phyp_put(console_sc, &ch, 1);
378 }
379 
380 static void
381 uart_phyp_cngrab(struct consdev *cp)
382 {
383 }
384 
385 static void
386 uart_phyp_cnungrab(struct consdev *cp)
387 {
388 }
389 
390 static void
391 uart_phyp_ttyoutwakeup(struct tty *tp)
392 {
393 	struct uart_phyp_softc *sc;
394 	char buffer[8];
395 	int len;
396 
397 	sc = tty_softc(tp);
398 
399 	while ((len = ttydisc_getc(tp, buffer, sizeof(buffer))) != 0)
400 		uart_phyp_put(sc, buffer, len);
401 }
402 
403 static void
404 uart_phyp_intr(void *v)
405 {
406 	struct uart_phyp_softc *sc = v;
407 	struct tty *tp = sc->tp;
408 	unsigned char c;
409 	int len;
410 
411 	tty_lock(tp);
412 	while ((len = uart_phyp_get(sc, &c, 1)) > 0)
413 		ttydisc_rint(tp, c, 0);
414 	ttydisc_rint_done(tp);
415 	tty_unlock(tp);
416 
417 	if (sc->irqres == NULL)
418 		callout_reset(&sc->callout, sc->polltime, uart_phyp_intr, sc);
419 }
420 
421