xref: /OK3568_Linux_fs/kernel/drivers/s390/char/sclp_con.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * SCLP line mode console driver
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Copyright IBM Corp. 1999, 2009
6*4882a593Smuzhiyun  * Author(s): Martin Peschke <mpeschke@de.ibm.com>
7*4882a593Smuzhiyun  *	      Martin Schwidefsky <schwidefsky@de.ibm.com>
8*4882a593Smuzhiyun  */
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun #include <linux/kmod.h>
11*4882a593Smuzhiyun #include <linux/console.h>
12*4882a593Smuzhiyun #include <linux/init.h>
13*4882a593Smuzhiyun #include <linux/timer.h>
14*4882a593Smuzhiyun #include <linux/jiffies.h>
15*4882a593Smuzhiyun #include <linux/termios.h>
16*4882a593Smuzhiyun #include <linux/err.h>
17*4882a593Smuzhiyun #include <linux/reboot.h>
18*4882a593Smuzhiyun #include <linux/gfp.h>
19*4882a593Smuzhiyun 
20*4882a593Smuzhiyun #include "sclp.h"
21*4882a593Smuzhiyun #include "sclp_rw.h"
22*4882a593Smuzhiyun #include "sclp_tty.h"
23*4882a593Smuzhiyun 
24*4882a593Smuzhiyun #define sclp_console_major 4		/* TTYAUX_MAJOR */
25*4882a593Smuzhiyun #define sclp_console_minor 64
26*4882a593Smuzhiyun #define sclp_console_name  "ttyS"
27*4882a593Smuzhiyun 
28*4882a593Smuzhiyun /* Lock to guard over changes to global variables */
29*4882a593Smuzhiyun static spinlock_t sclp_con_lock;
30*4882a593Smuzhiyun /* List of free pages that can be used for console output buffering */
31*4882a593Smuzhiyun static struct list_head sclp_con_pages;
32*4882a593Smuzhiyun /* List of full struct sclp_buffer structures ready for output */
33*4882a593Smuzhiyun static struct list_head sclp_con_outqueue;
34*4882a593Smuzhiyun /* Pointer to current console buffer */
35*4882a593Smuzhiyun static struct sclp_buffer *sclp_conbuf;
36*4882a593Smuzhiyun /* Timer for delayed output of console messages */
37*4882a593Smuzhiyun static struct timer_list sclp_con_timer;
38*4882a593Smuzhiyun /* Suspend mode flag */
39*4882a593Smuzhiyun static int sclp_con_suspended;
40*4882a593Smuzhiyun /* Flag that output queue is currently running */
41*4882a593Smuzhiyun static int sclp_con_queue_running;
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun /* Output format for console messages */
44*4882a593Smuzhiyun static unsigned short sclp_con_columns;
45*4882a593Smuzhiyun static unsigned short sclp_con_width_htab;
46*4882a593Smuzhiyun 
47*4882a593Smuzhiyun static void
sclp_conbuf_callback(struct sclp_buffer * buffer,int rc)48*4882a593Smuzhiyun sclp_conbuf_callback(struct sclp_buffer *buffer, int rc)
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun 	unsigned long flags;
51*4882a593Smuzhiyun 	void *page;
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun 	do {
54*4882a593Smuzhiyun 		page = sclp_unmake_buffer(buffer);
55*4882a593Smuzhiyun 		spin_lock_irqsave(&sclp_con_lock, flags);
56*4882a593Smuzhiyun 
57*4882a593Smuzhiyun 		/* Remove buffer from outqueue */
58*4882a593Smuzhiyun 		list_del(&buffer->list);
59*4882a593Smuzhiyun 		list_add_tail((struct list_head *) page, &sclp_con_pages);
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun 		/* Check if there is a pending buffer on the out queue. */
62*4882a593Smuzhiyun 		buffer = NULL;
63*4882a593Smuzhiyun 		if (!list_empty(&sclp_con_outqueue))
64*4882a593Smuzhiyun 			buffer = list_first_entry(&sclp_con_outqueue,
65*4882a593Smuzhiyun 						  struct sclp_buffer, list);
66*4882a593Smuzhiyun 		if (!buffer || sclp_con_suspended) {
67*4882a593Smuzhiyun 			sclp_con_queue_running = 0;
68*4882a593Smuzhiyun 			spin_unlock_irqrestore(&sclp_con_lock, flags);
69*4882a593Smuzhiyun 			break;
70*4882a593Smuzhiyun 		}
71*4882a593Smuzhiyun 		spin_unlock_irqrestore(&sclp_con_lock, flags);
72*4882a593Smuzhiyun 	} while (sclp_emit_buffer(buffer, sclp_conbuf_callback));
73*4882a593Smuzhiyun }
74*4882a593Smuzhiyun 
75*4882a593Smuzhiyun /*
76*4882a593Smuzhiyun  * Finalize and emit first pending buffer.
77*4882a593Smuzhiyun  */
sclp_conbuf_emit(void)78*4882a593Smuzhiyun static void sclp_conbuf_emit(void)
79*4882a593Smuzhiyun {
80*4882a593Smuzhiyun 	struct sclp_buffer* buffer;
81*4882a593Smuzhiyun 	unsigned long flags;
82*4882a593Smuzhiyun 	int rc;
83*4882a593Smuzhiyun 
84*4882a593Smuzhiyun 	spin_lock_irqsave(&sclp_con_lock, flags);
85*4882a593Smuzhiyun 	if (sclp_conbuf)
86*4882a593Smuzhiyun 		list_add_tail(&sclp_conbuf->list, &sclp_con_outqueue);
87*4882a593Smuzhiyun 	sclp_conbuf = NULL;
88*4882a593Smuzhiyun 	if (sclp_con_queue_running || sclp_con_suspended)
89*4882a593Smuzhiyun 		goto out_unlock;
90*4882a593Smuzhiyun 	if (list_empty(&sclp_con_outqueue))
91*4882a593Smuzhiyun 		goto out_unlock;
92*4882a593Smuzhiyun 	buffer = list_first_entry(&sclp_con_outqueue, struct sclp_buffer,
93*4882a593Smuzhiyun 				  list);
94*4882a593Smuzhiyun 	sclp_con_queue_running = 1;
95*4882a593Smuzhiyun 	spin_unlock_irqrestore(&sclp_con_lock, flags);
96*4882a593Smuzhiyun 
97*4882a593Smuzhiyun 	rc = sclp_emit_buffer(buffer, sclp_conbuf_callback);
98*4882a593Smuzhiyun 	if (rc)
99*4882a593Smuzhiyun 		sclp_conbuf_callback(buffer, rc);
100*4882a593Smuzhiyun 	return;
101*4882a593Smuzhiyun out_unlock:
102*4882a593Smuzhiyun 	spin_unlock_irqrestore(&sclp_con_lock, flags);
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun 
105*4882a593Smuzhiyun /*
106*4882a593Smuzhiyun  * Wait until out queue is empty
107*4882a593Smuzhiyun  */
sclp_console_sync_queue(void)108*4882a593Smuzhiyun static void sclp_console_sync_queue(void)
109*4882a593Smuzhiyun {
110*4882a593Smuzhiyun 	unsigned long flags;
111*4882a593Smuzhiyun 
112*4882a593Smuzhiyun 	spin_lock_irqsave(&sclp_con_lock, flags);
113*4882a593Smuzhiyun 	if (timer_pending(&sclp_con_timer))
114*4882a593Smuzhiyun 		del_timer(&sclp_con_timer);
115*4882a593Smuzhiyun 	while (sclp_con_queue_running) {
116*4882a593Smuzhiyun 		spin_unlock_irqrestore(&sclp_con_lock, flags);
117*4882a593Smuzhiyun 		sclp_sync_wait();
118*4882a593Smuzhiyun 		spin_lock_irqsave(&sclp_con_lock, flags);
119*4882a593Smuzhiyun 	}
120*4882a593Smuzhiyun 	spin_unlock_irqrestore(&sclp_con_lock, flags);
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun /*
124*4882a593Smuzhiyun  * When this routine is called from the timer then we flush the
125*4882a593Smuzhiyun  * temporary write buffer without further waiting on a final new line.
126*4882a593Smuzhiyun  */
127*4882a593Smuzhiyun static void
sclp_console_timeout(struct timer_list * unused)128*4882a593Smuzhiyun sclp_console_timeout(struct timer_list *unused)
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun 	sclp_conbuf_emit();
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun /*
134*4882a593Smuzhiyun  * Drop oldest console buffer if sclp_con_drop is set
135*4882a593Smuzhiyun  */
136*4882a593Smuzhiyun static int
sclp_console_drop_buffer(void)137*4882a593Smuzhiyun sclp_console_drop_buffer(void)
138*4882a593Smuzhiyun {
139*4882a593Smuzhiyun 	struct list_head *list;
140*4882a593Smuzhiyun 	struct sclp_buffer *buffer;
141*4882a593Smuzhiyun 	void *page;
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 	if (!sclp_console_drop)
144*4882a593Smuzhiyun 		return 0;
145*4882a593Smuzhiyun 	list = sclp_con_outqueue.next;
146*4882a593Smuzhiyun 	if (sclp_con_queue_running)
147*4882a593Smuzhiyun 		/* The first element is in I/O */
148*4882a593Smuzhiyun 		list = list->next;
149*4882a593Smuzhiyun 	if (list == &sclp_con_outqueue)
150*4882a593Smuzhiyun 		return 0;
151*4882a593Smuzhiyun 	list_del(list);
152*4882a593Smuzhiyun 	buffer = list_entry(list, struct sclp_buffer, list);
153*4882a593Smuzhiyun 	page = sclp_unmake_buffer(buffer);
154*4882a593Smuzhiyun 	list_add_tail((struct list_head *) page, &sclp_con_pages);
155*4882a593Smuzhiyun 	return 1;
156*4882a593Smuzhiyun }
157*4882a593Smuzhiyun 
158*4882a593Smuzhiyun /*
159*4882a593Smuzhiyun  * Writes the given message to S390 system console
160*4882a593Smuzhiyun  */
161*4882a593Smuzhiyun static void
sclp_console_write(struct console * console,const char * message,unsigned int count)162*4882a593Smuzhiyun sclp_console_write(struct console *console, const char *message,
163*4882a593Smuzhiyun 		   unsigned int count)
164*4882a593Smuzhiyun {
165*4882a593Smuzhiyun 	unsigned long flags;
166*4882a593Smuzhiyun 	void *page;
167*4882a593Smuzhiyun 	int written;
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun 	if (count == 0)
170*4882a593Smuzhiyun 		return;
171*4882a593Smuzhiyun 	spin_lock_irqsave(&sclp_con_lock, flags);
172*4882a593Smuzhiyun 	/*
173*4882a593Smuzhiyun 	 * process escape characters, write message into buffer,
174*4882a593Smuzhiyun 	 * send buffer to SCLP
175*4882a593Smuzhiyun 	 */
176*4882a593Smuzhiyun 	do {
177*4882a593Smuzhiyun 		/* make sure we have a console output buffer */
178*4882a593Smuzhiyun 		if (sclp_conbuf == NULL) {
179*4882a593Smuzhiyun 			if (list_empty(&sclp_con_pages))
180*4882a593Smuzhiyun 				sclp_console_full++;
181*4882a593Smuzhiyun 			while (list_empty(&sclp_con_pages)) {
182*4882a593Smuzhiyun 				if (sclp_con_suspended)
183*4882a593Smuzhiyun 					goto out;
184*4882a593Smuzhiyun 				if (sclp_console_drop_buffer())
185*4882a593Smuzhiyun 					break;
186*4882a593Smuzhiyun 				spin_unlock_irqrestore(&sclp_con_lock, flags);
187*4882a593Smuzhiyun 				sclp_sync_wait();
188*4882a593Smuzhiyun 				spin_lock_irqsave(&sclp_con_lock, flags);
189*4882a593Smuzhiyun 			}
190*4882a593Smuzhiyun 			page = sclp_con_pages.next;
191*4882a593Smuzhiyun 			list_del((struct list_head *) page);
192*4882a593Smuzhiyun 			sclp_conbuf = sclp_make_buffer(page, sclp_con_columns,
193*4882a593Smuzhiyun 						       sclp_con_width_htab);
194*4882a593Smuzhiyun 		}
195*4882a593Smuzhiyun 		/* try to write the string to the current output buffer */
196*4882a593Smuzhiyun 		written = sclp_write(sclp_conbuf, (const unsigned char *)
197*4882a593Smuzhiyun 				     message, count);
198*4882a593Smuzhiyun 		if (written == count)
199*4882a593Smuzhiyun 			break;
200*4882a593Smuzhiyun 		/*
201*4882a593Smuzhiyun 		 * Not all characters could be written to the current
202*4882a593Smuzhiyun 		 * output buffer. Emit the buffer, create a new buffer
203*4882a593Smuzhiyun 		 * and then output the rest of the string.
204*4882a593Smuzhiyun 		 */
205*4882a593Smuzhiyun 		spin_unlock_irqrestore(&sclp_con_lock, flags);
206*4882a593Smuzhiyun 		sclp_conbuf_emit();
207*4882a593Smuzhiyun 		spin_lock_irqsave(&sclp_con_lock, flags);
208*4882a593Smuzhiyun 		message += written;
209*4882a593Smuzhiyun 		count -= written;
210*4882a593Smuzhiyun 	} while (count > 0);
211*4882a593Smuzhiyun 	/* Setup timer to output current console buffer after 1/10 second */
212*4882a593Smuzhiyun 	if (sclp_conbuf != NULL && sclp_chars_in_buffer(sclp_conbuf) != 0 &&
213*4882a593Smuzhiyun 	    !timer_pending(&sclp_con_timer)) {
214*4882a593Smuzhiyun 		mod_timer(&sclp_con_timer, jiffies + HZ / 10);
215*4882a593Smuzhiyun 	}
216*4882a593Smuzhiyun out:
217*4882a593Smuzhiyun 	spin_unlock_irqrestore(&sclp_con_lock, flags);
218*4882a593Smuzhiyun }
219*4882a593Smuzhiyun 
220*4882a593Smuzhiyun static struct tty_driver *
sclp_console_device(struct console * c,int * index)221*4882a593Smuzhiyun sclp_console_device(struct console *c, int *index)
222*4882a593Smuzhiyun {
223*4882a593Smuzhiyun 	*index = c->index;
224*4882a593Smuzhiyun 	return sclp_tty_driver;
225*4882a593Smuzhiyun }
226*4882a593Smuzhiyun 
227*4882a593Smuzhiyun /*
228*4882a593Smuzhiyun  * Make sure that all buffers will be flushed to the SCLP.
229*4882a593Smuzhiyun  */
230*4882a593Smuzhiyun static void
sclp_console_flush(void)231*4882a593Smuzhiyun sclp_console_flush(void)
232*4882a593Smuzhiyun {
233*4882a593Smuzhiyun 	sclp_conbuf_emit();
234*4882a593Smuzhiyun 	sclp_console_sync_queue();
235*4882a593Smuzhiyun }
236*4882a593Smuzhiyun 
237*4882a593Smuzhiyun /*
238*4882a593Smuzhiyun  * Resume console: If there are cached messages, emit them.
239*4882a593Smuzhiyun  */
sclp_console_resume(void)240*4882a593Smuzhiyun static void sclp_console_resume(void)
241*4882a593Smuzhiyun {
242*4882a593Smuzhiyun 	unsigned long flags;
243*4882a593Smuzhiyun 
244*4882a593Smuzhiyun 	spin_lock_irqsave(&sclp_con_lock, flags);
245*4882a593Smuzhiyun 	sclp_con_suspended = 0;
246*4882a593Smuzhiyun 	spin_unlock_irqrestore(&sclp_con_lock, flags);
247*4882a593Smuzhiyun 	sclp_conbuf_emit();
248*4882a593Smuzhiyun }
249*4882a593Smuzhiyun 
250*4882a593Smuzhiyun /*
251*4882a593Smuzhiyun  * Suspend console: Set suspend flag and flush console
252*4882a593Smuzhiyun  */
sclp_console_suspend(void)253*4882a593Smuzhiyun static void sclp_console_suspend(void)
254*4882a593Smuzhiyun {
255*4882a593Smuzhiyun 	unsigned long flags;
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun 	spin_lock_irqsave(&sclp_con_lock, flags);
258*4882a593Smuzhiyun 	sclp_con_suspended = 1;
259*4882a593Smuzhiyun 	spin_unlock_irqrestore(&sclp_con_lock, flags);
260*4882a593Smuzhiyun 	sclp_console_flush();
261*4882a593Smuzhiyun }
262*4882a593Smuzhiyun 
sclp_console_notify(struct notifier_block * self,unsigned long event,void * data)263*4882a593Smuzhiyun static int sclp_console_notify(struct notifier_block *self,
264*4882a593Smuzhiyun 			       unsigned long event, void *data)
265*4882a593Smuzhiyun {
266*4882a593Smuzhiyun 	sclp_console_flush();
267*4882a593Smuzhiyun 	return NOTIFY_OK;
268*4882a593Smuzhiyun }
269*4882a593Smuzhiyun 
270*4882a593Smuzhiyun static struct notifier_block on_panic_nb = {
271*4882a593Smuzhiyun 	.notifier_call = sclp_console_notify,
272*4882a593Smuzhiyun 	.priority = SCLP_PANIC_PRIO_CLIENT,
273*4882a593Smuzhiyun };
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun static struct notifier_block on_reboot_nb = {
276*4882a593Smuzhiyun 	.notifier_call = sclp_console_notify,
277*4882a593Smuzhiyun 	.priority = 1,
278*4882a593Smuzhiyun };
279*4882a593Smuzhiyun 
280*4882a593Smuzhiyun /*
281*4882a593Smuzhiyun  * used to register the SCLP console to the kernel and to
282*4882a593Smuzhiyun  * give printk necessary information
283*4882a593Smuzhiyun  */
284*4882a593Smuzhiyun static struct console sclp_console =
285*4882a593Smuzhiyun {
286*4882a593Smuzhiyun 	.name = sclp_console_name,
287*4882a593Smuzhiyun 	.write = sclp_console_write,
288*4882a593Smuzhiyun 	.device = sclp_console_device,
289*4882a593Smuzhiyun 	.flags = CON_PRINTBUFFER,
290*4882a593Smuzhiyun 	.index = 0 /* ttyS0 */
291*4882a593Smuzhiyun };
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun /*
294*4882a593Smuzhiyun  * This function is called for SCLP suspend and resume events.
295*4882a593Smuzhiyun  */
sclp_console_pm_event(enum sclp_pm_event sclp_pm_event)296*4882a593Smuzhiyun void sclp_console_pm_event(enum sclp_pm_event sclp_pm_event)
297*4882a593Smuzhiyun {
298*4882a593Smuzhiyun 	switch (sclp_pm_event) {
299*4882a593Smuzhiyun 	case SCLP_PM_EVENT_FREEZE:
300*4882a593Smuzhiyun 		sclp_console_suspend();
301*4882a593Smuzhiyun 		break;
302*4882a593Smuzhiyun 	case SCLP_PM_EVENT_RESTORE:
303*4882a593Smuzhiyun 	case SCLP_PM_EVENT_THAW:
304*4882a593Smuzhiyun 		sclp_console_resume();
305*4882a593Smuzhiyun 		break;
306*4882a593Smuzhiyun 	}
307*4882a593Smuzhiyun }
308*4882a593Smuzhiyun 
309*4882a593Smuzhiyun /*
310*4882a593Smuzhiyun  * called by console_init() in drivers/char/tty_io.c at boot-time.
311*4882a593Smuzhiyun  */
312*4882a593Smuzhiyun static int __init
sclp_console_init(void)313*4882a593Smuzhiyun sclp_console_init(void)
314*4882a593Smuzhiyun {
315*4882a593Smuzhiyun 	void *page;
316*4882a593Smuzhiyun 	int i;
317*4882a593Smuzhiyun 	int rc;
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun 	/* SCLP consoles are handled together */
320*4882a593Smuzhiyun 	if (!(CONSOLE_IS_SCLP || CONSOLE_IS_VT220))
321*4882a593Smuzhiyun 		return 0;
322*4882a593Smuzhiyun 	rc = sclp_rw_init();
323*4882a593Smuzhiyun 	if (rc)
324*4882a593Smuzhiyun 		return rc;
325*4882a593Smuzhiyun 	/* Allocate pages for output buffering */
326*4882a593Smuzhiyun 	INIT_LIST_HEAD(&sclp_con_pages);
327*4882a593Smuzhiyun 	for (i = 0; i < sclp_console_pages; i++) {
328*4882a593Smuzhiyun 		page = (void *) get_zeroed_page(GFP_KERNEL | GFP_DMA);
329*4882a593Smuzhiyun 		list_add_tail(page, &sclp_con_pages);
330*4882a593Smuzhiyun 	}
331*4882a593Smuzhiyun 	INIT_LIST_HEAD(&sclp_con_outqueue);
332*4882a593Smuzhiyun 	spin_lock_init(&sclp_con_lock);
333*4882a593Smuzhiyun 	sclp_conbuf = NULL;
334*4882a593Smuzhiyun 	timer_setup(&sclp_con_timer, sclp_console_timeout, 0);
335*4882a593Smuzhiyun 
336*4882a593Smuzhiyun 	/* Set output format */
337*4882a593Smuzhiyun 	if (MACHINE_IS_VM)
338*4882a593Smuzhiyun 		/*
339*4882a593Smuzhiyun 		 * save 4 characters for the CPU number
340*4882a593Smuzhiyun 		 * written at start of each line by VM/CP
341*4882a593Smuzhiyun 		 */
342*4882a593Smuzhiyun 		sclp_con_columns = 76;
343*4882a593Smuzhiyun 	else
344*4882a593Smuzhiyun 		sclp_con_columns = 80;
345*4882a593Smuzhiyun 	sclp_con_width_htab = 8;
346*4882a593Smuzhiyun 
347*4882a593Smuzhiyun 	/* enable printk-access to this driver */
348*4882a593Smuzhiyun 	atomic_notifier_chain_register(&panic_notifier_list, &on_panic_nb);
349*4882a593Smuzhiyun 	register_reboot_notifier(&on_reboot_nb);
350*4882a593Smuzhiyun 	register_console(&sclp_console);
351*4882a593Smuzhiyun 	return 0;
352*4882a593Smuzhiyun }
353*4882a593Smuzhiyun 
354*4882a593Smuzhiyun console_initcall(sclp_console_init);
355