]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/powerpc/pseries/phyp_console.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / sys / powerpc / pseries / phyp_console.c
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->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
169 static int
170 uart_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
190 static void
191 uart_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_getprop(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         
224 fail:
225         cp->cn_pri = CN_DEAD;
226         return;
227 }
228
229 static int
230 uart_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, CALLOUT_MPSAFE);
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
271 static void
272 uart_phyp_cninit(struct consdev *cp)
273 {
274
275         strcpy(cp->cn_name, "phypcons");
276 }
277
278 static void
279 uart_phyp_cnterm(struct consdev *cp)
280 {
281 }
282
283 static int
284 uart_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
326 static int
327 uart_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
370 static int
371 uart_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
386 static void
387 uart_phyp_cnputc(struct consdev *cp, int c)
388 {
389         unsigned char ch = c;
390         uart_phyp_put(console_sc, &ch, 1);
391 }
392
393 static void
394 uart_phyp_cngrab(struct consdev *cp)
395 {
396 }
397
398 static void
399 uart_phyp_cnungrab(struct consdev *cp)
400 {
401 }
402
403 static void
404 uart_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
416 static void
417 uart_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