Updated `README.md` with instructions for building/using the kernel module.
[xeon-phi-kernel-module] / host / linvcons.c
CommitLineData
800f879a
AT
1/*
2 * Copyright 2010-2017 Intel Corporation.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License, version 2,
6 * as published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
12 *
13 * Disclaimer: The codes contained in these modules may be specific to
14 * the Intel Software Development Platform codenamed Knights Ferry,
15 * and the Intel product codenamed Knights Corner, and are not backward
16 * compatible with other Intel products. Additionally, Intel will NOT
17 * support the codes or instruction set in future products.
18 *
19 * Intel offers no warranty of any kind regarding the code. This code is
20 * licensed on an "AS IS" basis and Intel is not obligated to provide
21 * any support, assistance, installation, training, or other services
22 * of any kind. Intel is also not obligated to provide any updates,
23 * enhancements or extensions. Intel specifically disclaims any warranty
24 * of merchantability, non-infringement, fitness for any particular
25 * purpose, and any other warranty.
26 *
27 * Further, Intel disclaims all liability of any kind, including but
28 * not limited to liability for infringement of any proprietary rights,
29 * relating to the use of the code, even if Intel is notified of the
30 * possibility of such liability. Except as expressly stated in an Intel
31 * license agreement provided with this code and agreed upon with Intel,
32 * no license, express or implied, by estoppel or otherwise, to any
33 * intellectual property rights is granted herein.
34 */
35
36#include "micint.h"
37
38/* TODO: Improve debug messages */
39
40static int micvcons_open(struct tty_struct * tty, struct file * filp);
41static void micvcons_close(struct tty_struct * tty, struct file * filp);
42static int micvcons_write(struct tty_struct * tty, const unsigned char *buf,
43 int count);
44static int micvcons_write_room(struct tty_struct *tty);
45static void micvcons_set_termios(struct tty_struct *tty, struct ktermios * old);
f4a476d1 46static void micvcons_timeout(struct timer_list *);
800f879a
AT
47static void micvcons_throttle(struct tty_struct *tty);
48static void micvcons_unthrottle(struct tty_struct *tty);
49static void micvcons_wakeup_readbuf(struct work_struct *work);
50static int micvcons_resume(struct _mic_ctx_t *mic_ctx);
51
52static struct tty_operations micvcons_tty_ops = {
53 .open = micvcons_open,
54 .close = micvcons_close,
55 .write = micvcons_write,
56 .write_room = micvcons_write_room,
57 .set_termios = micvcons_set_termios,
58 .throttle = micvcons_throttle,
59 .unthrottle = micvcons_unthrottle,
60};
61
62static struct tty_driver *micvcons_tty = NULL;
63static u16 extra_timeout = 0;
64static u8 restart_timer_flag = MICVCONS_TIMER_RESTART;
800f879a
AT
65static spinlock_t timer_list_lock;
66
f4a476d1
AT
67// The timer API introduced in Linux 4.14.0 includes a from_timer() macro built
68// atop the container_of() macro. As the kernel moves away from using
69// timer_list.data, it quasi-assumes that the timer_list struct will be
70// contained in some larger struct that also contains the data, hence this
71// timer_wrapper struct since it allows us to avoid any serious code changes to
72// the driver.
73static struct timer_wrapper {
74 struct timer_list vcons_timer;
75 struct list_head timer_list_head;
76} timer_wrapper ;
77
800f879a
AT
78int
79micvcons_create(int num_bds)
80{
81 micvcons_port_t *port;
82 bd_info_t *bd_info;
83 int bd, ret = 0;
84 char wq_name[14];
85 struct device *dev;
86
f4a476d1 87 INIT_LIST_HEAD(&timer_wrapper.timer_list_head);
800f879a
AT
88
89 if (micvcons_tty)
90 goto exit;
91
92 micvcons_tty = alloc_tty_driver(num_bds);
93 if (!micvcons_tty) {
94 ret = -ENOMEM;
95 goto exit;
96 }
97 micvcons_tty->owner = THIS_MODULE;
98 micvcons_tty->driver_name = MICVCONS_DEVICE_NAME;
99 micvcons_tty->name = MICVCONS_DEVICE_NAME;
100 micvcons_tty->major = 0;
101#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0))
102 micvcons_tty->minor_num = num_bds;
103#endif
104 micvcons_tty->minor_start = 0;
105 micvcons_tty->type = TTY_DRIVER_TYPE_SERIAL;
106 micvcons_tty->subtype = SERIAL_TYPE_NORMAL;
107 micvcons_tty->flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
108 micvcons_tty->init_termios = tty_std_termios;
109 micvcons_tty->init_termios.c_iflag = IGNCR;
110 micvcons_tty->init_termios.c_oflag = 0;
111 micvcons_tty->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
112 micvcons_tty->init_termios.c_lflag = 0;
113
114 tty_set_operations(micvcons_tty, &micvcons_tty_ops);
115
116 if ((ret = tty_register_driver(micvcons_tty)) != 0) {
117 printk("Failed to register vcons tty driver\n");
118 put_tty_driver(micvcons_tty);
119 micvcons_tty = NULL;
120 goto exit;
121 }
122
123 for (bd = 0; bd < num_bds; bd++) {
124 port = &mic_data.dd_ports[bd];
125 port->dp_bdinfo = mic_data.dd_bi[bd];
126
127 spin_lock_init(&port->dp_lock);
128 mutex_init (&port->dp_mutex);
129
130 bd_info = (bd_info_t *)port->dp_bdinfo;
131 bd_info->bi_port = port;
132
133#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0))
134 tty_port_init(&port->port);
135 dev = tty_port_register_device(&port->port, micvcons_tty, bd, NULL);
136#else
137 dev = tty_register_device(micvcons_tty, bd, NULL);
138 if (IS_ERR(dev)) {
139 printk("Failed to register vcons tty device\n");
140 micvcons_destroy(bd);
141 ret = PTR_ERR(dev);
142 goto exit;
143 }
144#endif
145 snprintf(wq_name, sizeof(wq_name), "VCONS MIC %d", bd);
146 port->dp_wq = __mic_create_singlethread_workqueue(wq_name);
147 if (!port->dp_wq) {
148 printk(KERN_ERR "%s: create_singlethread_workqueue\n",
149 __func__);
150 tty_unregister_device(micvcons_tty, bd);
151 micvcons_destroy(bd);
152 ret = -ENOMEM;
153 goto exit;
154 }
155 INIT_WORK(&port->dp_wakeup_read_buf, micvcons_wakeup_readbuf);
156 }
f4a476d1 157 timer_setup(&timer_wrapper.vcons_timer, micvcons_timeout, 0);
800f879a
AT
158exit:
159 return ret;
160}
161
162void micvcons_destroy(int num_bds)
163{
164 int bd, ret;
165 micvcons_port_t *port;
166
167 if (!micvcons_tty)
168 return;
169 for (bd = 0; bd < num_bds; bd++) {
170 port = &mic_data.dd_ports[bd];
171 destroy_workqueue(port->dp_wq);
172 tty_unregister_device(micvcons_tty, bd);
173 }
174 ret = tty_unregister_driver(micvcons_tty);
175 put_tty_driver(micvcons_tty);
176 micvcons_tty = NULL;
177
178 if (ret)
179 printk(KERN_ERR "tty unregister_driver failed with code %d\n", ret);
180}
181
182static int
183micvcons_open(struct tty_struct * tty, struct file * filp)
184{
185 micvcons_port_t *port = &mic_data.dd_ports[tty->index];
186 int ret = 0;
187 mic_ctx_t *mic_ctx = get_per_dev_ctx(tty->index);
188
189 tty->driver_data = port;
190
191 mutex_lock(&port->dp_mutex);
192 spin_lock_bh(&port->dp_lock);
193
194 if ((filp->f_flags & O_ACCMODE) != O_RDONLY) {
195 if (port->dp_writer) {
196 ret = -EBUSY;
197 goto exit_locked;
198 }
199 port->dp_writer = filp;
200 port->dp_bytes = 0;
201 }
202
203 if ((filp->f_flags & O_ACCMODE) != O_WRONLY) {
204 if (port->dp_reader) {
205 ret = -EBUSY;
206 goto exit_locked;
207 }
208 port->dp_reader = filp;
209 port->dp_canread = 1;
210 }
211
212#if (LINUX_VERSION_CODE < KERNEL_VERSION(3,10,0))
213 tty->low_latency = 0;
214#endif
215
216 if (!port->dp_tty)
217 port->dp_tty = tty;
218 if (!port->dp_vcons)
219 port->dp_vcons = &mic_ctx->bi_vcons;
220 if (tty->count == 1) {
221 ret = micvcons_start(mic_ctx);
222 if (ret != 0)
223 goto exit_locked;
224 spin_lock(&timer_list_lock);
f4a476d1
AT
225 list_add_tail_rcu(&port->list_member, &timer_wrapper.timer_list_head);
226 if (list_is_singular(&timer_wrapper.timer_list_head)) {
800f879a 227 restart_timer_flag = MICVCONS_TIMER_RESTART;
f4a476d1 228 mod_timer(&timer_wrapper.vcons_timer, jiffies +
800f879a
AT
229 msecs_to_jiffies(MICVCONS_SHORT_TIMEOUT));
230 }
231 spin_unlock(&timer_list_lock);
232 }
233
234exit_locked:
235 spin_unlock_bh(&port->dp_lock);
236 mutex_unlock(&port->dp_mutex);
237 return ret;
238}
239
240static inline void
241micvcons_del_timer_entry(micvcons_port_t *port)
242{
243 spin_lock(&timer_list_lock);
244 list_del_rcu(&port->list_member);
f4a476d1 245 if (list_empty(&timer_wrapper.timer_list_head)) {
800f879a
AT
246 restart_timer_flag = MICVCONS_TIMER_SHUTDOWN;
247 spin_unlock(&timer_list_lock);
f4a476d1 248 del_timer_sync(&timer_wrapper.vcons_timer);
800f879a
AT
249 } else {
250 spin_unlock(&timer_list_lock);
251 }
252 synchronize_rcu();
253}
254
255static void
256micvcons_close(struct tty_struct * tty, struct file * filp)
257{
258 micvcons_port_t *port = (micvcons_port_t *)tty->driver_data;
259
260 mutex_lock(&port->dp_mutex);
261 if (tty->count == 1) {
262 micvcons_del_timer_entry(port);
263 flush_workqueue(port->dp_wq);
264 }
265 spin_lock_bh(&port->dp_lock);
266 if (port->dp_reader == filp)
267 port->dp_reader = 0;
268
269 if (port->dp_writer == filp)
270 port->dp_writer = 0;
271
272 if (tty->count == 1)
273 port->dp_tty = 0;
274 spin_unlock_bh(&port->dp_lock);
275 mutex_unlock(&port->dp_mutex);
276}
277
278static int
279micvcons_write(struct tty_struct * tty, const unsigned char *buf, int count)
280{
281 micvcons_port_t *port = (micvcons_port_t *)tty->driver_data;
282 mic_ctx_t *mic_ctx = get_per_dev_ctx(tty->index);
283 int bytes=0, status;
284 struct vcons_buf *vcons_host_header;
285 u8 card_alive = 1;
286
287 spin_lock_bh(&port->dp_lock);
288 vcons_host_header = (struct vcons_buf *)port->dp_vcons->dc_hdr_virt;
289 if (vcons_host_header->mic_magic == MIC_VCONS_SLEEPING) {
290 status = micvcons_resume(mic_ctx);
291 if (status != 0) {
292 /* If card can not wakeup, it is dead. */
293 card_alive = 0;
294 goto exit;
295 }
296 }
297 if (vcons_host_header->mic_magic != MIC_VCONS_READY)
298 goto exit;
299 bytes = micvcons_port_write(port, buf, count);
300 if (bytes) {
301 mic_send_hvc_intr(mic_ctx);
302 extra_timeout = 0;
303 }
304exit:
305 spin_unlock_bh(&port->dp_lock);
306 if (!card_alive)
307 micvcons_del_timer_entry(port);
308 return bytes;
309}
310
311static int
312micvcons_write_room(struct tty_struct *tty)
313{
314 micvcons_port_t *port = (micvcons_port_t *)tty->driver_data;
315 int room;
316
317 spin_lock_bh(&port->dp_lock);
318 if (port->dp_out)
319 room = micscif_rb_space(port->dp_out);
320 else
321 room = 0;
322 spin_unlock_bh(&port->dp_lock);
323
324 return room;
325}
326
327static void
328micvcons_set_termios(struct tty_struct *tty, struct ktermios * old)
329{
330}
331
332static int
333micvcons_readchars(micvcons_port_t *port)
334{
335 int len, ret, get_count;
336 int bytes_total = 0;
337 int bytes_read = 0;
338 char buf[64];
339
340 for (;;) {
341 len = micscif_rb_count(port->dp_in, sizeof(buf));
342 if (!len)
343 break;
344 get_count = min(len, (int)sizeof(buf));
345 ret = micscif_rb_get_next(port->dp_in, buf, get_count);
346 micscif_rb_update_read_ptr(port->dp_in);
347 if (port->dp_reader && port->dp_canread) {
348#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3,10,0))
349 if ((bytes_read = tty_insert_flip_string(
350 &port->port, buf, get_count)) != 0)
351 tty_flip_buffer_push(&port->port);
352#else
353 bytes_read = tty_insert_flip_string(port->dp_tty,
354 buf, get_count);
355 tty_flip_buffer_push(port->dp_tty);
356#endif
357 bytes_total += bytes_read;
358 if (bytes_read != get_count) {
359 printk(KERN_WARNING "dropping characters: \
360 bytes_read %d, get_count %d\n",
361 bytes_read, get_count);
362 break;
363 }
364 }
365 }
366 return bytes_total;
367}
368
369static int
370micvcons_initport(micvcons_port_t *port)
371{
372 struct vcons_buf *vcons_host_header;
373 struct vcons_mic_header *vcons_mic_header;
374 char *mic_hdr, *mic_buf, *host_buf;
375
376 vcons_host_header = (struct vcons_buf *)port->dp_vcons->dc_hdr_virt;
377 if (!vcons_host_header) {
378 printk(KERN_ERR "vcons_host_header NULL\n");
379 return -EFAULT;
380 }
381
382 host_buf = (char *)port->dp_vcons->dc_buf_virt;
383 if (!host_buf) {
384 printk(KERN_ERR "host_buf NULL\n");
385 return -EFAULT;
386 }
387
388 if (port->dp_bdinfo->bi_ctx.bi_family == FAMILY_ABR) {
389 set_pci_aperture(&port->dp_bdinfo->bi_ctx,
390 (port->dp_bdinfo->bi_ctx.aper.len - PAGE_SIZE) >> PAGE_SHIFT,
391 vcons_host_header->i_hdr_addr & PAGE_MASK, PAGE_SIZE);
392 mic_hdr = port->dp_bdinfo->bi_ctx.aper.va +
393 port->dp_bdinfo->bi_ctx.aper.len - PAGE_SIZE;
394 mic_buf = mic_hdr + PAGE_SIZE/2;
395 } else {
396 mic_hdr = port->dp_bdinfo->bi_ctx.aper.va + vcons_host_header->i_hdr_addr;
397 mic_buf = port->dp_bdinfo->bi_ctx.aper.va + vcons_host_header->i_buf_addr;
398 }
399
400 port->dp_in = kmalloc(sizeof(struct micscif_rb), GFP_ATOMIC);
401 if (port->dp_in)
402 port->dp_out = kmalloc(sizeof(struct micscif_rb), GFP_ATOMIC);
403 else
404 return -ENOMEM;
405
406 if (port->dp_out) {
407 vcons_mic_header = (struct vcons_mic_header *)mic_hdr;
408 micscif_rb_init(port->dp_in,
409 &vcons_mic_header->o_rd,
410 &vcons_host_header->o_wr,
411 host_buf,
412 vcons_host_header->o_size);
413 micscif_rb_init(port->dp_out, &vcons_host_header->i_rd,
414 &vcons_mic_header->i_wr,
415 mic_buf,
416 vcons_host_header->i_size);
417 wmb();
418 writel(MIC_VCONS_HOST_OPEN, &vcons_mic_header->host_status);
419 } else {
420 kfree(port->dp_in);
421 return -ENOMEM;
422 }
423 return 0;
424}
425
426static int
427micvcons_readport(micvcons_port_t *port)
428{
429 int num_chars_read = 0, status;
430 static uint32_t prev_mic_magic;
431 struct vcons_buf *vcons_host_header;
432
433 if (!port || !port->dp_vcons)
434 return 0;
435
436 spin_lock_bh(&port->dp_lock);
437 if (!port->dp_tty) {
438 spin_unlock_bh(&port->dp_lock);
439 return 0;
440 }
441
442 vcons_host_header = (struct vcons_buf *)port->dp_vcons->dc_hdr_virt;
443 if ((vcons_host_header->mic_magic != MIC_VCONS_READY) &&
444 (vcons_host_header->mic_magic != MIC_VCONS_SLEEPING)) {
445 if ((vcons_host_header->mic_magic == MIC_VCONS_RB_VER_ERR)
446 && (vcons_host_header->mic_magic != prev_mic_magic)) {
447 printk(KERN_ERR "Card and host ring buffer versions mismatch.");
448 printk(KERN_ERR "Card version: %d, Host version: %d \n",
449 vcons_host_header->mic_rb_ver,
450 vcons_host_header->host_rb_ver);
451 }
452 goto exit;
453 }
454 if (!port->dp_in) {
455 status = micvcons_initport(port);
456 if (status != 0) {
457 spin_unlock_bh(&port->dp_lock);
458 return status;
459 }
460 }
461
462 if (port->dp_in) {
463 if (vcons_host_header->mic_magic == MIC_VCONS_SLEEPING) {
464 /*
465 * If the card is sleeping and there is data in the
466 * buffer, schedule work in a work queue to wake-up
467 * the card and read from the buffer.
468 */
469 if (micscif_rb_count(port->dp_in, 1))
470 queue_work(port->dp_wq,
471 &port->dp_wakeup_read_buf);
472 } else {
473 num_chars_read = micvcons_readchars(port);
474 tty_wakeup(port->dp_tty);
475 }
476 }
477exit:
478 prev_mic_magic = vcons_host_header->mic_magic;
479 spin_unlock_bh(&port->dp_lock);
480 return num_chars_read;
481}
482
483static void
484micvcons_wakeup_readbuf(struct work_struct *work)
485{
486 u8 card_alive = 1;
487 int status;
488 micvcons_port_t *port;
489 struct vcons_buf *vcons_host_header;
490
491 port = container_of(work, micvcons_port_t, dp_wakeup_read_buf);
492
493 vcons_host_header = (struct vcons_buf *)port->dp_vcons->dc_hdr_virt;
494 spin_lock_bh(&port->dp_lock);
495 status = micvcons_resume(get_per_dev_ctx(port->dp_tty->index));
496 if (status == 0) {
497 micvcons_readchars(port);
498 tty_wakeup(port->dp_tty);
499 } else {
500 /* If card can not wakeup, it is dead. */
501 card_alive = 0;
502 }
503 spin_unlock_bh(&port->dp_lock);
504 if (!card_alive)
505 micvcons_del_timer_entry(port);
506}
507
508static void
f4a476d1 509micvcons_timeout(struct timer_list *t)
800f879a 510{
f4a476d1
AT
511 struct timer_wrapper *tm_wrap = from_timer(tm_wrap, t, vcons_timer);
512 struct list_head *timer_list_ptr = &tm_wrap->timer_list_head;
800f879a
AT
513 micvcons_port_t *port;
514 u8 console_active = 0;
515 int num_chars_read = 0;
516
517 rcu_read_lock();
518 list_for_each_entry_rcu(port, timer_list_ptr, list_member) {
519 num_chars_read = micvcons_readport(port);
520 if (num_chars_read != 0)
521 console_active = 1;
522 }
523 rcu_read_unlock();
524
525 spin_lock(&timer_list_lock);
526 if (restart_timer_flag == MICVCONS_TIMER_RESTART) {
527 extra_timeout = (console_active ? 0 :
528 extra_timeout + MICVCONS_SHORT_TIMEOUT);
529 extra_timeout = min(extra_timeout, (u16)MICVCONS_MAX_TIMEOUT);
f4a476d1 530 mod_timer(&timer_wrapper.vcons_timer, jiffies +
800f879a
AT
531 msecs_to_jiffies(MICVCONS_SHORT_TIMEOUT+extra_timeout));
532 }
533 spin_unlock(&timer_list_lock);
534}
535
536static void
537micvcons_throttle(struct tty_struct *tty)
538{
539 micvcons_port_t *port = (micvcons_port_t *)tty->driver_data;
540 port->dp_canread = 0;
541}
542
543static void
544micvcons_unthrottle(struct tty_struct *tty)
545{
546 micvcons_port_t *port = (micvcons_port_t *)tty->driver_data;
547 port->dp_canread = 1;
548}
549
550int micvcons_start(mic_ctx_t *mic_ctx)
551{
552 struct vcons_buf *vcons_host_header;
553 int status;
554 micvcons_port_t *port = mic_ctx->bd_info->bi_port;
555
556 vcons_host_header = (struct vcons_buf *)port->dp_vcons->dc_hdr_virt;
557 if (vcons_host_header->mic_magic == MIC_VCONS_SLEEPING) {
558 status = micvcons_resume(mic_ctx);
559 if (status != 0)
560 return status;
561 }
562 if (vcons_host_header->mic_magic == MIC_VCONS_READY) {
563 if (!port->dp_in) {
564 status = micvcons_initport(port);
565 if (status != 0)
566 return status;
567 }
568 }
569 return 0;
570}
571
572int micvcons_port_write(struct micvcons_port *port, const unsigned char *buf,
573 int count)
574{
575 int ret;
576 uint32_t bytes = 0;
577
578 if (port->dp_out) {
579 bytes = min(count, micscif_rb_space(port->dp_out));
580 ret = micscif_rb_write(port->dp_out, (void *)buf, bytes);
581 BUG_ON(ret);
582 port->dp_bytes += bytes;
583 micscif_rb_commit(port->dp_out);
584 }
585 return bytes;
586}
587
588/**
589 * micvcons_stop - cleans up before a node is rebooted
590 * @ mic_ctx: node to clean up
591 *
592 * Called before rebooting a node, reads remaining characters
593 * from the node's vcons output buffer, resets the input/output
594 * ring buffers so that things work when the node comes up again
595 */
596void
597micvcons_stop(mic_ctx_t *mic_ctx)
598{
599 micvcons_port_t *port;
600 struct vcons_buf *vcons_host_header;
601
602 port = mic_ctx->bd_info->bi_port;
603 micvcons_readport(port);
604 spin_lock_bh(&port->dp_lock);
605 if (port->dp_in) {
606 vcons_host_header = (struct vcons_buf *)port->dp_vcons->dc_hdr_virt;
607 vcons_host_header->mic_magic = 0;
608 kfree(port->dp_in);
609 kfree(port->dp_out);
610 port->dp_in = NULL;
611 port->dp_out = NULL;
612 }
613 spin_unlock_bh(&port->dp_lock);
614}
615
616/**
617 * micvcons_resume - sets the state of a node's console to ready
618 * @ mic_ctx: node to clean up
619 *
620 * @ return: zero if successful.
621 * called before resuming a node from PC6. MUST acquire the spinlock
622 * port->dp_lock with bottom-halves disabled before calling this function.
623 */
624static int
625micvcons_resume(mic_ctx_t *mic_ctx)
626{
627 int status = 0;
628 micvcons_port_t *port;
629 struct vcons_buf *vcons_host_header;
630
631 port = mic_ctx->bd_info->bi_port;
632 vcons_host_header = mic_ctx->bi_vcons.dc_hdr_virt;
633 if (vcons_host_header->mic_magic == MIC_VCONS_SLEEPING) {
634 do {
635 vcons_host_header->mic_magic = MIC_VCONS_WAKINGUP;
636 spin_unlock_bh(&port->dp_lock);
637 status = micscif_connect_node(mic_get_scifnode_id(mic_ctx), false);
638 spin_lock_bh(&port->dp_lock);
639 } while ((status == 0) &&
640 (vcons_host_header->mic_magic == MIC_VCONS_SLEEPING));
641 if (status == 0)
642 vcons_host_header->mic_magic = MIC_VCONS_READY;
643 }
644 return status;
645}
646
647/**
648 * micvcons_pm_disconnect_node - Check if a card can be put to sleep in case
649 * there is any activity on the virtual console. If yes, it also sets the
650 * internal state of a node's console to sleeping.
651 * @ node_bitmask: bits set indicate which cards to check.
652 * Bit-1 for the first, Bit-2 for the second,...
653 * Ignore Bit-0 which indicates host.
654 * @ return: bits set indicating which cards can sleep.
655 * This is called from PM to check if a card can be put to sleep (PC-6 state).
656 * This is called when the node is disconnected from the SCIF network
657 * before putting it into the PC6 state where it should no longer
658 * receive an PCIe transactions until woken up by the host driver.
659 */
660int
661micvcons_pm_disconnect_node(uint8_t *node_bitmask, enum disconn_type type)
662{
663 int err = 0;
664 if ((type == DISCONN_TYPE_POWER_MGMT) && (node_bitmask)) {
665 int i = 0;
666 mic_ctx_t *mic_ctx;
667 micvcons_port_t *port;
668 struct vcons_buf *vcons_host_header;
669
670 for (i = 0; i <= mic_data.dd_numdevs; i++) {
671 if (!get_nodemask_bit(node_bitmask, i))
672 continue;
673
674 if (!(mic_ctx = get_per_dev_ctx(i - 1)))
675 continue;
676
677 port = mic_ctx->bd_info->bi_port;
678 micvcons_readport(port);
679 /*
680 * If this function is called when virtual console is
681 * not active, port->dp_vcons needs to be initialized.
682 */
683 if (!port->dp_vcons)
684 port->dp_vcons = &mic_ctx->bi_vcons;
685
686 vcons_host_header = (struct vcons_buf *)port->dp_vcons->dc_hdr_virt;
687 spin_lock_bh(&port->dp_lock);
688 vcons_host_header->mic_magic = MIC_VCONS_SLEEPING;
689 spin_unlock_bh(&port->dp_lock);
690 }
691 }
692
693 return err;
694}
695