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