[S390] ap/zcrypt: Suspend/Resume ap bus and zcrypt

Add Suspend/Resume support to ap bus and zcrypt. All enhancements are
done in the ap bus. No changes in the crypto card specific part are
necessary.

Signed-off-by: Felix Beck <felix.beck@de.ibm.com>
Signed-off-by: Martin Schwidefsky <schwidefsky@de.ibm.com>
diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c
index 9c14840..727a809 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -54,6 +54,12 @@
 static void ap_poll_thread_stop(void);
 static void ap_request_timeout(unsigned long);
 static inline void ap_schedule_poll_timer(void);
+static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags);
+static int ap_device_remove(struct device *dev);
+static int ap_device_probe(struct device *dev);
+static void ap_interrupt_handler(void *unused1, void *unused2);
+static void ap_reset(struct ap_device *ap_dev);
+static void ap_config_timeout(unsigned long ptr);
 
 /*
  * Module description.
@@ -101,6 +107,10 @@
  * If z/VM change to 1500000 nanoseconds to adjust to z/VM polling.*/
 static unsigned long long poll_timeout = 250000;
 
+/* Suspend flag */
+static int ap_suspend_flag;
+static struct bus_type ap_bus_type;
+
 /**
  * ap_using_interrupts() - Returns non-zero if interrupt support is
  * available.
@@ -617,10 +627,79 @@
 	return retval;
 }
 
+static int ap_bus_suspend(struct device *dev, pm_message_t state)
+{
+	struct ap_device *ap_dev = to_ap_dev(dev);
+	unsigned long flags;
+
+	if (!ap_suspend_flag) {
+		ap_suspend_flag = 1;
+
+		/* Disable scanning for devices, thus we do not want to scan
+		 * for them after removing.
+		 */
+		del_timer_sync(&ap_config_timer);
+		if (ap_work_queue != NULL) {
+			destroy_workqueue(ap_work_queue);
+			ap_work_queue = NULL;
+		}
+		tasklet_disable(&ap_tasklet);
+	}
+	/* Poll on the device until all requests are finished. */
+	do {
+		flags = 0;
+		__ap_poll_device(ap_dev, &flags);
+	} while ((flags & 1) || (flags & 2));
+
+	ap_device_remove(dev);
+	return 0;
+}
+
+static int ap_bus_resume(struct device *dev)
+{
+	int rc = 0;
+	struct ap_device *ap_dev = to_ap_dev(dev);
+
+	if (ap_suspend_flag) {
+		ap_suspend_flag = 0;
+		if (!ap_interrupts_available())
+			ap_interrupt_indicator = NULL;
+		ap_device_probe(dev);
+		ap_reset(ap_dev);
+		setup_timer(&ap_dev->timeout, ap_request_timeout,
+			    (unsigned long) ap_dev);
+		ap_scan_bus(NULL);
+		init_timer(&ap_config_timer);
+		ap_config_timer.function = ap_config_timeout;
+		ap_config_timer.data = 0;
+		ap_config_timer.expires = jiffies + ap_config_time * HZ;
+		add_timer(&ap_config_timer);
+		ap_work_queue = create_singlethread_workqueue("kapwork");
+		if (!ap_work_queue)
+			return -ENOMEM;
+		tasklet_enable(&ap_tasklet);
+		if (!ap_using_interrupts())
+			ap_schedule_poll_timer();
+		else
+			tasklet_schedule(&ap_tasklet);
+		if (ap_thread_flag)
+			rc = ap_poll_thread_start();
+	} else {
+		ap_device_probe(dev);
+		ap_reset(ap_dev);
+		setup_timer(&ap_dev->timeout, ap_request_timeout,
+			    (unsigned long) ap_dev);
+	}
+
+	return rc;
+}
+
 static struct bus_type ap_bus_type = {
 	.name = "ap",
 	.match = &ap_bus_match,
 	.uevent = &ap_uevent,
+	.suspend = ap_bus_suspend,
+	.resume = ap_bus_resume
 };
 
 static int ap_device_probe(struct device *dev)
@@ -1066,7 +1145,7 @@
  */
 static inline void ap_schedule_poll_timer(void)
 {
-	if (ap_using_interrupts())
+	if (ap_using_interrupts() || ap_suspend_flag)
 		return;
 	if (hrtimer_is_queued(&ap_poll_timer))
 		return;
@@ -1384,6 +1463,8 @@
 
 	set_user_nice(current, 19);
 	while (1) {
+		if (ap_suspend_flag)
+			return 0;
 		if (need_resched()) {
 			schedule();
 			continue;
@@ -1414,7 +1495,7 @@
 {
 	int rc;
 
-	if (ap_using_interrupts())
+	if (ap_using_interrupts() || ap_suspend_flag)
 		return 0;
 	mutex_lock(&ap_poll_thread_mutex);
 	if (!ap_poll_kthread) {