s390/zcrypt: cleanup AP bus timer code
[firefly-linux-kernel-4.4.55.git] / drivers / s390 / crypto / ap_bus.c
index 613c92b22445288880d6f84454002022fa365aea..ba1f806051b72babb00ca178a4b0e3c85932185d 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/notifier.h>
 #include <linux/kthread.h>
 #include <linux/mutex.h>
+#include <linux/suspend.h>
 #include <asm/reset.h>
 #include <asm/airq.h>
 #include <linux/atomic.h>
 /* Some prototypes. */
 static void ap_scan_bus(struct work_struct *);
 static void ap_poll_all(unsigned long);
-static enum hrtimer_restart ap_poll_timeout(struct hrtimer *);
-static int ap_poll_thread_start(void);
-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(struct airq_struct *airq);
 static void ap_reset(struct ap_device *ap_dev, unsigned long *flags);
-static void ap_config_timeout(unsigned long ptr);
 static int ap_select_domain(void);
-static void ap_query_configuration(void);
 
 /*
  * Module description.
@@ -115,6 +110,8 @@ static unsigned long long poll_timeout = 250000;
 
 /* Suspend flag */
 static int ap_suspend_flag;
+/* Maximum domain id */
+static int ap_max_domain_id;
 /* Flag to check if domain was set through module parameter domain=. This is
  * important when supsend and resume is done in a z/VM environment where the
  * domain might change. */
@@ -242,12 +239,19 @@ ap_queue_interruption_control(ap_qid_t qid, void *ind)
        return reg1_out;
 }
 
-static inline int __ap_query_configuration(struct ap_config_info *config)
+/**
+ * ap_query_configuration(): Get AP configuration data
+ *
+ * Returns 0 on success, or -EOPNOTSUPP.
+ */
+static inline int ap_query_configuration(void)
 {
        register unsigned long reg0 asm ("0") = 0x04000000UL;
        register unsigned long reg1 asm ("1") = -EINVAL;
-       register unsigned char *reg2 asm ("2") = (unsigned char *)config;
+       register void *reg2 asm ("2") = (void *) ap_configuration;
 
+       if (!ap_configuration)
+               return -EOPNOTSUPP;
        asm volatile(
                ".long 0xb2af0000\n"            /* PQAP(QCI) */
                "0: la    %1,0\n"
@@ -260,6 +264,63 @@ static inline int __ap_query_configuration(struct ap_config_info *config)
        return reg1;
 }
 
+/**
+ * ap_init_configuration(): Allocate and query configuration array.
+ */
+static void ap_init_configuration(void)
+{
+       if (!ap_configuration_available())
+               return;
+
+       ap_configuration = kzalloc(sizeof(*ap_configuration), GFP_KERNEL);
+       if (!ap_configuration)
+               return;
+       if (ap_query_configuration() != 0) {
+               kfree(ap_configuration);
+               ap_configuration = NULL;
+               return;
+       }
+}
+
+/*
+ * ap_test_config(): helper function to extract the nrth bit
+ *                  within the unsigned int array field.
+ */
+static inline int ap_test_config(unsigned int *field, unsigned int nr)
+{
+       return ap_test_bit((field + (nr >> 5)), (nr & 0x1f));
+}
+
+/*
+ * ap_test_config_card_id(): Test, whether an AP card ID is configured.
+ * @id AP card ID
+ *
+ * Returns 0 if the card is not configured
+ *        1 if the card is configured or
+ *          if the configuration information is not available
+ */
+static inline int ap_test_config_card_id(unsigned int id)
+{
+       if (!ap_configuration)  /* QCI not supported */
+               return 1;
+       return ap_test_config(ap_configuration->apm, id);
+}
+
+/*
+ * ap_test_config_domain(): Test, whether an AP usage domain is configured.
+ * @domain AP usage domain ID
+ *
+ * Returns 0 if the usage domain is not configured
+ *        1 if the usage domain is configured or
+ *          if the configuration information is not available
+ */
+static inline int ap_test_config_domain(unsigned int domain)
+{
+       if (!ap_configuration)  /* QCI not supported */
+               return domain < 16;
+       return ap_test_config(ap_configuration->aqm, domain);
+}
+
 /**
  * ap_queue_enable_interruption(): Enable interruption on an AP.
  * @qid: The AP queue number
@@ -450,6 +511,10 @@ static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type,
 {
        struct ap_queue_status status;
        unsigned long info;
+       int nd;
+
+       if (!ap_test_config_card_id(AP_QID_DEVICE(qid)))
+               return -ENODEV;
 
        status = ap_test_queue(qid, &info);
        switch (status.response_code) {
@@ -457,6 +522,10 @@ static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type,
                *queue_depth = (int)(info & 0xff);
                *device_type = (int)((info >> 24) & 0xff);
                *facilities = (unsigned int)(info >> 32);
+               /* Update maximum domain id */
+               nd = (info >> 16) & 0xff;
+               if ((info & (1UL << 57)) && nd > 0)
+                       ap_max_domain_id = nd;
                return 0;
        case AP_RESPONSE_Q_NOT_AVAIL:
        case AP_RESPONSE_DECONFIGURED:
@@ -541,6 +610,78 @@ static void ap_decrease_queue_count(struct ap_device *ap_dev)
                ap_dev->reset = AP_RESET_IGNORE;
 }
 
