diff options
Diffstat (limited to 'drivers/s390')
-rw-r--r-- | drivers/s390/crypto/ap_bus.c | 85 |
1 files changed, 83 insertions, 2 deletions
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 9c148406b980..727a809636d8 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c | |||
@@ -54,6 +54,12 @@ static int ap_poll_thread_start(void); | |||
54 | static void ap_poll_thread_stop(void); | 54 | static void ap_poll_thread_stop(void); |
55 | static void ap_request_timeout(unsigned long); | 55 | static void ap_request_timeout(unsigned long); |
56 | static inline void ap_schedule_poll_timer(void); | 56 | static inline void ap_schedule_poll_timer(void); |
57 | static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags); | ||
58 | static int ap_device_remove(struct device *dev); | ||
59 | static int ap_device_probe(struct device *dev); | ||
60 | static void ap_interrupt_handler(void *unused1, void *unused2); | ||
61 | static void ap_reset(struct ap_device *ap_dev); | ||
62 | static void ap_config_timeout(unsigned long ptr); | ||
57 | 63 | ||
58 | /* | 64 | /* |
59 | * Module description. | 65 | * Module description. |
@@ -101,6 +107,10 @@ static struct hrtimer ap_poll_timer; | |||
101 | * If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.*/ | 107 | * If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.*/ |
102 | static unsigned long long poll_timeout = 250000; | 108 | static unsigned long long poll_timeout = 250000; |
103 | 109 | ||
110 | /* Suspend flag */ | ||
111 | static int ap_suspend_flag; | ||
112 | static struct bus_type ap_bus_type; | ||
113 | |||
104 | /** | 114 | /** |
105 | * ap_using_interrupts() - Returns non-zero if interrupt support is | 115 | * ap_using_interrupts() - Returns non-zero if interrupt support is |
106 | * available. | 116 | * available. |
@@ -617,10 +627,79 @@ static int ap_uevent (struct device *dev, struct kobj_uevent_env *env) | |||
617 | return retval; | 627 | return retval; |
618 | } | 628 | } |
619 | 629 | ||
630 | static int ap_bus_suspend(struct device *dev, pm_message_t state) | ||
631 | { | ||
632 | struct ap_device *ap_dev = to_ap_dev(dev); | ||
633 | unsigned long flags; | ||
634 | |||
635 | if (!ap_suspend_flag) { | ||
636 | ap_suspend_flag = 1; | ||
637 | |||
638 | /* Disable scanning for devices, thus we do not want to scan | ||
639 | * for them after removing. | ||
640 | */ | ||
641 | del_timer_sync(&ap_config_timer); | ||
642 | if (ap_work_queue != NULL) { | ||
643 | destroy_workqueue(ap_work_queue); | ||
644 | ap_work_queue = NULL; | ||
645 | } | ||
646 | tasklet_disable(&ap_tasklet); | ||
647 | } | ||
648 | /* Poll on the device until all requests are finished. */ | ||
649 | do { | ||
650 | flags = 0; | ||
651 | __ap_poll_device(ap_dev, &flags); | ||
652 | } while ((flags & 1) || (flags & 2)); | ||
653 | |||
654 | ap_device_remove(dev); | ||
655 | return 0; | ||
656 | } | ||
657 | |||
658 | static int ap_bus_resume(struct device *dev) | ||
659 | { | ||
660 | int rc = 0; | ||
661 | struct ap_device *ap_dev = to_ap_dev(dev); | ||
662 | |||
663 | if (ap_suspend_flag) { | ||
664 | ap_suspend_flag = 0; | ||
665 | if (!ap_interrupts_available()) | ||
666 | ap_interrupt_indicator = NULL; | ||
667 | ap_device_probe(dev); | ||
668 | ap_reset(ap_dev); | ||
669 | setup_timer(&ap_dev->timeout, ap_request_timeout, | ||
670 | (unsigned long) ap_dev); | ||
671 | ap_scan_bus(NULL); | ||
672 | init_timer(&ap_config_timer); | ||
673 | ap_config_timer.function = ap_config_timeout; | ||
674 | ap_config_timer.data = 0; | ||
675 | ap_config_timer.expires = jiffies + ap_config_time * HZ; | ||
676 | add_timer(&ap_config_timer); | ||
677 | ap_work_queue = create_singlethread_workqueue("kapwork"); | ||
678 | if (!ap_work_queue) | ||
679 | return -ENOMEM; | ||
680 | tasklet_enable(&ap_tasklet); | ||
681 | if (!ap_using_interrupts()) | ||
682 | ap_schedule_poll_timer(); | ||
683 | else | ||
684 | tasklet_schedule(&ap_tasklet); | ||
685 | if (ap_thread_flag) | ||
686 | rc = ap_poll_thread_start(); | ||
687 | } else { | ||
688 | ap_device_probe(dev); | ||
689 | ap_reset(ap_dev); | ||
690 | setup_timer(&ap_dev->timeout, ap_request_timeout, | ||
691 | (unsigned long) ap_dev); | ||
692 | } | ||
693 | |||
694 | return rc; | ||
695 | } | ||
696 | |||
620 | static struct bus_type ap_bus_type = { | 697 | static struct bus_type ap_bus_type = { |
621 | .name = "ap", | 698 | .name = "ap", |
622 | .match = &ap_bus_match, | 699 | .match = &ap_bus_match, |
623 | .uevent = &ap_uevent, | 700 | .uevent = &ap_uevent, |
701 | .suspend = ap_bus_suspend, | ||
702 | .resume = ap_bus_resume | ||
624 | }; | 703 | }; |
625 | 704 | ||
626 | static int ap_device_probe(struct device *dev) | 705 | static int ap_device_probe(struct device *dev) |
@@ -1066,7 +1145,7 @@ ap_config_timeout(unsigned long ptr) | |||
1066 | */ | 1145 | */ |
1067 | static inline void ap_schedule_poll_timer(void) | 1146 | static inline void ap_schedule_poll_timer(void) |
1068 | { | 1147 | { |
1069 | if (ap_using_interrupts()) | 1148 | if (ap_using_interrupts() || ap_suspend_flag) |
1070 | return; | 1149 | return; |
1071 | if (hrtimer_is_queued(&ap_poll_timer)) | 1150 | if (hrtimer_is_queued(&ap_poll_timer)) |
1072 | return; | 1151 | return; |
@@ -1384,6 +1463,8 @@ static int ap_poll_thread(void *data) | |||
1384 | 1463 | ||
1385 | set_user_nice(current, 19); | 1464 | set_user_nice(current, 19); |
1386 | while (1) { | 1465 | while (1) { |
1466 | if (ap_suspend_flag) | ||
1467 | return 0; | ||
1387 | if (need_resched()) { | 1468 | if (need_resched()) { |
1388 | schedule(); | 1469 | schedule(); |
1389 | continue; | 1470 | continue; |
@@ -1414,7 +1495,7 @@ static int ap_poll_thread_start(void) | |||
1414 | { | 1495 | { |
1415 | int rc; | 1496 | int rc; |
1416 | 1497 | ||
1417 | if (ap_using_interrupts()) | 1498 | if (ap_using_interrupts() || ap_suspend_flag) |
1418 | return 0; | 1499 | return 0; |
1419 | mutex_lock(&ap_poll_thread_mutex); | 1500 | mutex_lock(&ap_poll_thread_mutex); |
1420 | if (!ap_poll_kthread) { | 1501 | if (!ap_poll_kthread) { |