aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/s390/char/sclp_con.c
diff options
context:
space:
mode:
authorMichael Holzheu <holzheu@de.ibm.com>2009-06-16 04:30:40 -0400
committerMartin Schwidefsky <schwidefsky@de.ibm.com>2009-06-16 04:31:16 -0400
commit62b7494209495847269a6ce0504cbefd23d42eb1 (patch)
tree6318661837ddd250dc86b48a94f44fa2d1ae9421 /drivers/s390/char/sclp_con.c
parent3ef32e62cb55785f6f1b5ad7290744f74bdadaf0 (diff)
[S390] pm: power management support for SCLP drivers.
The SCLP base driver defines a new notifier call back for all upper level SCLP drivers, like the SCLP console, etc. This guarantees that in suspend first the upper level drivers are suspended and afterwards the SCLP base driver. For resume it is the other way round. The SCLP base driver itself registers a new platform device at the platform bus and gets PM notifications via the dev_pm_ops. In suspend, the SCLP base driver switches off the receiver and sender mask This is done in sclp_deactivate(). After suspend all new requests will be rejected with -EIO and no more interrupts will be received, because the masks are switched off. For resume the sender and receiver masks are reset in the sclp_reactivate() function. When the SCLP console is suspended, all new messages are cached in the sclp console buffers. In resume, all the cached messages are written to the console. In addition to that we have an early resume function that removes the cached messages from the suspend image. Signed-off-by: Michael Holzheu <holzheu@de.ibm.com> Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
Diffstat (limited to 'drivers/s390/char/sclp_con.c')
-rw-r--r--drivers/s390/char/sclp_con.c139
1 files changed, 99 insertions, 40 deletions
diff --git a/drivers/s390/char/sclp_con.c b/drivers/s390/char/sclp_con.c
index 9a25c4bd1421..336811a77672 100644
--- a/drivers/s390/char/sclp_con.c
+++ b/drivers/s390/char/sclp_con.c
@@ -1,11 +1,9 @@
1/* 1/*
2 * drivers/s390/char/sclp_con.c 2 * SCLP line mode console driver
3 * SCLP line mode console driver
4 * 3 *
5 * S390 version 4 * Copyright IBM Corp. 1999, 2009
6 * Copyright (C) 1999 IBM Deutschland Entwicklung GmbH, IBM Corporation 5 * Author(s): Martin Peschke <mpeschke@de.ibm.com>
7 * Author(s): Martin Peschke <mpeschke@de.ibm.com> 6 * Martin Schwidefsky <schwidefsky@de.ibm.com>
8 * Martin Schwidefsky <schwidefsky@de.ibm.com>
9 */ 7 */
10 8
11#include <linux/kmod.h> 9#include <linux/kmod.h>
@@ -32,13 +30,14 @@ static spinlock_t sclp_con_lock;
32static struct list_head sclp_con_pages; 30static struct list_head sclp_con_pages;
33/* List of full struct sclp_buffer structures ready for output */ 31/* List of full struct sclp_buffer structures ready for output */
34static struct list_head sclp_con_outqueue; 32static struct list_head sclp_con_outqueue;
35/* Counter how many buffers are emitted (max 1) and how many */
36/* are on the output queue. */
37static int sclp_con_buffer_count;
38/* Pointer to current console buffer */ 33/* Pointer to current console buffer */
39static struct sclp_buffer *sclp_conbuf; 34static struct sclp_buffer *sclp_conbuf;
40/* Timer for delayed output of console messages */ 35/* Timer for delayed output of console messages */
41static struct timer_list sclp_con_timer; 36static struct timer_list sclp_con_timer;
37/* Suspend mode flag */
38static int sclp_con_suspended;
39/* Flag that output queue is currently running */
40static int sclp_con_queue_running;
42 41
43/* Output format for console messages */ 42/* Output format for console messages */
44static unsigned short sclp_con_columns; 43static unsigned short sclp_con_columns;
@@ -53,42 +52,71 @@ sclp_conbuf_callback(struct sclp_buffer *buffer, int rc)
53 do { 52 do {
54 page = sclp_unmake_buffer(buffer); 53 page = sclp_unmake_buffer(buffer);
55 spin_lock_irqsave(&sclp_con_lock, flags); 54 spin_lock_irqsave(&sclp_con_lock, flags);
55
56 /* Remove buffer from outqueue */ 56 /* Remove buffer from outqueue */
57 list_del(&buffer->list); 57 list_del(&buffer->list);
58 sclp_con_buffer_count--;
59 list_add_tail((struct list_head *) page, &sclp_con_pages); 58 list_add_tail((struct list_head *) page, &sclp_con_pages);
59
60 /* Check if there is a pending buffer on the out queue. */ 60 /* Check if there is a pending buffer on the out queue. */
61 buffer = NULL; 61 buffer = NULL;
62 if (!list_empty(&sclp_con_outqueue)) 62 if (!list_empty(&sclp_con_outqueue))
63 buffer = list_entry(sclp_con_outqueue.next, 63 buffer = list_first_entry(&sclp_con_outqueue,
64 struct sclp_buffer, list); 64 struct sclp_buffer, list);
65 if (!buffer || sclp_con_suspended) {
66 sclp_con_queue_running = 0;
67 spin_unlock_irqrestore(&sclp_con_lock, flags);
68 break;
69 }
65 spin_unlock_irqrestore(&sclp_con_lock, flags); 70 spin_unlock_irqrestore(&sclp_con_lock, flags);
66 } while (buffer && sclp_emit_buffer(buffer, sclp_conbuf_callback)); 71 } while (sclp_emit_buffer(buffer, sclp_conbuf_callback));
67} 72}
68 73
69static void 74/*
70sclp_conbuf_emit(void) 75 * Finalize and emit first pending buffer.
76 */
77static void sclp_conbuf_emit(void)
71{ 78{
72 struct sclp_buffer* buffer; 79 struct sclp_buffer* buffer;
73 unsigned long flags; 80 unsigned long flags;
74 int count;
75 int rc; 81 int rc;
76 82
77 spin_lock_irqsave(&sclp_con_lock, flags); 83 spin_lock_irqsave(&sclp_con_lock, flags);
78 buffer = sclp_conbuf; 84 if (sclp_conbuf)
85 list_add_tail(&sclp_conbuf->list, &sclp_con_outqueue);
79 sclp_conbuf = NULL; 86 sclp_conbuf = NULL;
80 if (buffer == NULL) { 87 if (sclp_con_queue_running || sclp_con_suspended)
81 spin_unlock_irqrestore(&sclp_con_lock, flags); 88 goto out_unlock;
82 return; 89 if (list_empty(&sclp_con_outqueue))
83 } 90 goto out_unlock;
84 list_add_tail(&buffer->list, &sclp_con_outqueue); 91 buffer = list_first_entry(&sclp_con_outqueue, struct sclp_buffer,
85 count = sclp_con_buffer_count++; 92 list);
93 sclp_con_queue_running = 1;
86 spin_unlock_irqrestore(&sclp_con_lock, flags); 94 spin_unlock_irqrestore(&sclp_con_lock, flags);
87 if (count) 95
88 return;
89 rc = sclp_emit_buffer(buffer, sclp_conbuf_callback); 96 rc = sclp_emit_buffer(buffer, sclp_conbuf_callback);
90 if (rc) 97 if (rc)
91 sclp_conbuf_callback(buffer, rc); 98 sclp_conbuf_callback(buffer, rc);
99 return;
100out_unlock:
101 spin_unlock_irqrestore(&sclp_con_lock, flags);
102}
103
104/*
105 * Wait until out queue is empty
106 */
107static void sclp_console_sync_queue(void)
108{
109 unsigned long flags;
110
111 spin_lock_irqsave(&sclp_con_lock, flags);
112 if (timer_pending(&sclp_con_timer))
113 del_timer_sync(&sclp_con_timer);
114 while (sclp_con_queue_running) {
115 spin_unlock_irqrestore(&sclp_con_lock, flags);
116 sclp_sync_wait();
117 spin_lock_irqsave(&sclp_con_lock, flags);
118 }
119 spin_unlock_irqrestore(&sclp_con_lock, flags);
92} 120}
93 121
94/* 122/*
@@ -123,6 +151,8 @@ sclp_console_write(struct console *console, const char *message,
123 /* make sure we have a console output buffer */ 151 /* make sure we have a console output buffer */
124 if (sclp_conbuf == NULL) { 152 if (sclp_conbuf == NULL) {
125 while (list_empty(&sclp_con_pages)) { 153 while (list_empty(&sclp_con_pages)) {
154 if (sclp_con_suspended)
155 goto out;
126 spin_unlock_irqrestore(&sclp_con_lock, flags); 156 spin_unlock_irqrestore(&sclp_con_lock, flags);
127 sclp_sync_wait(); 157 sclp_sync_wait();
128 spin_lock_irqsave(&sclp_con_lock, flags); 158 spin_lock_irqsave(&sclp_con_lock, flags);
@@ -157,6 +187,7 @@ sclp_console_write(struct console *console, const char *message,
157 sclp_con_timer.expires = jiffies + HZ/10; 187 sclp_con_timer.expires = jiffies + HZ/10;
158 add_timer(&sclp_con_timer); 188 add_timer(&sclp_con_timer);
159 } 189 }
190out:
160 spin_unlock_irqrestore(&sclp_con_lock, flags); 191 spin_unlock_irqrestore(&sclp_con_lock, flags);
161} 192}
162 193
@@ -168,30 +199,43 @@ sclp_console_device(struct console *c, int *index)
168} 199}
169 200
170/* 201/*
171 * This routine is called from panic when the kernel 202 * Make sure that all buffers will be flushed to the SCLP.
172 * is going to give up. We have to make sure that all buffers
173 * will be flushed to the SCLP.
174 */ 203 */
175static void 204static void
176sclp_console_flush(void) 205sclp_console_flush(void)
177{ 206{
207 sclp_conbuf_emit();
208 sclp_console_sync_queue();
209}
210
211/*
212 * Resume console: If there are cached messages, emit them.
213 */
214static void sclp_console_resume(void)
215{
178 unsigned long flags; 216 unsigned long flags;
179 217
218 spin_lock_irqsave(&sclp_con_lock, flags);
219 sclp_con_suspended = 0;
220 spin_unlock_irqrestore(&sclp_con_lock, flags);
180 sclp_conbuf_emit(); 221 sclp_conbuf_emit();
222}
223
224/*
225 * Suspend console: Set suspend flag and flush console
226 */
227static void sclp_console_suspend(void)
228{
229 unsigned long flags;
230
181 spin_lock_irqsave(&sclp_con_lock, flags); 231 spin_lock_irqsave(&sclp_con_lock, flags);
182 if (timer_pending(&sclp_con_timer)) 232 sclp_con_suspended = 1;
183 del_timer(&sclp_con_timer);
184 while (sclp_con_buffer_count > 0) {
185 spin_unlock_irqrestore(&sclp_con_lock, flags);
186 sclp_sync_wait();
187 spin_lock_irqsave(&sclp_con_lock, flags);
188 }
189 spin_unlock_irqrestore(&sclp_con_lock, flags); 233 spin_unlock_irqrestore(&sclp_con_lock, flags);
234 sclp_console_flush();
190} 235}
191 236
192static int 237static int sclp_console_notify(struct notifier_block *self,
193sclp_console_notify(struct notifier_block *self, 238 unsigned long event, void *data)
194 unsigned long event, void *data)
195{ 239{
196 sclp_console_flush(); 240 sclp_console_flush();
197 return NOTIFY_OK; 241 return NOTIFY_OK;
@@ -199,7 +243,7 @@ sclp_console_notify(struct notifier_block *self,
199 243
200static struct notifier_block on_panic_nb = { 244static struct notifier_block on_panic_nb = {
201 .notifier_call = sclp_console_notify, 245 .notifier_call = sclp_console_notify,
202 .priority = 1, 246 .priority = SCLP_PANIC_PRIO_CLIENT,
203}; 247};
204 248
205static struct notifier_block on_reboot_nb = { 249static struct notifier_block on_reboot_nb = {
@@ -221,6 +265,22 @@ static struct console sclp_console =
221}; 265};
222 266
223/* 267/*
268 * This function is called for SCLP suspend and resume events.
269 */
270void sclp_console_pm_event(enum sclp_pm_event sclp_pm_event)
271{
272 switch (sclp_pm_event) {
273 case SCLP_PM_EVENT_FREEZE:
274 sclp_console_suspend();
275 break;
276 case SCLP_PM_EVENT_RESTORE:
277 case SCLP_PM_EVENT_THAW:
278 sclp_console_resume();
279 break;
280 }
281}
282
283/*
224 * called by console_init() in drivers/char/tty_io.c at boot-time. 284 * called by console_init() in drivers/char/tty_io.c at boot-time.
225 */ 285 */
226static int __init 286static int __init
@@ -243,7 +303,6 @@ sclp_console_init(void)
243 } 303 }
244 INIT_LIST_HEAD(&sclp_con_outqueue); 304 INIT_LIST_HEAD(&sclp_con_outqueue);
245 spin_lock_init(&sclp_con_lock); 305 spin_lock_init(&sclp_con_lock);
246 sclp_con_buffer_count = 0;
247 sclp_conbuf = NULL; 306 sclp_conbuf = NULL;
248 init_timer(&sclp_con_timer); 307 init_timer(&sclp_con_timer);
249 308