+/**
+ * ap_poll_thread(): Thread that polls for finished requests.
+ * @data: Unused pointer
+ *
+ * AP bus poll thread. The purpose of this thread is to poll for
+ * finished requests in a loop if there is a "free" cpu - that is
+ * a cpu that doesn't have anything better to do. The polling stops
+ * as soon as there is another task or if all messages have been
+ * delivered.
+ */
+static int ap_poll_thread(void *data)
+{
+       DECLARE_WAITQUEUE(wait, current);
+       unsigned long flags;
+       struct ap_device *ap_dev;
+
+       set_user_nice(current, MAX_NICE);
+       set_freezable();
+       while (!kthread_should_stop()) {
+               add_wait_queue(&ap_poll_wait, &wait);
+               set_current_state(TASK_INTERRUPTIBLE);
+               if (ap_suspend_flag ||
+                   atomic_read(&ap_poll_requests) <= 0) {
+                       schedule();
+                       try_to_freeze();
+               }
+               set_current_state(TASK_RUNNING);
+               remove_wait_queue(&ap_poll_wait, &wait);
+
+               if (need_resched()) {
+                       schedule();
+                       try_to_freeze();
+                       continue;
+               }
+
+               flags = 0;
+               spin_lock_bh(&ap_device_list_lock);
+               list_for_each_entry(ap_dev, &ap_device_list, list) {
+                       spin_lock(&ap_dev->lock);
+                       __ap_poll_device(ap_dev, &flags);
+                       spin_unlock(&ap_dev->lock);
+               }
+               spin_unlock_bh(&ap_device_list_lock);
+       } while (!kthread_should_stop());
+       return 0;
+}
+
+static int ap_poll_thread_start(void)
+{
+       int rc;
+
+       if (ap_using_interrupts() || ap_poll_kthread)
+               return 0;
+       mutex_lock(&ap_poll_thread_mutex);
+       ap_poll_kthread = kthread_run(ap_poll_thread, NULL, "appoll");
+       rc = PTR_RET(ap_poll_kthread);
+       if (rc)
+               ap_poll_kthread = NULL;
+       mutex_unlock(&ap_poll_thread_mutex);
+       return rc;
+}
+
+static void ap_poll_thread_stop(void)
+{
+       if (!ap_poll_kthread)
+               return;
+       mutex_lock(&ap_poll_thread_mutex);
+       kthread_stop(ap_poll_kthread);
+       ap_poll_kthread = NULL;
+       mutex_unlock(&ap_poll_thread_mutex);
+}
+
 /*
  * AP device related attributes.
  */
@@ -754,25 +895,11 @@ static int ap_uevent (struct device *dev, struct kobj_uevent_env *env)
        return retval;
 }
 
-static int ap_bus_suspend(struct device *dev, pm_message_t state)
+static int ap_dev_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;
@@ -781,72 +908,85 @@ static int ap_bus_suspend(struct device *dev, pm_message_t state)
                spin_unlock_bh(&ap_dev->lock);
        } while ((flags & 1) || (flags & 2));
 
-       spin_lock_bh(&ap_dev->lock);
-       ap_dev->unregistered = 1;
-       spin_unlock_bh(&ap_dev->lock);
+       return 0;
+}
 
+static int ap_dev_resume(struct device *dev)
+{
        return 0;
 }
 
