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