11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
262b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu * SCLP line mode console driver
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
462b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu * Copyright IBM Corp. 1999, 2009
562b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu * Author(s): Martin Peschke <mpeschke@de.ibm.com>
662b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu *	      Martin Schwidefsky <schwidefsky@de.ibm.com>
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kmod.h>
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/console.h>
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/timer.h>
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/jiffies.h>
14095761d28ae43eae7d4504d49b0b952cf02b0188Heiko Carstens#include <linux/termios.h>
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/err.h>
162332ce1a97963b7769e0c2d40492a10a124efba5Holger Smolinski#include <linux/reboot.h>
175a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/gfp.h>
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "sclp.h"
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "sclp_rw.h"
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include "sclp_tty.h"
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define sclp_console_major 4		/* TTYAUX_MAJOR */
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define sclp_console_minor 64
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define sclp_console_name  "ttyS"
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Lock to guard over changes to global variables */
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic spinlock_t sclp_con_lock;
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* List of free pages that can be used for console output buffering */
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct list_head sclp_con_pages;
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* List of full struct sclp_buffer structures ready for output */
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct list_head sclp_con_outqueue;
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Pointer to current console buffer */
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct sclp_buffer *sclp_conbuf;
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Timer for delayed output of console messages */
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct timer_list sclp_con_timer;
3762b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu/* Suspend mode flag */
3862b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheustatic int sclp_con_suspended;
3962b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu/* Flag that output queue is currently running */
4062b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheustatic int sclp_con_queue_running;
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Output format for console messages */
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned short sclp_con_columns;
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned short sclp_con_width_htab;
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldssclp_conbuf_callback(struct sclp_buffer *buffer, int rc)
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void *page;
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	do {
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		page = sclp_unmake_buffer(buffer);
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_lock_irqsave(&sclp_con_lock, flags);
5562b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Remove buffer from outqueue */
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		list_del(&buffer->list);
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		list_add_tail((struct list_head *) page, &sclp_con_pages);
5962b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Check if there is a pending buffer on the out queue. */
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buffer = NULL;
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!list_empty(&sclp_con_outqueue))
6362b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu			buffer = list_first_entry(&sclp_con_outqueue,
6462b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu						  struct sclp_buffer, list);
6562b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu		if (!buffer || sclp_con_suspended) {
6662b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu			sclp_con_queue_running = 0;
6762b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu			spin_unlock_irqrestore(&sclp_con_lock, flags);
6862b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu			break;
6962b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu		}
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_unlock_irqrestore(&sclp_con_lock, flags);
7162b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	} while (sclp_emit_buffer(buffer, sclp_conbuf_callback));
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7462b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu/*
7562b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu * Finalize and emit first pending buffer.
7662b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu */
7762b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheustatic void sclp_conbuf_emit(void)
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct sclp_buffer* buffer;
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc;
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&sclp_con_lock, flags);
8462b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	if (sclp_conbuf)
8562b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu		list_add_tail(&sclp_conbuf->list, &sclp_con_outqueue);
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sclp_conbuf = NULL;
8762b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	if (sclp_con_queue_running || sclp_con_suspended)
8862b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu		goto out_unlock;
8962b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	if (list_empty(&sclp_con_outqueue))
9062b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu		goto out_unlock;
9162b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	buffer = list_first_entry(&sclp_con_outqueue, struct sclp_buffer,
9262b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu				  list);
9362b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	sclp_con_queue_running = 1;
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&sclp_con_lock, flags);
9562b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = sclp_emit_buffer(buffer, sclp_conbuf_callback);
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rc)
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sclp_conbuf_callback(buffer, rc);
9962b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	return;
10062b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheuout_unlock:
10162b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	spin_unlock_irqrestore(&sclp_con_lock, flags);
10262b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu}
10362b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu
10462b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu/*
10562b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu * Wait until out queue is empty
10662b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu */
10762b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheustatic void sclp_console_sync_queue(void)
10862b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu{
10962b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	unsigned long flags;
11062b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu
11162b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	spin_lock_irqsave(&sclp_con_lock, flags);
11262b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	if (timer_pending(&sclp_con_timer))
113f3dfa86caa4a54aceb2b235bf28a6f6ad73b2716Michael Holzheu		del_timer(&sclp_con_timer);
11462b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	while (sclp_con_queue_running) {
11562b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu		spin_unlock_irqrestore(&sclp_con_lock, flags);
11662b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu		sclp_sync_wait();
11762b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu		spin_lock_irqsave(&sclp_con_lock, flags);
11862b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	}
11962b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	spin_unlock_irqrestore(&sclp_con_lock, flags);
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * When this routine is called from the timer then we flush the
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * temporary write buffer without further waiting on a final new line.
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldssclp_console_timeout(unsigned long data)
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sclp_conbuf_emit();
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Writes the given message to S390 system console
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldssclp_console_write(struct console *console, const char *message,
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		   unsigned int count)
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void *page;
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int written;
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (count == 0)
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&sclp_con_lock, flags);
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * process escape characters, write message into buffer,
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * send buffer to SCLP
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	do {
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* make sure we have a console output buffer */
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (sclp_conbuf == NULL) {
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			while (list_empty(&sclp_con_pages)) {
15462b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu				if (sclp_con_suspended)
15562b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu					goto out;
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				spin_unlock_irqrestore(&sclp_con_lock, flags);
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				sclp_sync_wait();
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				spin_lock_irqsave(&sclp_con_lock, flags);
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			page = sclp_con_pages.next;
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			list_del((struct list_head *) page);
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			sclp_conbuf = sclp_make_buffer(page, sclp_con_columns,
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						       sclp_con_width_htab);
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* try to write the string to the current output buffer */
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		written = sclp_write(sclp_conbuf, (const unsigned char *)
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				     message, count);
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (written == count)
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Not all characters could be written to the current
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * output buffer. Emit the buffer, create a new buffer
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * and then output the rest of the string.
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_unlock_irqrestore(&sclp_con_lock, flags);
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sclp_conbuf_emit();
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_lock_irqsave(&sclp_con_lock, flags);
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		message += written;
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		count -= written;
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} while (count > 0);
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Setup timer to output current console buffer after 1/10 second */
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (sclp_conbuf != NULL && sclp_chars_in_buffer(sclp_conbuf) != 0 &&
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    !timer_pending(&sclp_con_timer)) {
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		init_timer(&sclp_con_timer);
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sclp_con_timer.function = sclp_console_timeout;
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sclp_con_timer.data = 0UL;
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sclp_con_timer.expires = jiffies + HZ/10;
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		add_timer(&sclp_con_timer);
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
19062b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheuout:
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&sclp_con_lock, flags);
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct tty_driver *
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldssclp_console_device(struct console *c, int *index)
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*index = c->index;
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return sclp_tty_driver;
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
20262b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu * Make sure that all buffers will be flushed to the SCLP.
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void
2052332ce1a97963b7769e0c2d40492a10a124efba5Holger Smolinskisclp_console_flush(void)
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
20762b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	sclp_conbuf_emit();
20862b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	sclp_console_sync_queue();
20962b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu}
21062b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu
21162b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu/*
21262b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu * Resume console: If there are cached messages, emit them.
21362b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu */
21462b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheustatic void sclp_console_resume(void)
21562b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu{
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
21862b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	spin_lock_irqsave(&sclp_con_lock, flags);
21962b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	sclp_con_suspended = 0;
22062b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	spin_unlock_irqrestore(&sclp_con_lock, flags);
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sclp_conbuf_emit();
22262b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu}
22362b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu
22462b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu/*
22562b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu * Suspend console: Set suspend flag and flush console
22662b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu */
22762b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheustatic void sclp_console_suspend(void)
22862b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu{
22962b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	unsigned long flags;
23062b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_irqsave(&sclp_con_lock, flags);
23262b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	sclp_con_suspended = 1;
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock_irqrestore(&sclp_con_lock, flags);
23462b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	sclp_console_flush();
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
23762b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheustatic int sclp_console_notify(struct notifier_block *self,
23862b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu			       unsigned long event, void *data)
2392332ce1a97963b7769e0c2d40492a10a124efba5Holger Smolinski{
2402332ce1a97963b7769e0c2d40492a10a124efba5Holger Smolinski	sclp_console_flush();
2412332ce1a97963b7769e0c2d40492a10a124efba5Holger Smolinski	return NOTIFY_OK;
2422332ce1a97963b7769e0c2d40492a10a124efba5Holger Smolinski}
2432332ce1a97963b7769e0c2d40492a10a124efba5Holger Smolinski
2442332ce1a97963b7769e0c2d40492a10a124efba5Holger Smolinskistatic struct notifier_block on_panic_nb = {
2452332ce1a97963b7769e0c2d40492a10a124efba5Holger Smolinski	.notifier_call = sclp_console_notify,
24662b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	.priority = SCLP_PANIC_PRIO_CLIENT,
2472332ce1a97963b7769e0c2d40492a10a124efba5Holger Smolinski};
2482332ce1a97963b7769e0c2d40492a10a124efba5Holger Smolinski
2492332ce1a97963b7769e0c2d40492a10a124efba5Holger Smolinskistatic struct notifier_block on_reboot_nb = {
2502332ce1a97963b7769e0c2d40492a10a124efba5Holger Smolinski	.notifier_call = sclp_console_notify,
2512332ce1a97963b7769e0c2d40492a10a124efba5Holger Smolinski	.priority = 1,
2522332ce1a97963b7769e0c2d40492a10a124efba5Holger Smolinski};
2532332ce1a97963b7769e0c2d40492a10a124efba5Holger Smolinski
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * used to register the SCLP console to the kernel and to
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * give printk necessary information
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct console sclp_console =
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.name = sclp_console_name,
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write = sclp_console_write,
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.device = sclp_console_device,
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.flags = CON_PRINTBUFFER,
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.index = 0 /* ttyS0 */
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
26862b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu * This function is called for SCLP suspend and resume events.
26962b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu */
27062b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheuvoid sclp_console_pm_event(enum sclp_pm_event sclp_pm_event)
27162b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu{
27262b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	switch (sclp_pm_event) {
27362b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	case SCLP_PM_EVENT_FREEZE:
27462b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu		sclp_console_suspend();
27562b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu		break;
27662b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	case SCLP_PM_EVENT_RESTORE:
27762b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	case SCLP_PM_EVENT_THAW:
27862b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu		sclp_console_resume();
27962b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu		break;
28062b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu	}
28162b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu}
28262b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu
28362b7494209495847269a6ce0504cbefd23d42eb1Michael Holzheu/*
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * called by console_init() in drivers/char/tty_io.c at boot-time.
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldssclp_console_init(void)
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	void *page;
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rc;
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!CONSOLE_IS_SCLP)
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rc = sclp_rw_init();
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (rc)
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return rc;
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Allocate pages for output buffering */
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	INIT_LIST_HEAD(&sclp_con_pages);
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < MAX_CONSOLE_PAGES; i++) {
3014c8f4794b61e89dd68f96cfc23a9d9b6c25be420Heiko Carstens		page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
3024c8f4794b61e89dd68f96cfc23a9d9b6c25be420Heiko Carstens		list_add_tail(page, &sclp_con_pages);
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	INIT_LIST_HEAD(&sclp_con_outqueue);
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_init(&sclp_con_lock);
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sclp_conbuf = NULL;
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	init_timer(&sclp_con_timer);
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Set output format */
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (MACHINE_IS_VM)
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * save 4 characters for the CPU number
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * written at start of each line by VM/CP
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sclp_con_columns = 76;
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sclp_con_columns = 80;
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sclp_con_width_htab = 8;
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* enable printk-access to this driver */
3212332ce1a97963b7769e0c2d40492a10a124efba5Holger Smolinski	atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);
3222332ce1a97963b7769e0c2d40492a10a124efba5Holger Smolinski	register_reboot_notifier(&on_reboot_nb);
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	register_console(&sclp_console);
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsconsole_initcall(sclp_console_init);
328