-static int ap_bus_resume(struct device *dev)
+static void ap_bus_suspend(void)
+{
+       ap_suspend_flag = 1;
+       /*
+        * Disable scanning for devices, thus we do not want to scan
+        * for them after removing.
+        */
+       flush_workqueue(ap_work_queue);
+       tasklet_disable(&ap_tasklet);
+}
+
+static int __ap_devices_unregister(struct device *dev, void *dummy)
+{
+       device_unregister(dev);
+       return 0;
+}
+
+static void ap_bus_resume(void)
 {
-       struct ap_device *ap_dev = to_ap_dev(dev);
        int rc;
 
-       if (ap_suspend_flag) {
-               ap_suspend_flag = 0;
-               if (ap_interrupts_available()) {
-                       if (!ap_using_interrupts()) {
-                               rc = register_adapter_interrupt(&ap_airq);
-                               ap_airq_flag = (rc == 0);
-                       }
-               } else {
-                       if (ap_using_interrupts()) {
-                               unregister_adapter_interrupt(&ap_airq);
-                               ap_airq_flag = 0;
-                       }
-               }
-               ap_query_configuration();
-               if (!user_set_domain) {
-                       ap_domain_index = -1;
-                       ap_select_domain();
-               }
-               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
-                       rc = 0;
-       } else
-               rc = 0;
-       if (AP_QID_QUEUE(ap_dev->qid) != ap_domain_index) {
-               spin_lock_bh(&ap_dev->lock);
-               ap_dev->qid = AP_MKQID(AP_QID_DEVICE(ap_dev->qid),
-                                      ap_domain_index);
-               spin_unlock_bh(&ap_dev->lock);
+       /* Unconditionally remove all AP devices */
+       bus_for_each_dev(&ap_bus_type, NULL, NULL, __ap_devices_unregister);
+       /* Reset thin interrupt setting */
+       if (ap_interrupts_available() && !ap_using_interrupts()) {
+               rc = register_adapter_interrupt(&ap_airq);
+               ap_airq_flag = (rc == 0);
+       }
+       if (!ap_interrupts_available() && ap_using_interrupts()) {
+               unregister_adapter_interrupt(&ap_airq);
+               ap_airq_flag = 0;
        }
+       /* Reset domain */
+       if (!user_set_domain)
+               ap_domain_index = -1;
+       /* Get things going again */
+       ap_suspend_flag = 0;
+       if (ap_airq_flag)
+               xchg(ap_airq.lsi_ptr, 0);
+       tasklet_enable(&ap_tasklet);
        queue_work(ap_work_queue, &ap_config_work);
+       wake_up(&ap_poll_wait);
+}
 
-       return rc;
+static int ap_power_event(struct notifier_block *this, unsigned long event,
+                         void *ptr)
+{
+       switch (event) {
+       case PM_HIBERNATION_PREPARE:
+       case PM_SUSPEND_PREPARE:
+               ap_bus_suspend();
+               break;
+       case PM_POST_HIBERNATION:
+       case PM_POST_SUSPEND:
+               ap_bus_resume();
+               break;
+       default:
+               break;
+       }
+       return NOTIFY_DONE;
 }
+static struct notifier_block ap_power_notifier = {
+       .notifier_call = ap_power_event,
+};
 
 static struct bus_type ap_bus_type = {
        .name = "ap",
        .match = &ap_bus_match,
        .uevent = &ap_uevent,
-       .suspend = ap_bus_suspend,
-       .resume = ap_bus_resume
+       .suspend = ap_dev_suspend,
+       .resume = ap_dev_resume,
 };
 
 static int ap_device_probe(struct device *dev)
@@ -944,59 +1084,15 @@ EXPORT_SYMBOL(ap_driver_unregister);
 
 void ap_bus_force_rescan(void)
 {
-       /* reconfigure the AP bus rescan timer. */
-       mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ);
+       if (ap_suspend_flag)
+               return;
        /* processing a asynchronous bus rescan */
+       del_timer(&ap_config_timer);
        queue_work(ap_work_queue, &ap_config_work);
        flush_work(&ap_config_work);
 }
 EXPORT_SYMBOL(ap_bus_force_rescan);
 
