aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/hvc/hvc_vio.c
diff options
context:
space:
mode:
authorBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-05-11 23:46:38 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2011-06-29 03:48:35 -0400
commit4d2bb3f5003617cb42b89faefd0009c505c3abd5 (patch)
tree6c64b00f64d5a3290e89003427cff156c9afa640 /drivers/tty/hvc/hvc_vio.c
parentdd2e356a3dd1fea6d911798044532304c3ef4050 (diff)
powerpc/pseries: Re-implement HVSI as part of hvc_vio
On pseries machines, consoles are provided by the hypervisor using a low level get_chars/put_chars type interface. However, this is really just a transport to the service processor which implements them either as "raw" console (networked consoles, HMC, ...) or as "hvsi" serial ports. The later is a simple packet protocol on top of the raw character interface that is supposed to convey additional "serial port" style semantics. In practice however, all it does is provide a way to read the CD line and set/clear our DTR line, that's it. We currently implement the "raw" protocol as an hvc console backend (/dev/hvcN) and the "hvsi" protocol using a separate tty driver (/dev/hvsi0). However this is quite impractical. The arbitrary difference between the two type of devices has been a major source of user (and distro) confusion. Additionally, there's an additional mini -hvsi implementation in the pseries platform code for our low level debug console and early boot kernel messages, which means code duplication, though that low level variant is impractical as it's incapable of doing the initial protocol negociation to establish the link to the FSP. This essentially replaces the dedicated hvsi driver and the platform udbg code completely by extending the existing hvc_vio backend used in "raw" mode so that: - It now supports HVSI as well - We add support for hvc backend providing tiocm{get,set} - It also provides a udbg interface for early debug and boot console This is overall less code, though this will only be obvious once we remove the old "hvsi" driver, which is still available for now. When the old driver is enabled, the new code still kicks in for the low level udbg console, replacing the old mini implementation in the platform code, it just doesn't provide the higher level "hvc" interface. In addition to producing generally simler code, this has several benefits over our current situation: - The user/distro only has to deal with /dev/hvcN for the hypervisor console, avoiding all sort of confusion that has plagued us in the past - The tty, kernel and low level debug console all use the same code base which supports the full protocol establishment process, thus the console is now available much earlier than it used to be with the old HVSI driver. The kernel console works much earlier and udbg is available much earlier too. Hackers can enable a hard coded very-early debug console as well that works with HVSI (previously that was only supported for the "raw" mode). I've tried to keep the same semantics as hvsi relative to how I react to things like CD changes, with some subtle differences though: - I clear DTR on close if HUPCL is set - Current hvsi triggers a hangup if it detects a up->down transition on CD (you can still open a console with CD down). My new implementation triggers a hangup if the link to the FSP is severed, and severs it upon detecting a up->down transition on CD. Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Diffstat (limited to 'drivers/tty/hvc/hvc_vio.c')
-rw-r--r--drivers/tty/hvc/hvc_vio.c725
1 files changed, 692 insertions, 33 deletions
diff --git a/drivers/tty/hvc/hvc_vio.c b/drivers/tty/hvc/hvc_vio.c
index e6eea148524..d4e0850e805 100644
--- a/drivers/tty/hvc/hvc_vio.c
+++ b/drivers/tty/hvc/hvc_vio.c
@@ -27,15 +27,27 @@
27 * You should have received a copy of the GNU General Public License 27 * You should have received a copy of the GNU General Public License
28 * along with this program; if not, write to the Free Software 28 * along with this program; if not, write to the Free Software
29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 29 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
30 *
31 * TODO:
32 *
33 * - handle error in sending hvsi protocol packets
34 * - retry nego on subsequent sends ?
30 */ 35 */
31 36
37#undef DEBUG
38
32#include <linux/types.h> 39#include <linux/types.h>
33#include <linux/init.h> 40#include <linux/init.h>
41#include <linux/delay.h>
42#include <linux/slab.h>
43#include <linux/console.h>
34 44
35#include <asm/hvconsole.h> 45#include <asm/hvconsole.h>
36#include <asm/vio.h> 46#include <asm/vio.h>
37#include <asm/prom.h> 47#include <asm/prom.h>
38#include <asm/firmware.h> 48#include <asm/firmware.h>
49#include <asm/hvsi.h>
50#include <asm/udbg.h>
39 51
40#include "hvc_console.h" 52#include "hvc_console.h"
41 53
@@ -43,14 +55,47 @@ static const char hvc_driver_name[] = "hvc_console";
43 55
44static struct vio_device_id hvc_driver_table[] __devinitdata = { 56static struct vio_device_id hvc_driver_table[] __devinitdata = {
45 {"serial", "hvterm1"}, 57 {"serial", "hvterm1"},
58#ifndef HVC_OLD_HVSI
59 {"serial", "hvterm-protocol"},
60#endif
46 { "", "" } 61 { "", "" }
47}; 62};
48MODULE_DEVICE_TABLE(vio, hvc_driver_table); 63MODULE_DEVICE_TABLE(vio, hvc_driver_table);
49 64
50static int filtered_get_chars(uint32_t vtermno, char *buf, int count) 65typedef enum hv_protocol {
66 HV_PROTOCOL_RAW,
67 HV_PROTOCOL_HVSI
68} hv_protocol_t;
69
70#define HV_INBUF_SIZE 255
71
72struct hvterm_priv {
73 u32 termno; /* HV term number */
74 hv_protocol_t proto; /* Raw data or HVSI packets */
75 unsigned int inbuf_len; /* Data in input buffer */
76 unsigned char inbuf[HV_INBUF_SIZE];
77 unsigned int inbuf_cur; /* Cursor in input buffer */
78 unsigned int inbuf_pktlen; /* HVSI packet lenght from cursor */
79 atomic_t seqno; /* HVSI packet sequence number */
80 unsigned int opened:1; /* HVSI driver opened */
81 unsigned int established:1; /* HVSI protocol established */
82 unsigned int is_console:1; /* Used as a kernel console device */
83 unsigned int mctrl_update:1; /* HVSI modem control updated */
84 unsigned short mctrl; /* HVSI modem control */
85 struct tty_struct *tty; /* TTY structure */
86};
87static struct hvterm_priv *hvterm_privs[MAX_NR_HVC_CONSOLES];
88
89/* For early boot console */
90static struct hvterm_priv hvterm_priv0;
91
92static int hvterm_raw_get_chars(uint32_t vtermno, char *buf, int count)
51{ 93{
52 unsigned long got; 94 struct hvterm_priv *pv = hvterm_privs[vtermno];
53 int i; 95 unsigned long got, i;
96
97 if (WARN_ON(!pv))
98 return 0;
54 99
55 /* 100 /*
56 * Vio firmware will read up to SIZE_VIO_GET_CHARS at its own discretion 101 * Vio firmware will read up to SIZE_VIO_GET_CHARS at its own discretion
@@ -60,7 +105,7 @@ static int filtered_get_chars(uint32_t vtermno, char *buf, int count)
60 if (count < SIZE_VIO_GET_CHARS) 105 if (count < SIZE_VIO_GET_CHARS)
61 return -EAGAIN; 106 return -EAGAIN;
62 107
63 got = hvc_get_chars(vtermno, buf, count); 108 got = hvc_get_chars(pv->termno, buf, count);
64 109
65 /* 110 /*
66 * Work around a HV bug where it gives us a null 111 * Work around a HV bug where it gives us a null
@@ -70,32 +115,527 @@ static int filtered_get_chars(uint32_t vtermno, char *buf, int count)
70 if (buf[i] == 0 && buf[i-1] == '\r') { 115 if (buf[i] == 0 && buf[i-1] == '\r') {
71 --got; 116 --got;
72 if (i < got) 117 if (i < got)
73 memmove(&buf[i], &buf[i+1], 118 memmove(&buf[i], &buf[i+1], got - i);
74 got - i);
75 } 119 }
76 } 120 }
77 return got; 121 return got;
78} 122}
79 123
80static const struct hv_ops hvc_get_put_ops = { 124static int hvterm_raw_put_chars(uint32_t vtermno, const char *buf, int count)
81 .get_chars = filtered_get_chars, 125{
82 .put_chars = hvc_put_chars, 126 struct hvterm_priv *pv = hvterm_privs[vtermno];
127
128 if (WARN_ON(!pv))
129 return 0;
130
131 return hvc_put_chars(pv->termno, buf, count);
132}
133
134static const struct hv_ops hvterm_raw_ops = {
135 .get_chars = hvterm_raw_get_chars,
136 .put_chars = hvterm_raw_put_chars,
83 .notifier_add = notifier_add_irq, 137 .notifier_add = notifier_add_irq,
84 .notifier_del = notifier_del_irq, 138 .notifier_del = notifier_del_irq,
85 .notifier_hangup = notifier_hangup_irq, 139 .notifier_hangup = notifier_hangup_irq,
86}; 140};
87 141
142static int hvterm_hvsi_send_packet(struct hvterm_priv *pv, struct hvsi_header *packet)
143{
144 packet->seqno = atomic_inc_return(&pv->seqno);
145
146 /* Assumes that always succeeds, works in practice */
147 return hvc_put_chars(pv->termno, (char *)packet, packet->len);
148}
149
150static void hvterm_hvsi_start_handshake(struct hvterm_priv *pv)
151{
152 struct hvsi_query q;
153
154 /* Reset state */
155 pv->established = 0;
156 atomic_set(&pv->seqno, 0);
157
158 pr_devel("HVSI@%x: Handshaking started\n", pv->termno);
159
160 /* Send version query */
161 q.hdr.type = VS_QUERY_PACKET_HEADER;
162 q.hdr.len = sizeof(struct hvsi_query);
163 q.verb = VSV_SEND_VERSION_NUMBER;
164 hvterm_hvsi_send_packet(pv, &q.hdr);
165}
166
167static int hvterm_hvsi_send_close(struct hvterm_priv *pv)
168{
169 struct hvsi_control ctrl;
170
171 pv->established = 0;
172
173 ctrl.hdr.type = VS_CONTROL_PACKET_HEADER;
174 ctrl.hdr.len = sizeof(struct hvsi_control);
175 ctrl.verb = VSV_CLOSE_PROTOCOL;
176 return hvterm_hvsi_send_packet(pv, &ctrl.hdr);
177}
178
179static void hvterm_cd_change(struct hvterm_priv *pv, int cd)
180{
181 if (cd)
182 pv->mctrl |= TIOCM_CD;
183 else {
184 pv->mctrl &= ~TIOCM_CD;
185
186 /* We copy the existing hvsi driver semantics
187 * here which are to trigger a hangup when
188 * we get a carrier loss.
189 * Closing our connection to the server will
190 * do just that.
191 */
192 if (!pv->is_console && pv->opened) {
193 pr_devel("HVSI@%x Carrier lost, hanging up !\n",
194 pv->termno);
195 hvterm_hvsi_send_close(pv);
196 }
197 }
198}
199
200static void hvterm_hvsi_got_control(struct hvterm_priv *pv)
201{
202 struct hvsi_control *pkt = (struct hvsi_control *)pv->inbuf;
203
204 switch (pkt->verb) {
205 case VSV_CLOSE_PROTOCOL:
206 /* We restart the handshaking */
207 hvterm_hvsi_start_handshake(pv);
208 break;
209 case VSV_MODEM_CTL_UPDATE:
210 /* Transition of carrier detect */
211 hvterm_cd_change(pv, pkt->word & HVSI_TSCD);
212 break;
213 }
214}
215
216static void hvterm_hvsi_got_query(struct hvterm_priv *pv)
217{
218 struct hvsi_query *pkt = (struct hvsi_query *)pv->inbuf;
219 struct hvsi_query_response r;
220
221 /* We only handle version queries */
222 if (pkt->verb != VSV_SEND_VERSION_NUMBER)
223 return;
224
225 pr_devel("HVSI@%x: Got version query, sending response...\n",
226 pv->termno);
227
228 /* Send version response */
229 r.hdr.type = VS_QUERY_RESPONSE_PACKET_HEADER;
230 r.hdr.len = sizeof(struct hvsi_query_response);
231 r.verb = VSV_SEND_VERSION_NUMBER;
232 r.u.version = HVSI_VERSION;
233 r.query_seqno = pkt->hdr.seqno;
234 hvterm_hvsi_send_packet(pv, &r.hdr);
235
236 /* Assume protocol is open now */
237 pv->established = 1;
238}
239
240static void hvterm_hvsi_got_response(struct hvterm_priv *pv)
241{
242 struct hvsi_query_response *r = (struct hvsi_query_response *)pv->inbuf;
243
244 switch(r->verb) {
245 case VSV_SEND_MODEM_CTL_STATUS:
246 hvterm_cd_change(pv, r->u.mctrl_word & HVSI_TSCD);
247 pv->mctrl_update = 1;
248 break;
249 }
250}
251
252static int hvterm_hvsi_check_packet(struct hvterm_priv *pv)
253{
254 u8 len, type;
255
256 /* Check header validity. If it's invalid, we ditch
257 * the whole buffer and hope we eventually resync
258 */
259 if (pv->inbuf[0] < 0xfc) {
260 pv->inbuf_len = pv->inbuf_pktlen = 0;
261 return 0;
262 }
263 type = pv->inbuf[0];
264 len = pv->inbuf[1];
265
266 /* Packet incomplete ? */
267 if (pv->inbuf_len < len)
268 return 0;
269
270 pr_devel("HVSI@%x: Got packet type %x len %d bytes:\n",
271 pv->termno, type, len);
272
273 /* We have a packet, yay ! Handle it */
274 switch(type) {
275 case VS_DATA_PACKET_HEADER:
276 pv->inbuf_pktlen = len - 4;
277 pv->inbuf_cur = 4;
278 return 1;
279 case VS_CONTROL_PACKET_HEADER:
280 hvterm_hvsi_got_control(pv);
281 break;
282 case VS_QUERY_PACKET_HEADER:
283 hvterm_hvsi_got_query(pv);
284 break;
285 case VS_QUERY_RESPONSE_PACKET_HEADER:
286 hvterm_hvsi_got_response(pv);
287 break;
288 }
289
290 /* Swallow packet and retry */
291 pv->inbuf_len -= len;
292 memmove(pv->inbuf, &pv->inbuf[len], pv->inbuf_len);
293 return 1;
294}
295
296static int hvterm_hvsi_get_packet(struct hvterm_priv *pv)
297{
298 /* If we have room in the buffer, ask HV for more */
299 if (pv->inbuf_len < HV_INBUF_SIZE)
300 pv->inbuf_len += hvc_get_chars(pv->termno,
301 &pv->inbuf[pv->inbuf_len],
302 HV_INBUF_SIZE - pv->inbuf_len);
303 /*
304 * If we have at least 4 bytes in the buffer, check for
305 * a full packet and retry
306 */
307 if (pv->inbuf_len >= 4)
308 return hvterm_hvsi_check_packet(pv);
309 return 0;
310}
311
312static int hvterm_hvsi_get_chars(uint32_t vtermno, char *buf, int count)
313{
314 struct hvterm_priv *pv = hvterm_privs[vtermno];
315 unsigned int tries, read = 0;
316
317 if (WARN_ON(!pv))
318 return 0;
319
320 /* If we aren't open, dont do anything in order to avoid races
321 * with connection establishment. The hvc core will call this
322 * before we have returned from notifier_add(), and we need to
323 * avoid multiple users playing with the receive buffer
324 */
325 if (!pv->opened)
326 return 0;
327
328 /* We try twice, once with what data we have and once more
329 * after we try to fetch some more from the hypervisor
330 */
331 for (tries = 1; count && tries < 2; tries++) {
332 /* Consume existing data packet */
333 if (pv->inbuf_pktlen) {
334 unsigned int l = min(count, (int)pv->inbuf_pktlen);
335 memcpy(&buf[read], &pv->inbuf[pv->inbuf_cur], l);
336 pv->inbuf_cur += l;
337 pv->inbuf_pktlen -= l;
338 count -= l;
339 read += l;
340 }
341 if (count == 0)
342 break;
343
344 /* Data packet fully consumed, move down remaning data */
345 if (pv->inbuf_cur) {
346 pv->inbuf_len -= pv->inbuf_cur;
347 memmove(pv->inbuf, &pv->inbuf[pv->inbuf_cur], pv->inbuf_len);
348 pv->inbuf_cur = 0;
349 }
350
351 /* Try to get another packet */
352 if (hvterm_hvsi_get_packet(pv))
353 tries--;
354 }
355 if (!pv->established) {
356 pr_devel("HVSI@%x: returning -EPIPE\n", pv->termno);
357 return -EPIPE;
358 }
359 return read;
360}
361
362static int hvterm_hvsi_put_chars(uint32_t vtermno, const char *buf, int count)
363{
364 struct hvterm_priv *pv = hvterm_privs[vtermno];
365 struct hvsi_data dp;
366 int rc, adjcount = min(count, HVSI_MAX_OUTGOING_DATA);
367
368 if (WARN_ON(!pv))
369 return 0;
370
371 dp.hdr.type = VS_DATA_PACKET_HEADER;
372 dp.hdr.len = adjcount + sizeof(struct hvsi_header);
373 memcpy(dp.data, buf, adjcount);
374 rc = hvterm_hvsi_send_packet(pv, &dp.hdr);
375 if (rc <= 0)
376 return rc;
377 return adjcount;
378}
379
380static void maybe_msleep(unsigned long ms)
381{
382 /* During early boot, IRQs are disabled, use mdelay */
383 if (irqs_disabled())
384 mdelay(ms);
385 else
386 msleep(ms);
387}
388
389static int hvterm_hvsi_read_mctrl(struct hvterm_priv *pv)
390{
391 struct hvsi_query q;
392 int rc, timeout;
393
394 pr_devel("HVSI@%x: Querying modem control status...\n",
395 pv->termno);
396
397 pv->mctrl_update = 0;
398 q.hdr.type = VS_QUERY_PACKET_HEADER;
399 q.hdr.len = sizeof(struct hvsi_query);
400 q.hdr.seqno = atomic_inc_return(&pv->seqno);
401 q.verb = VSV_SEND_MODEM_CTL_STATUS;
402 rc = hvterm_hvsi_send_packet(pv, &q.hdr);
403 if (rc <= 0) {
404 pr_devel("HVSI@%x: Error %d...\n", pv->termno, rc);
405 return rc;
406 }
407
408 /* Try for up to 1s */
409 for (timeout = 0; timeout < 1000; timeout++) {
410 if (!pv->established)
411 return -ENXIO;
412 if (pv->mctrl_update)
413 return 0;
414 if (!hvterm_hvsi_get_packet(pv))
415 maybe_msleep(1);
416 }
417 return -EIO;
418}
419
420static int hvterm_hvsi_write_mctrl(struct hvterm_priv *pv, int dtr)
421{
422 struct hvsi_control ctrl;
423
424 pr_devel("HVSI@%x: %s DTR...\n", pv->termno,
425 dtr ? "Setting" : "Clearing");
426
427 ctrl.hdr.type = VS_CONTROL_PACKET_HEADER,
428 ctrl.hdr.len = sizeof(struct hvsi_control);
429 ctrl.verb = VSV_SET_MODEM_CTL;
430 ctrl.mask = HVSI_TSDTR;
431 ctrl.word = dtr ? HVSI_TSDTR : 0;
432 if (dtr)
433 pv->mctrl |= TIOCM_DTR;
434 else
435 pv->mctrl &= ~TIOCM_DTR;
436 return hvterm_hvsi_send_packet(pv, &ctrl.hdr);
437}
438
439static void hvterm_hvsi_establish(struct hvterm_priv *pv)
440{
441 int timeout;
442
443 /* Try for up to 10ms, there can be a packet to
444 * start the process waiting for us...
445 */
446 for (timeout = 0; timeout < 10; timeout++) {
447 if (pv->established)
448 goto established;
449 if (!hvterm_hvsi_get_packet(pv))
450 maybe_msleep(1);
451 }
452
453 /* Failed, send a close connection packet just
454 * in case
455 */
456 hvterm_hvsi_send_close(pv);
457
458 /* Then restart handshake */
459 hvterm_hvsi_start_handshake(pv);
460
461 /* Try for up to 100ms */
462 for (timeout = 0; timeout < 100; timeout++) {
463 if (pv->established)
464 goto established;
465 if (!hvterm_hvsi_get_packet(pv))
466 maybe_msleep(1);
467 }
468
469 if (!pv->established) {
470 pr_devel("HVSI@%x: Timeout handshaking, giving up !\n",
471 pv->termno);
472 return;
473 }
474 established:
475 /* Query modem control lines */
476 hvterm_hvsi_read_mctrl(pv);
477
478 /* Set our own DTR */
479 hvterm_hvsi_write_mctrl(pv, 1);
480
481 /* Set the opened flag so reads are allowed */
482 wmb();
483 pv->opened = 1;
484}
485
486static int hvterm_hvsi_open(struct hvc_struct *hp, int data)
487{
488 struct hvterm_priv *pv = hvterm_privs[hp->vtermno];
489 int rc;
490
491 pr_devel("HVSI@%x: open !\n", pv->termno);
492
493 rc = notifier_add_irq(hp, data);
494 if (rc)
495 return rc;
496
497 /* Keep track of the tty data structure */
498 pv->tty = tty_kref_get(hp->tty);
499
500 hvterm_hvsi_establish(pv);
501 return 0;
502}
503
504static void hvterm_hvsi_shutdown(struct hvc_struct *hp, struct hvterm_priv *pv)
505{
506 unsigned long flags;
507
508 if (!pv->is_console) {
509 pr_devel("HVSI@%x: Not a console, tearing down\n",
510 pv->termno);
511
512 /* Clear opened, synchronize with khvcd */
513 spin_lock_irqsave(&hp->lock, flags);
514 pv->opened = 0;
515 spin_unlock_irqrestore(&hp->lock, flags);
516
517 /* Clear our own DTR */
518 if (!pv->tty || (pv->tty->termios->c_cflag & HUPCL))
519 hvterm_hvsi_write_mctrl(pv, 0);
520
521 /* Tear down the connection */
522 hvterm_hvsi_send_close(pv);
523 }
524
525 if (pv->tty)
526 tty_kref_put(pv->tty);
527 pv->tty = NULL;
528}
529
530static void hvterm_hvsi_close(struct hvc_struct *hp, int data)
531{
532 struct hvterm_priv *pv = hvterm_privs[hp->vtermno];
533
534 pr_devel("HVSI@%x: close !\n", pv->termno);
535
536 hvterm_hvsi_shutdown(hp, pv);
537
538 notifier_del_irq(hp, data);
539}
540
541void hvterm_hvsi_hangup(struct hvc_struct *hp, int data)
542{
543 struct hvterm_priv *pv = hvterm_privs[hp->vtermno];
544
545 pr_devel("HVSI@%x: hangup !\n", pv->termno);
546
547 hvterm_hvsi_shutdown(hp, pv);
548
549 notifier_hangup_irq(hp, data);
550}
551
552static int hvterm_hvsi_tiocmget(struct hvc_struct *hp)
553{
554 struct hvterm_priv *pv = hvterm_privs[hp->vtermno];
555
556 if (!pv)
557 return -EINVAL;
558 return pv->mctrl;
559}
560
561static int hvterm_hvsi_tiocmset(struct hvc_struct *hp, unsigned int set,
562 unsigned int clear)
563{
564 struct hvterm_priv *pv = hvterm_privs[hp->vtermno];
565
566 pr_devel("HVSI@%x: Set modem control, set=%x,clr=%x\n",
567 pv->termno, set, clear);
568
569 if (set & TIOCM_DTR)
570 hvterm_hvsi_write_mctrl(pv, 1);
571 else if (clear & TIOCM_DTR)
572 hvterm_hvsi_write_mctrl(pv, 0);
573
574 return 0;
575}
576
577static const struct hv_ops hvterm_hvsi_ops = {
578 .get_chars = hvterm_hvsi_get_chars,
579 .put_chars = hvterm_hvsi_put_chars,
580 .notifier_add = hvterm_hvsi_open,
581 .notifier_del = hvterm_hvsi_close,
582 .notifier_hangup = hvterm_hvsi_hangup,
583 .tiocmget = hvterm_hvsi_tiocmget,
584 .tiocmset = hvterm_hvsi_tiocmset,
585};
586
88static int __devinit hvc_vio_probe(struct vio_dev *vdev, 587static int __devinit hvc_vio_probe(struct vio_dev *vdev,
89 const struct vio_device_id *id) 588 const struct vio_device_id *id)
90{ 589{
590 const struct hv_ops *ops;
91 struct hvc_struct *hp; 591 struct hvc_struct *hp;
592 struct hvterm_priv *pv;
593 hv_protocol_t proto;
594 int i, termno = -1;
92 595
93 /* probed with invalid parameters. */ 596 /* probed with invalid parameters. */
94 if (!vdev || !id) 597 if (!vdev || !id)
95 return -EPERM; 598 return -EPERM;
96 599
97 hp = hvc_alloc(vdev->unit_address, vdev->irq, &hvc_get_put_ops, 600 if (of_device_is_compatible(vdev->dev.of_node, "hvterm1")) {
98 MAX_VIO_PUT_CHARS); 601 proto = HV_PROTOCOL_RAW;
602 ops = &hvterm_raw_ops;
603 } else if (of_device_is_compatible(vdev->dev.of_node, "hvterm-protocol")) {
604 proto = HV_PROTOCOL_HVSI;
605 ops = &hvterm_hvsi_ops;
606 } else {
607 pr_err("hvc_vio: Unkown protocol for %s\n", vdev->dev.of_node->full_name);
608 return -ENXIO;
609 }
610
611 pr_devel("hvc_vio_probe() device %s, using %s protocol\n",
612 vdev->dev.of_node->full_name,
613 proto == HV_PROTOCOL_RAW ? "raw" : "hvsi");
614
615 /* Is it our boot one ? */
616 if (hvterm_privs[0] == &hvterm_priv0 &&
617 vdev->unit_address == hvterm_priv0.termno) {
618 pv = hvterm_privs[0];
619 termno = 0;
620 pr_devel("->boot console, using termno 0\n");
621 }
622 /* nope, allocate a new one */
623 else {
624 for (i = 0; i < MAX_NR_HVC_CONSOLES && termno < 0; i++)
625 if (!hvterm_privs[i])
626 termno = i;
627 pr_devel("->non-boot console, using termno %d\n", termno);
628 if (termno < 0)
629 return -ENODEV;
630 pv = kzalloc(sizeof(struct hvterm_priv), GFP_KERNEL);
631 if (!pv)
632 return -ENOMEM;
633 pv->termno = vdev->unit_address;
634 pv->proto = proto;
635 hvterm_privs[termno] = pv;
636 }
637
638 hp = hvc_alloc(termno, vdev->irq, ops, MAX_VIO_PUT_CHARS);
99 if (IS_ERR(hp)) 639 if (IS_ERR(hp))
100 return PTR_ERR(hp); 640 return PTR_ERR(hp);
101 dev_set_drvdata(&vdev->dev, hp); 641 dev_set_drvdata(&vdev->dev, hp);
@@ -106,8 +646,16 @@ static int __devinit hvc_vio_probe(struct vio_dev *vdev,
106static int __devexit hvc_vio_remove(struct vio_dev *vdev) 646static int __devexit hvc_vio_remove(struct vio_dev *vdev)
107{ 647{
108 struct hvc_struct *hp = dev_get_drvdata(&vdev->dev); 648 struct hvc_struct *hp = dev_get_drvdata(&vdev->dev);
649 int rc, termno;
109 650
110 return hvc_remove(hp); 651 termno = hp->vtermno;
652 rc = hvc_remove(hp);
653 if (rc == 0) {
654 if (hvterm_privs[termno] != &hvterm_priv0)
655 kfree(hvterm_privs[termno]);
656 hvterm_privs[termno] = NULL;
657 }
658 return rc;
111} 659}
112 660
113static struct vio_driver hvc_vio_driver = { 661static struct vio_driver hvc_vio_driver = {
@@ -140,34 +688,145 @@ static void __exit hvc_vio_exit(void)
140} 688}
141module_exit(hvc_vio_exit); 689module_exit(hvc_vio_exit);
142 690
143/* the device tree order defines our numbering */ 691static void udbg_hvc_putc(char c)
144static int hvc_find_vtys(void)
145{ 692{
146 struct device_node *vty; 693 int count = -1;
147 int num_found = 0;
148 694
149 for (vty = of_find_node_by_name(NULL, "vty"); vty != NULL; 695 if (c == '\n')
150 vty = of_find_node_by_name(vty, "vty")) { 696 udbg_hvc_putc('\r');
151 const uint32_t *vtermno;
152 697
153 /* We have statically defined space for only a certain number 698 do {
154 * of console adapters. 699 switch(hvterm_priv0.proto) {
155 */ 700 case HV_PROTOCOL_RAW:
156 if (num_found >= MAX_NR_HVC_CONSOLES) { 701 count = hvterm_raw_put_chars(0, &c, 1);
157 of_node_put(vty); 702 break;
703 case HV_PROTOCOL_HVSI:
704 count = hvterm_hvsi_put_chars(0, &c, 1);
158 break; 705 break;
159 } 706 }
707 } while(count == 0);
708}
709
710static int udbg_hvc_getc_poll(void)
711{
712 int rc = 0;
713 char c;
160 714
161 vtermno = of_get_property(vty, "reg", NULL); 715 switch(hvterm_priv0.proto) {
162 if (!vtermno) 716 case HV_PROTOCOL_RAW:
163 continue; 717 rc = hvterm_raw_get_chars(0, &c, 1);
718 break;
719 case HV_PROTOCOL_HVSI:
720 rc = hvterm_hvsi_get_chars(0, &c, 1);
721 break;
722 }
723 if (!rc)
724 return -1;
725 return c;
726}
164 727
165 if (of_device_is_compatible(vty, "hvterm1")) { 728static int udbg_hvc_getc(void)
166 hvc_instantiate(*vtermno, num_found, &hvc_get_put_ops); 729{
167 ++num_found; 730 int ch;
731 for (;;) {
732 ch = udbg_hvc_getc_poll();
733 if (ch == -1) {
734 /* This shouldn't be needed...but... */
735 volatile unsigned long delay;
736 for (delay=0; delay < 2000000; delay++)
737 ;
738 } else {
739 return ch;
168 } 740 }
169 } 741 }
742}
743
744void __init hvc_vio_init_early(void)
745{
746 struct device_node *stdout_node;
747 const u32 *termno;
748 const char *name;
749 const struct hv_ops *ops;
750
751 /* find the boot console from /chosen/stdout */
752 if (!of_chosen)
753 return;
754 name = of_get_property(of_chosen, "linux,stdout-path", NULL);
755 if (name == NULL)
756 return;
757 stdout_node = of_find_node_by_path(name);
758 if (!stdout_node)
759 return;
760 name = of_get_property(stdout_node, "name", NULL);
761 if (!name) {
762 printk(KERN_WARNING "stdout node missing 'name' property!\n");
763 goto out;
764 }
765
766 /* Check if it's a virtual terminal */
767 if (strncmp(name, "vty", 3) != 0)
768 goto out;
769 termno = of_get_property(stdout_node, "reg", NULL);
770 if (termno == NULL)
771 goto out;
772 hvterm_priv0.termno = *termno;
773 hvterm_priv0.is_console = 1;
774 hvterm_privs[0] = &hvterm_priv0;
775
776 /* Check the protocol */
777 if (of_device_is_compatible(stdout_node, "hvterm1")) {
778 hvterm_priv0.proto = HV_PROTOCOL_RAW;
779 ops = &hvterm_raw_ops;
780 }
781 else if (of_device_is_compatible(stdout_node, "hvterm-protocol")) {
782 hvterm_priv0.proto = HV_PROTOCOL_HVSI;
783 ops = &hvterm_hvsi_ops;
784 /* HVSI, perform the handshake now */
785 hvterm_hvsi_establish(&hvterm_priv0);
786 } else
787 goto out;
788 udbg_putc = udbg_hvc_putc;
789 udbg_getc = udbg_hvc_getc;
790 udbg_getc_poll = udbg_hvc_getc_poll;
791#ifdef HVC_OLD_HVSI
792 /* When using the old HVSI driver don't register the HVC
793 * backend for HVSI, only do udbg
794 */
795 if (hvterm_priv0.proto == HV_PROTOCOL_HVSI)
796 goto out;
797#endif
798 add_preferred_console("hvc", 0, NULL);
799 hvc_instantiate(0, 0, ops);
800out:
801 of_node_put(stdout_node);
802}
803
804/* call this from early_init() for a working debug console on
805 * vterm capable LPAR machines
806 */
807#ifdef CONFIG_PPC_EARLY_DEBUG_LPAR
808void __init udbg_init_debug_lpar(void)
809{
810 hvterm_privs[0] = &hvterm_priv0;
811 hvterm_priv0.termno = 0;
812 hvterm_priv0.proto = HV_PROTOCOL_RAW;
813 hvterm_priv0.is_console = 1;
814 udbg_putc = udbg_hvc_putc;
815 udbg_getc = udbg_hvc_getc;
816 udbg_getc_poll = udbg_hvc_getc_poll;
817}
818#endif /* CONFIG_PPC_EARLY_DEBUG_LPAR */
170 819
171 return num_found; 820#ifdef CONFIG_PPC_EARLY_DEBUG_LPAR_HVSI
821void __init udbg_init_debug_lpar_hvsi(void)
822{
823 hvterm_privs[0] = &hvterm_priv0;
824 hvterm_priv0.termno = CONFIG_PPC_EARLY_DEBUG_HVSI_VTERMNO;
825 hvterm_priv0.proto = HV_PROTOCOL_HVSI;
826 hvterm_priv0.is_console = 1;
827 udbg_putc = udbg_hvc_putc;
828 udbg_getc = udbg_hvc_getc;
829 udbg_getc_poll = udbg_hvc_getc_poll;
830 hvterm_hvsi_establish(&hvterm_priv0);
172} 831}
173console_initcall(hvc_find_vtys); 832#endif /* CONFIG_PPC_EARLY_DEBUG_LPAR_HVSI */