-/*
- * ap_test_config(): helper function to extract the nrth bit
- *                  within the unsigned int array field.
- */
-static inline int ap_test_config(unsigned int *field, unsigned int nr)
-{
-       if (nr > 0xFFu)
-               return 0;
-       return ap_test_bit((field + (nr >> 5)), (nr & 0x1f));
-}
-
-/*
- * ap_test_config_card_id(): Test, whether an AP card ID is configured.
- * @id AP card ID
- *
- * Returns 0 if the card is not configured
- *        1 if the card is configured or
- *          if the configuration information is not available
- */
-static inline int ap_test_config_card_id(unsigned int id)
-{
-       if (!ap_configuration)
-               return 1;
-       return ap_test_config(ap_configuration->apm, id);
-}
-
-/*
- * ap_test_config_domain(): Test, whether an AP usage domain is configured.
- * @domain AP usage domain ID
- *
- * Returns 0 if the usage domain is not configured
- *        1 if the usage domain is configured or
- *          if the configuration information is not available
- */
-static inline int ap_test_config_domain(unsigned int domain)
-{
-       if (!ap_configuration)    /* QCI not supported */
-               if (domain < 16)
-                       return 1; /* then domains 0...15 are configured */
-               else
-                       return 0;
-       else
-               return ap_test_config(ap_configuration->aqm, domain);
-}
-
 /*
  * AP bus attributes.
  */
@@ -1009,21 +1105,20 @@ static BUS_ATTR(ap_domain, 0444, ap_domain_show, NULL);
 
 static ssize_t ap_control_domain_mask_show(struct bus_type *bus, char *buf)
 {
-       if (ap_configuration != NULL) { /* QCI not supported */
-               if (test_facility(76)) { /* format 1 - 256 bit domain field */
-                       return snprintf(buf, PAGE_SIZE,
-                               "0x%08x%08x%08x%08x%08x%08x%08x%08x\n",
+       if (!ap_configuration)  /* QCI not supported */
+               return snprintf(buf, PAGE_SIZE, "not supported\n");
+       if (!test_facility(76))
+               /* format 0 - 16 bit domain field */
+               return snprintf(buf, PAGE_SIZE, "%08x%08x\n",
+                               ap_configuration->adm[0],
+                               ap_configuration->adm[1]);
+       /* format 1 - 256 bit domain field */
+       return snprintf(buf, PAGE_SIZE,
+                       "0x%08x%08x%08x%08x%08x%08x%08x%08x\n",
                        ap_configuration->adm[0], ap_configuration->adm[1],
                        ap_configuration->adm[2], ap_configuration->adm[3],
                        ap_configuration->adm[4], ap_configuration->adm[5],
                        ap_configuration->adm[6], ap_configuration->adm[7]);
-               } else { /* format 0 - 16 bit domain field */
-                       return snprintf(buf, PAGE_SIZE, "%08x%08x\n",
-                       ap_configuration->adm[0], ap_configuration->adm[1]);
-                 }
-       } else {
-               return snprintf(buf, PAGE_SIZE, "not supported\n");
-         }
 }
 
 static BUS_ATTR(ap_control_domain_mask, 0444,
@@ -1050,11 +1145,7 @@ static ssize_t ap_config_time_store(struct bus_type *bus,
        if (sscanf(buf, "%d\n", &time) != 1 || time < 5 || time > 120)
                return -EINVAL;
        ap_config_time = time;
-       if (!timer_pending(&ap_config_timer) ||
-           !mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ)) {
-               ap_config_timer.expires = jiffies + ap_config_time * HZ;
-               add_timer(&ap_config_timer);
-       }
+       mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ);
        return count;
 }
 
@@ -1075,9 +1166,8 @@ static ssize_t ap_poll_thread_store(struct bus_type *bus,
        if (flag) {
                rc = ap_poll_thread_start();
                if (rc)
-                       return rc;
-       }
-       else
+                       count = rc;
+       } else
                ap_poll_thread_stop();
        return count;
 }
@@ -1115,38 +1205,12 @@ static BUS_ATTR(poll_timeout, 0644, poll_timeout_show, poll_timeout_store);
 
 static ssize_t ap_max_domain_id_show(struct bus_type *bus, char *buf)
 {
-       struct ap_queue_status status;
-       ap_qid_t qid;
-       int i, nd, max_domain_id = -1;
-       unsigned long fbits;
-
-       if (ap_configuration) {
-               if (ap_domain_index >= 0 && ap_domain_index < AP_DOMAINS) {
-                       for (i = 0; i < AP_DEVICES; i++) {
-                               if (!ap_test_config_card_id(i))
-                                       continue;
-                               qid = AP_MKQID(i, ap_domain_index);
-                               status = ap_test_queue(qid, &fbits);
-                               if (status.response_code != AP_RESPONSE_NORMAL)
-                                       continue;
-                               if (fbits & (1UL << 57)) {
-                                       /* the N bit is 0, Nd field is filled */
-                                       nd = (int)((fbits & 0x00FF0000UL)>>16);
-                                       if (nd > 0)
-                                               max_domain_id = nd;
-                                       else
-                                               max_domain_id = 15;
-                               } else {
-                                       /* N bit is 1, max 16 domains */
-                                       max_domain_id = 15;
-                               }
-                               break;
-                       }
-               }
-       } else {
-               /* no APXA support, older machines with max 16 domains */
+       int max_domain_id;
+
+       if (ap_configuration)
+               max_domain_id = ap_max_domain_id ? : -1;
+       else
                max_domain_id = 15;
-       }
        return snprintf(buf, PAGE_SIZE, "%d\n", max_domain_id);
 }
 
@@ -1163,24 +1227,6 @@ static struct bus_attribute *const ap_bus_attrs[] = {
        NULL,
 };
 
-/**
- * ap_query_configuration(): Query AP configuration information.
- *
- * Query information of installed cards and configured domains from AP.
- */
-static void ap_query_configuration(void)
-{
-       if (ap_configuration_available()) {
-               if (!ap_configuration)
-                       ap_configuration =
-                               kzalloc(sizeof(struct ap_config_info),
-                                       GFP_KERNEL);
-               if (ap_configuration)
-                       __ap_query_configuration(ap_configuration);
-       } else
-               ap_configuration = NULL;
-}
-
 /**
  * ap_select_domain(): Select an AP domain.
  *
@@ -1192,16 +1238,12 @@ static int ap_select_domain(void)
        struct ap_queue_status status;
        int i, j;
 
-       /* IF APXA isn't installed, only 16 domains could be defined */
-       if (!ap_configuration->ap_extended && (ap_domain_index > 15))
-               return -EINVAL;
-
        /*
         * We want to use a single domain. Either the one specified with
         * the "domain=" parameter or the domain with the maximum number
         * of devices.
         */
-       if (ap_domain_index >= 0 && ap_domain_index < AP_DOMAINS)
+       if (ap_domain_index >= 0)
                /* Domain has already been selected. */
                return 0;
        best_domain = -1;
@@ -1330,7 +1372,8 @@ out:
 static void ap_interrupt_handler(struct airq_struct *airq)
 {
        inc_irq_stat(IRQIO_APB);
-       tasklet_schedule(&ap_tasklet);
+       if (!ap_suspend_flag)
+               tasklet_schedule(&ap_tasklet);
 }
 
 /**
@@ -1362,19 +1405,16 @@ static void ap_scan_bus(struct work_struct *unused)
        int rc, i;
 
        ap_query_configuration();
-       if (ap_select_domain() != 0) {
-               return;
-       }
+       if (ap_select_domain() != 0)
+               goto out;
+
        for (i = 0; i < AP_DEVICES; i++) {
                qid = AP_MKQID(i, ap_domain_index);
                dev = bus_find_device(&ap_bus_type, NULL,
                                      (void *)(unsigned long)qid,
                                      __ap_scan_bus);
-               if (ap_test_config_card_id(i))
-                       rc = ap_query_queue(qid, &queue_depth, &device_type,
-                                           &device_functions);
-               else
-                       rc = -ENODEV;
+               rc = ap_query_queue(qid, &queue_depth, &device_type,
+                                   &device_functions);
                if (dev) {
                        ap_dev = to_ap_dev(dev);
                        spin_lock_bh(&ap_dev->lock);
@@ -1443,14 +1483,15 @@ static void ap_scan_bus(struct work_struct *unused)
                else
                        device_unregister(&ap_dev->device);
        }
+out:
+       mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ);
 }
 
-static void
-ap_config_timeout(unsigned long ptr)
+static void ap_config_timeout(unsigned long ptr)
 {
+       if (ap_suspend_flag)
+               return;
        queue_work(ap_work_queue, &ap_config_work);
-       ap_config_timer.expires = jiffies + ap_config_time * HZ;
-       add_timer(&ap_config_timer);
 }
 
 /**
@@ -1689,8 +1730,6 @@ void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
                rc = -ENODEV;
        }
        spin_unlock_bh(&ap_dev->lock);
-       if (rc == -ENODEV)
-               device_unregister(&ap_dev->device);
 }
 EXPORT_SYMBOL(ap_queue_message);
 
@@ -1731,7 +1770,8 @@ EXPORT_SYMBOL(ap_cancel_message);
  */
 static enum hrtimer_restart ap_poll_timeout(struct hrtimer *unused)
 {
-       tasklet_schedule(&ap_tasklet);
+       if (!ap_suspend_flag)
+               tasklet_schedule(&ap_tasklet);
        return HRTIMER_NORESTART;
 }
 
@@ -1802,84 +1842,6 @@ static void ap_poll_all(unsigned long dummy)
                __ap_schedule_poll_timer();
 }
 
-/**
- * ap_poll_thread(): Thread that polls for finished requests.
- * @data: Unused pointer
- *
- * AP bus poll thread. The purpose of this thread is to poll for
- * finished requests in a loop if there is a "free" cpu - that is
- * a cpu that doesn't have anything better to do. The polling stops
- * as soon as there is another task or if all messages have been
- * delivered.
- */
-static int ap_poll_thread(void *data)
-{
-       DECLARE_WAITQUEUE(wait, current);
-       unsigned long flags;
-       int requests;
-       struct ap_device *ap_dev;
-
-       set_user_nice(current, MAX_NICE);
-       while (1) {
-               if (ap_suspend_flag)
-                       return 0;
-               if (need_resched()) {
-                       schedule();
-                       continue;
-               }
-               add_wait_queue(&ap_poll_wait, &wait);
-               set_current_state(TASK_INTERRUPTIBLE);
-               if (kthread_should_stop())
-                       break;
-               requests = atomic_read(&ap_poll_requests);
-               if (requests <= 0)
-                       schedule();
-               set_current_state(TASK_RUNNING);
-               remove_wait_queue(&ap_poll_wait, &wait);
-
-               flags = 0;
-               spin_lock_bh(&ap_device_list_lock);
-               list_for_each_entry(ap_dev, &ap_device_list, list) {
-                       spin_lock(&ap_dev->lock);
-                       __ap_poll_device(ap_dev, &flags);
-                       spin_unlock(&ap_dev->lock);
-               }
-               spin_unlock_bh(&ap_device_list_lock);
-       }
-       set_current_state(TASK_RUNNING);
-       remove_wait_queue(&ap_poll_wait, &wait);
-       return 0;
-}
-
-static int ap_poll_thread_start(void)
-{
-       int rc;
-
-       if (ap_using_interrupts() || ap_suspend_flag)
-               return 0;
-       mutex_lock(&ap_poll_thread_mutex);
-       if (!ap_poll_kthread) {
-               ap_poll_kthread = kthread_run(ap_poll_thread, NULL, "appoll");
-               rc = PTR_RET(ap_poll_kthread);
-               if (rc)
-                       ap_poll_kthread = NULL;
-       }
-       else
-               rc = 0;
-       mutex_unlock(&ap_poll_thread_mutex);
-       return rc;
-}
-
-static void ap_poll_thread_stop(void)
-{
-       mutex_lock(&ap_poll_thread_mutex);
-       if (ap_poll_kthread) {
-               kthread_stop(ap_poll_kthread);
-               ap_poll_kthread = NULL;
-       }
-       mutex_unlock(&ap_poll_thread_mutex);
-}
-
 /**
  * ap_request_timeout(): Handling of request timeouts
  * @data: Holds the AP device.
@@ -1890,6 +1852,8 @@ static void ap_request_timeout(unsigned long data)
 {
        struct ap_device *ap_dev = (struct ap_device *) data;
 
+       if (ap_suspend_flag)
+               return;
        if (ap_dev->reset == AP_RESET_ARMED) {
                ap_dev->reset = AP_RESET_DO;
 
@@ -1902,9 +1866,10 @@ static void ap_reset_domain(void)
 {
        int i;
 
-       if ((ap_domain_index != -1) && (ap_test_config_domain(ap_domain_index)))
-               for (i = 0; i < AP_DEVICES; i++)
-                       ap_reset_queue(AP_MKQID(i, ap_domain_index));
+       if (ap_domain_index == -1 || !ap_test_config_domain(ap_domain_index))
+               return;
+       for (i = 0; i < AP_DEVICES; i++)
+               ap_reset_queue(AP_MKQID(i, ap_domain_index));
 }
 
 static void ap_reset_all(void)
@@ -1933,11 +1898,24 @@ static struct reset_call ap_reset_call = {
  */
 int __init ap_module_init(void)
 {
+       int max_domain_id;
        int rc, i;
 
-       if (ap_domain_index < -1 || ap_domain_index >= AP_DOMAINS) {
-               pr_warning("%d is not a valid cryptographic domain\n",
-                          ap_domain_index);
+       if (ap_instructions_available() != 0) {
+               pr_warn("The hardware system does not support AP instructions\n");
+               return -ENODEV;
+       }
+
+       /* Get AP configuration data if available */
+       ap_init_configuration();
+
+       if (ap_configuration)
+               max_domain_id = ap_max_domain_id ? : (AP_DOMAINS - 1);
+       else
+               max_domain_id = 15;
+       if (ap_domain_index < -1 || ap_domain_index > max_domain_id) {
+               pr_warn("%d is not a valid cryptographic domain\n",
+                       ap_domain_index);
                return -EINVAL;
        }
        /* In resume callback we need to know if the user had set the domain.
@@ -1946,11 +1924,6 @@ int __init ap_module_init(void)
        if (ap_domain_index >= 0)
                user_set_domain = 1;
 
-       if (ap_instructions_available() != 0) {
-               pr_warning("The hardware system does not support "
-                          "AP instructions\n");
-               return -ENODEV;
-       }
        if (ap_interrupts_available()) {
                rc = register_adapter_interrupt(&ap_airq);
                ap_airq_flag = (rc == 0);
@@ -1980,15 +1953,11 @@ int __init ap_module_init(void)
                goto out_root;
        }
 
-       ap_query_configuration();
        if (ap_select_domain() == 0)
                ap_scan_bus(NULL);
 
        /* Setup the AP bus rescan timer. */
-       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;
+       setup_timer(&ap_config_timer, ap_config_timeout, 0);
        add_timer(&ap_config_timer);
 
        /* Setup the high resultion poll timer.
@@ -2007,8 +1976,14 @@ int __init ap_module_init(void)
                        goto out_work;
        }
 
+       rc = register_pm_notifier(&ap_power_notifier);
+       if (rc)
+               goto out_pm;
+
        return 0;
 
+out_pm:
+       ap_poll_thread_stop();
 out_work:
        del_timer_sync(&ap_config_timer);
        hrtimer_cancel(&ap_poll_timer);
@@ -2023,14 +1998,10 @@ out:
        unregister_reset_call(&ap_reset_call);
        if (ap_using_interrupts())
                unregister_adapter_interrupt(&ap_airq);
+       kfree(ap_configuration);
        return rc;
 }
 
-static int __ap_match_all(struct device *dev, void *data)
-{
-       return 1;
-}
-
 /**
  * ap_modules_exit(): The module termination code
  *
@@ -2039,7 +2010,6 @@ static int __ap_match_all(struct device *dev, void *data)
 void ap_module_exit(void)
 {
        int i;
-       struct device *dev;
 
        ap_reset_domain();
        ap_poll_thread_stop();
@@ -2047,16 +2017,13 @@ void ap_module_exit(void)
        hrtimer_cancel(&ap_poll_timer);
        destroy_workqueue(ap_work_queue);
        tasklet_kill(&ap_tasklet);
-       while ((dev = bus_find_device(&ap_bus_type, NULL, NULL,
-                   __ap_match_all)))
-       {
-               device_unregister(dev);
-               put_device(dev);
-       }
+       bus_for_each_dev(&ap_bus_type, NULL, NULL, __ap_devices_unregister);
        for (i = 0; ap_bus_attrs[i]; i++)
                bus_remove_file(&ap_bus_type, ap_bus_attrs[i]);
+       unregister_pm_notifier(&ap_power_notifier);
        root_device_unregister(ap_root_device);
        bus_unregister(&ap_bus_type);
+       kfree(ap_configuration);
        unregister_reset_call(&ap_reset_call);
        if (ap_using_interrupts())
                unregister_adapter_interrupt(&ap_airq);