s390/zcrypt: introduce state machine for the AP bus

Replace the two fields 'unregistered' and 'reset' with a device
state with 5 possible values. Introduce two events for the AP devices,
device poll and device timeout. With the state machine it is easier
to deal with device initialization and suspend/resume. Device polling
is simpler as well, the arkane 'flags' passing is gone.

Reviewd-by: Ingo Tuchscherer <ingo.tuchscherer@linux.vnet.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 7224bf7..92d3b67 100644
--- a/drivers/s390/crypto/ap_bus.c
+++ b/drivers/s390/crypto/ap_bus.c
@@ -49,17 +49,6 @@
 
 #include "ap_bus.h"
 
-/* Some prototypes. */
-static void ap_scan_bus(struct work_struct *);
-static void ap_poll_all(unsigned long);
-static void ap_request_timeout(unsigned long);
-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 int ap_select_domain(void);
-
 /*
  * Module description.
  */
@@ -92,12 +81,14 @@
 static struct workqueue_struct *ap_work_queue;
 static struct timer_list ap_config_timer;
 static int ap_config_time = AP_CONFIG_TIME;
+static void ap_scan_bus(struct work_struct *);
 static DECLARE_WORK(ap_config_work, ap_scan_bus);
 
 /*
  * Tasklet & timer for AP request polling and interrupts
  */
-static DECLARE_TASKLET(ap_tasklet, ap_poll_all, 0);
+static void ap_tasklet_fn(unsigned long);
+static DECLARE_TASKLET(ap_tasklet, ap_tasklet_fn, 0);
 static atomic_t ap_poll_requests = ATOMIC_INIT(0);
 static DECLARE_WAIT_QUEUE_HEAD(ap_poll_wait);
 static struct task_struct *ap_poll_kthread = NULL;
@@ -119,6 +110,8 @@
 static struct bus_type ap_bus_type;
 
 /* Adapter interrupt definitions */
+static void ap_interrupt_handler(struct airq_struct *airq);
+
 static int ap_airq_flag;
 
 static struct airq_struct ap_airq = {
@@ -343,7 +336,9 @@
 	case AP_RESPONSE_DECONFIGURED:
 	case AP_RESPONSE_CHECKSTOPPED:
 	case AP_RESPONSE_INVALID_ADDRESS:
-		return -ENODEV;
+		pr_err("Registering adapter interrupts for AP %d failed\n",
+		       AP_QID_DEVICE(ap_dev->qid));
+		return -EOPNOTSUPP;
 	case AP_RESPONSE_RESET_IN_PROGRESS:
 	case AP_RESPONSE_BUSY:
 	default:
@@ -469,37 +464,6 @@
 EXPORT_SYMBOL(ap_recv);
 
 /**
- * __ap_schedule_poll_timer(): Schedule poll timer.
- *
- * Set up the timer to run the poll tasklet
- */
-static inline void __ap_schedule_poll_timer(void)
-{
-	ktime_t hr_time;
-
-	spin_lock_bh(&ap_poll_timer_lock);
-	if (!hrtimer_is_queued(&ap_poll_timer) && !ap_suspend_flag) {
-		hr_time = ktime_set(0, poll_timeout);
-		hrtimer_forward_now(&ap_poll_timer, hr_time);
-		hrtimer_restart(&ap_poll_timer);
-	}
-	spin_unlock_bh(&ap_poll_timer_lock);
-}
-
-/**
- * ap_schedule_poll_timer(): Schedule poll timer.
- *
- * Set up the timer to run the poll tasklet
- */
-static inline void ap_schedule_poll_timer(void)
-{
-	if (ap_using_interrupts())
-		return;
-	__ap_schedule_poll_timer();
-}
-
-
-/**
  * ap_query_queue(): Check if an AP queue is available.
  * @qid: The AP queue number
  * @queue_depth: Pointer to queue depth value
@@ -541,73 +505,404 @@
 	}
 }
 
+/* State machine definitions and helpers */
+
+static void ap_sm_wait(enum ap_wait wait)
+{
+	ktime_t hr_time;
+
+	switch (wait) {
+	case AP_WAIT_AGAIN:
+	case AP_WAIT_INTERRUPT:
+		if (ap_using_interrupts())
+			break;
+		if (ap_poll_kthread) {
+			wake_up(&ap_poll_wait);
+			break;
+		}
+		/* Fall through */
+	case AP_WAIT_TIMEOUT:
+		spin_lock_bh(&ap_poll_timer_lock);
+		if (!hrtimer_is_queued(&ap_poll_timer)) {
+			hr_time = ktime_set(0, poll_timeout);
+			hrtimer_forward_now(&ap_poll_timer, hr_time);
+			hrtimer_restart(&ap_poll_timer);
+		}
+		spin_unlock_bh(&ap_poll_timer_lock);
+		break;
+	case AP_WAIT_NONE:
+	default:
+		break;
+	}
+}
+
+static enum ap_wait ap_sm_nop(struct ap_device *ap_dev)
+{
+	return AP_WAIT_NONE;
+}
+
 /**
- * ap_init_queue(): Reset an AP queue.
+ * ap_sm_recv(): Receive pending reply messages from an AP device but do
+ *	not change the state of the device.
+ * @ap_dev: pointer to the AP device
+ *
+ * Returns AP_WAIT_NONE, AP_WAIT_AGAIN, or AP_WAIT_INTERRUPT
+ */
+static struct ap_queue_status ap_sm_recv(struct ap_device *ap_dev)
+{
+	struct ap_queue_status status;
+	struct ap_message *ap_msg;
+
+	status = __ap_recv(ap_dev->qid, &ap_dev->reply->psmid,
+			   ap_dev->reply->message, ap_dev->reply->length);
+	switch (status.response_code) {
+	case AP_RESPONSE_NORMAL:
+		atomic_dec(&ap_poll_requests);
+		ap_dev->queue_count--;
+		if (ap_dev->queue_count > 0)
+			mod_timer(&ap_dev->timeout,
+				  jiffies + ap_dev->drv->request_timeout);
+		list_for_each_entry(ap_msg, &ap_dev->pendingq, list) {
+			if (ap_msg->psmid != ap_dev->reply->psmid)
+				continue;
+			list_del_init(&ap_msg->list);
+			ap_dev->pendingq_count--;
+			ap_msg->receive(ap_dev, ap_msg, ap_dev->reply);
+			break;
+		}
+	case AP_RESPONSE_NO_PENDING_REPLY:
+		if (!status.queue_empty || ap_dev->queue_count <= 0)
+			break;
+		/* The card shouldn't forget requests but who knows. */
+		atomic_sub(ap_dev->queue_count, &ap_poll_requests);
+		ap_dev->queue_count = 0;
+		list_splice_init(&ap_dev->pendingq, &ap_dev->requestq);
+		ap_dev->requestq_count += ap_dev->pendingq_count;
+		ap_dev->pendingq_count = 0;
+		break;
+	default:
+		break;
+	}
+	return status;
+}
+
+/**
+ * ap_sm_read(): Receive pending reply messages from an AP device.
+ * @ap_dev: pointer to the AP device
+ *
+ * Returns AP_WAIT_NONE, AP_WAIT_AGAIN, or AP_WAIT_INTERRUPT
+ */
+static enum ap_wait ap_sm_read(struct ap_device *ap_dev)
+{
+	struct ap_queue_status status;
+
+	status = ap_sm_recv(ap_dev);
+	switch (status.response_code) {
+	case AP_RESPONSE_NORMAL:
+		if (ap_dev->queue_count > 0)
+			return AP_WAIT_AGAIN;
+		ap_dev->state = AP_STATE_IDLE;
+		return AP_WAIT_NONE;
+	case AP_RESPONSE_NO_PENDING_REPLY:
+		if (ap_dev->queue_count > 0)
+			return AP_WAIT_INTERRUPT;
+		ap_dev->state = AP_STATE_IDLE;
+		return AP_WAIT_NONE;
+	default:
+		ap_dev->state = AP_STATE_BORKED;
+		return AP_WAIT_NONE;
+	}
+}
+
+/**
+ * ap_sm_write(): Send messages from the request queue to an AP device.
+ * @ap_dev: pointer to the AP device
+ *
+ * Returns AP_WAIT_NONE, AP_WAIT_AGAIN, or AP_WAIT_INTERRUPT
+ */
+static enum ap_wait ap_sm_write(struct ap_device *ap_dev)
+{
+	struct ap_queue_status status;
+	struct ap_message *ap_msg;
+
+	if (ap_dev->requestq_count <= 0)
+		return AP_WAIT_NONE;
+	/* Start the next request on the queue. */
+	ap_msg = list_entry(ap_dev->requestq.next, struct ap_message, list);
+	status = __ap_send(ap_dev->qid, ap_msg->psmid,
+			   ap_msg->message, ap_msg->length, ap_msg->special);
+	switch (status.response_code) {
+	case AP_RESPONSE_NORMAL:
+		atomic_inc(&ap_poll_requests);
+		ap_dev->queue_count++;
+		if (ap_dev->queue_count == 1)
+			mod_timer(&ap_dev->timeout,
+				  jiffies + ap_dev->drv->request_timeout);
+		list_move_tail(&ap_msg->list, &ap_dev->pendingq);
+		ap_dev->requestq_count--;
+		ap_dev->pendingq_count++;
+		if (ap_dev->queue_count < ap_dev->queue_depth) {
+			ap_dev->state = AP_STATE_WORKING;
+			return AP_WAIT_AGAIN;
+		}
+		/* fall through */
+	case AP_RESPONSE_Q_FULL:
+		ap_dev->state = AP_STATE_QUEUE_FULL;
+		return AP_WAIT_INTERRUPT;
+	case AP_RESPONSE_RESET_IN_PROGRESS:
+		ap_dev->state = AP_STATE_RESET_WAIT;
+		return AP_WAIT_TIMEOUT;
+	case AP_RESPONSE_MESSAGE_TOO_BIG:
+	case AP_RESPONSE_REQ_FAC_NOT_INST:
+		list_del_init(&ap_msg->list);
+		ap_dev->requestq_count--;
+		ap_msg->rc = -EINVAL;
+		ap_msg->receive(ap_dev, ap_msg, NULL);
+		return AP_WAIT_AGAIN;
+	default:
+		ap_dev->state = AP_STATE_BORKED;
+		return AP_WAIT_NONE;
+	}
+}
+
+/**
+ * ap_sm_read_write(): Send and receive messages to/from an AP device.
+ * @ap_dev: pointer to the AP device
+ *
+ * Returns AP_WAIT_NONE, AP_WAIT_AGAIN, or AP_WAIT_INTERRUPT
+ */
+static enum ap_wait ap_sm_read_write(struct ap_device *ap_dev)
+{
+	return min(ap_sm_read(ap_dev), ap_sm_write(ap_dev));
+}
+
+/**
+ * ap_sm_reset(): Reset an AP queue.
  * @qid: The AP queue number
  *
  * Submit the Reset command to an AP queue.
- * Since the reset is asynchron set the state to 'RESET_IN_PROGRESS'
- * and check later via ap_poll_queue() if the reset is done.
  */
-static int ap_init_queue(struct ap_device *ap_dev)
+static enum ap_wait ap_sm_reset(struct ap_device *ap_dev)
 {
 	struct ap_queue_status status;
 
 	status = ap_reset_queue(ap_dev->qid);
 	switch (status.response_code) {
 	case AP_RESPONSE_NORMAL:
-		ap_dev->interrupt = AP_INTR_DISABLED;
-		ap_dev->reset = AP_RESET_IN_PROGRESS;
-		return 0;
 	case AP_RESPONSE_RESET_IN_PROGRESS:
+		ap_dev->state = AP_STATE_RESET_WAIT;
+		ap_dev->interrupt = AP_INTR_DISABLED;
+		return AP_WAIT_TIMEOUT;
 	case AP_RESPONSE_BUSY:
-		return -EBUSY;
+		return AP_WAIT_TIMEOUT;
 	case AP_RESPONSE_Q_NOT_AVAIL:
 	case AP_RESPONSE_DECONFIGURED:
 	case AP_RESPONSE_CHECKSTOPPED:
 	default:
-		return -ENODEV;
+		ap_dev->state = AP_STATE_BORKED;
+		return AP_WAIT_NONE;
 	}
 }
 
 /**
- * ap_increase_queue_count(): Arm request timeout.
- * @ap_dev: Pointer to an AP device.
+ * ap_sm_reset_wait(): Test queue for completion of the reset operation
+ * @ap_dev: pointer to the AP device
  *
- * Arm request timeout if an AP device was idle and a new request is submitted.
+ * Returns AP_POLL_IMMEDIATELY, AP_POLL_AFTER_TIMEROUT or 0.
  */
-static void ap_increase_queue_count(struct ap_device *ap_dev)
+static enum ap_wait ap_sm_reset_wait(struct ap_device *ap_dev)
 {
-	int timeout = ap_dev->drv->request_timeout;
+	struct ap_queue_status status;
+	unsigned long info;
 
-	ap_dev->queue_count++;
-	if (ap_dev->queue_count == 1) {
-		mod_timer(&ap_dev->timeout, jiffies + timeout);
-		ap_dev->reset = AP_RESET_ARMED;
-	}
-}
-
-/**
- * ap_decrease_queue_count(): Decrease queue count.
- * @ap_dev: Pointer to an AP device.
- *
- * If AP device is still alive, re-schedule request timeout if there are still
- * pending requests.
- */
-static void ap_decrease_queue_count(struct ap_device *ap_dev)
-{
-	int timeout = ap_dev->drv->request_timeout;
-
-	ap_dev->queue_count--;
 	if (ap_dev->queue_count > 0)
-		mod_timer(&ap_dev->timeout, jiffies + timeout);
+		/* Try to read a completed message and get the status */
+		status = ap_sm_recv(ap_dev);
 	else
-		/*
-		 * The timeout timer should to be disabled now - since
-		 * del_timer_sync() is very expensive, we just tell via the
-		 * reset flag to ignore the pending timeout timer.
-		 */
-		ap_dev->reset = AP_RESET_IGNORE;
+		/* Get the status with TAPQ */
+		status = ap_test_queue(ap_dev->qid, &info);
+
+	switch (status.response_code) {
+	case AP_RESPONSE_NORMAL:
+		if (ap_using_interrupts() &&
+		    ap_queue_enable_interruption(ap_dev,
+						 ap_airq.lsi_ptr) == 0)
+			ap_dev->state = AP_STATE_SETIRQ_WAIT;
+		else
+			ap_dev->state = (ap_dev->queue_count > 0) ?
+				AP_STATE_WORKING : AP_STATE_IDLE;
+		return AP_WAIT_AGAIN;
+	case AP_RESPONSE_BUSY:
+	case AP_RESPONSE_RESET_IN_PROGRESS:
+		return AP_WAIT_TIMEOUT;
+	case AP_RESPONSE_Q_NOT_AVAIL:
+	case AP_RESPONSE_DECONFIGURED:
+	case AP_RESPONSE_CHECKSTOPPED:
+	default:
+		ap_dev->state = AP_STATE_BORKED;
+		return AP_WAIT_NONE;
+	}
+}
+
+/**
+ * ap_sm_setirq_wait(): Test queue for completion of the irq enablement
+ * @ap_dev: pointer to the AP device
+ *
+ * Returns AP_POLL_IMMEDIATELY, AP_POLL_AFTER_TIMEROUT or 0.
+ */
+static enum ap_wait ap_sm_setirq_wait(struct ap_device *ap_dev)
+{
+	struct ap_queue_status status;
+	unsigned long info;
+
+	if (ap_dev->queue_count > 0)
+		/* Try to read a completed message and get the status */
+		status = ap_sm_recv(ap_dev);
+	else
+		/* Get the status with TAPQ */
+		status = ap_test_queue(ap_dev->qid, &info);
+
+	if (status.int_enabled == 1) {
+		/* Irqs are now enabled */
+		ap_dev->interrupt = AP_INTR_ENABLED;
+		ap_dev->state = (ap_dev->queue_count > 0) ?
+			AP_STATE_WORKING : AP_STATE_IDLE;
+	}
+
+	switch (status.response_code) {
+	case AP_RESPONSE_NORMAL:
+		if (ap_dev->queue_count > 0)
+			return AP_WAIT_AGAIN;
+		/* fallthrough */
+	case AP_RESPONSE_NO_PENDING_REPLY:
+		return AP_WAIT_TIMEOUT;
+	default:
+		ap_dev->state = AP_STATE_BORKED;
+		return AP_WAIT_NONE;
+	}
+}
+
+/*
+ * AP state machine jump table
+ */
+ap_func_t *ap_jumptable[NR_AP_STATES][NR_AP_EVENTS] = {
+	[AP_STATE_RESET_START] = {
+		[AP_EVENT_POLL] = ap_sm_reset,
+		[AP_EVENT_TIMEOUT] = ap_sm_nop,
+	},
+	[AP_STATE_RESET_WAIT] = {
+		[AP_EVENT_POLL] = ap_sm_reset_wait,
+		[AP_EVENT_TIMEOUT] = ap_sm_nop,
+	},
+	[AP_STATE_SETIRQ_WAIT] = {
+		[AP_EVENT_POLL] = ap_sm_setirq_wait,
+		[AP_EVENT_TIMEOUT] = ap_sm_nop,
+	},
+	[AP_STATE_IDLE] = {
+		[AP_EVENT_POLL] = ap_sm_write,
+		[AP_EVENT_TIMEOUT] = ap_sm_nop,
+	},
+	[AP_STATE_WORKING] = {
+		[AP_EVENT_POLL] = ap_sm_read_write,
+		[AP_EVENT_TIMEOUT] = ap_sm_reset,
+	},
+	[AP_STATE_QUEUE_FULL] = {
+		[AP_EVENT_POLL] = ap_sm_read,
+		[AP_EVENT_TIMEOUT] = ap_sm_reset,
+	},
+	[AP_STATE_SUSPEND_WAIT] = {
+		[AP_EVENT_POLL] = ap_sm_read,
+		[AP_EVENT_TIMEOUT] = ap_sm_nop,
+	},
+	[AP_STATE_BORKED] = {
+		[AP_EVENT_POLL] = ap_sm_nop,
+		[AP_EVENT_TIMEOUT] = ap_sm_nop,
+	},
+};
+
+static inline enum ap_wait ap_sm_event(struct ap_device *ap_dev,
+				       enum ap_event event)
+{
+	return ap_jumptable[ap_dev->state][event](ap_dev);
+}
+
+static inline enum ap_wait ap_sm_event_loop(struct ap_device *ap_dev,
+					    enum ap_event event)
+{
+	enum ap_wait wait;
+
+	while ((wait = ap_sm_event(ap_dev, event)) == AP_WAIT_AGAIN)
+		;
+	return wait;
+}
+
+/**
+ * ap_request_timeout(): Handling of request timeouts
+ * @data: Holds the AP device.
+ *
+ * Handles request timeouts.
+ */
+static void ap_request_timeout(unsigned long data)
+{
+	struct ap_device *ap_dev = (struct ap_device *) data;
+
+	if (ap_suspend_flag)
+		return;
+	spin_lock_bh(&ap_dev->lock);
+	ap_sm_wait(ap_sm_event(ap_dev, AP_EVENT_TIMEOUT));
+	spin_unlock_bh(&ap_dev->lock);
+}
+
+/**
+ * ap_poll_timeout(): AP receive polling for finished AP requests.
+ * @unused: Unused pointer.
+ *
+ * Schedules the AP tasklet using a high resolution timer.
+ */
+static enum hrtimer_restart ap_poll_timeout(struct hrtimer *unused)
+{
+	if (!ap_suspend_flag)
+		tasklet_schedule(&ap_tasklet);
+	return HRTIMER_NORESTART;
+}
+
+/**
+ * ap_interrupt_handler() - Schedule ap_tasklet on interrupt
+ * @airq: pointer to adapter interrupt descriptor
+ */
+static void ap_interrupt_handler(struct airq_struct *airq)
+{
+	inc_irq_stat(IRQIO_APB);
+	if (!ap_suspend_flag)
+		tasklet_schedule(&ap_tasklet);
+}
+
+/**
+ * ap_tasklet_fn(): Tasklet to poll all AP devices.
+ * @dummy: Unused variable
+ *
+ * Poll all AP devices on the bus.
+ */
+static void ap_tasklet_fn(unsigned long dummy)
+{
+	struct ap_device *ap_dev;
+	enum ap_wait wait = AP_WAIT_NONE;
+
+	/* Reset the indicator if interrupts are used. Thus new interrupts can
+	 * be received. Doing it in the beginning of the tasklet is therefor
+	 * important that no requests on any AP get lost.
+	 */
+	if (ap_using_interrupts())
+		xchg(ap_airq.lsi_ptr, 0);
+
+	spin_lock(&ap_device_list_lock);
+	list_for_each_entry(ap_dev, &ap_device_list, list) {
+		spin_lock_bh(&ap_dev->lock);
+		wait = min(wait, ap_sm_event_loop(ap_dev, AP_EVENT_POLL));
+		spin_unlock_bh(&ap_dev->lock);
+	}
+	spin_unlock(&ap_device_list_lock);
+	ap_sm_wait(wait);
 }
 
 /**
@@ -623,8 +918,6 @@
 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();
@@ -638,21 +931,12 @@
 		}
 		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);
+		ap_tasklet_fn(0);
 	} while (!kthread_should_stop());
 	return 0;
 }
@@ -682,6 +966,57 @@
 	mutex_unlock(&ap_poll_thread_mutex);
 }
 
+/**
+ * ap_queue_message(): Queue a request to an AP device.
+ * @ap_dev: The AP device to queue the message to
+ * @ap_msg: The message that is to be added
+ */
+void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
+{
+	/* For asynchronous message handling a valid receive-callback
+	 * is required. */
+	BUG_ON(!ap_msg->receive);
+
+	spin_lock_bh(&ap_dev->lock);
+	/* Queue the message. */
+	list_add_tail(&ap_msg->list, &ap_dev->requestq);
+	ap_dev->requestq_count++;
+	ap_dev->total_request_count++;
+	/* Send/receive as many request from the queue as possible. */
+	ap_sm_wait(ap_sm_event_loop(ap_dev, AP_EVENT_POLL));
+	spin_unlock_bh(&ap_dev->lock);
+}
+EXPORT_SYMBOL(ap_queue_message);
+
+/**
+ * ap_cancel_message(): Cancel a crypto request.
+ * @ap_dev: The AP device that has the message queued
+ * @ap_msg: The message that is to be removed
+ *
+ * Cancel a crypto request. This is done by removing the request
+ * from the device pending or request queue. Note that the
+ * request stays on the AP queue. When it finishes the message
+ * reply will be discarded because the psmid can't be found.
+ */
+void ap_cancel_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
+{
+	struct ap_message *tmp;
+
+	spin_lock_bh(&ap_dev->lock);
+	if (!list_empty(&ap_msg->list)) {
+		list_for_each_entry(tmp, &ap_dev->pendingq, list)
+			if (tmp->psmid == ap_msg->psmid) {
+				ap_dev->pendingq_count--;
+				goto found;
+			}
+		ap_dev->requestq_count--;
+found:
+		list_del_init(&ap_msg->list);
+	}
+	spin_unlock_bh(&ap_dev->lock);
+}
+EXPORT_SYMBOL(ap_cancel_message);
+
 /*
  * AP device related attributes.
  */
@@ -762,21 +1097,17 @@
 	int rc = 0;
 
 	spin_lock_bh(&ap_dev->lock);
-	switch (ap_dev->reset) {
-	case AP_RESET_IGNORE:
-		rc = snprintf(buf, PAGE_SIZE, "No Reset Timer set.\n");
-		break;
-	case AP_RESET_ARMED:
-		rc = snprintf(buf, PAGE_SIZE, "Reset Timer armed.\n");
-		break;
-	case AP_RESET_DO:
-		rc = snprintf(buf, PAGE_SIZE, "Reset Timer expired.\n");
-		break;
-	case AP_RESET_IN_PROGRESS:
+	switch (ap_dev->state) {
+	case AP_STATE_RESET_START:
+	case AP_STATE_RESET_WAIT:
 		rc = snprintf(buf, PAGE_SIZE, "Reset in progress.\n");
 		break;
-	default:
+	case AP_STATE_WORKING:
+	case AP_STATE_QUEUE_FULL:
+		rc = snprintf(buf, PAGE_SIZE, "Reset Timer armed.\n");
 		break;
+	default:
+		rc = snprintf(buf, PAGE_SIZE, "No Reset Timer set.\n");
 	}
 	spin_unlock_bh(&ap_dev->lock);
 	return rc;
@@ -791,17 +1122,12 @@
 	int rc = 0;
 
 	spin_lock_bh(&ap_dev->lock);
-	switch (ap_dev->interrupt) {
-	case AP_INTR_DISABLED:
-		rc = snprintf(buf, PAGE_SIZE, "Interrupts disabled.\n");
-		break;
-	case AP_INTR_ENABLED:
-		rc = snprintf(buf, PAGE_SIZE, "Interrupts enabled.\n");
-		break;
-	case AP_INTR_IN_PROGRESS:
+	if (ap_dev->state == AP_STATE_SETIRQ_WAIT)
 		rc = snprintf(buf, PAGE_SIZE, "Enable Interrupt pending.\n");
-		break;
-	}
+	else if (ap_dev->interrupt == AP_INTR_ENABLED)
+		rc = snprintf(buf, PAGE_SIZE, "Interrupts enabled.\n");
+	else
+		rc = snprintf(buf, PAGE_SIZE, "Interrupts disabled.\n");
 	spin_unlock_bh(&ap_dev->lock);
 	return rc;
 }
@@ -898,16 +1224,14 @@
 static int ap_dev_suspend(struct device *dev, pm_message_t state)
 {
 	struct ap_device *ap_dev = to_ap_dev(dev);
-	unsigned long flags;
 
 	/* Poll on the device until all requests are finished. */
-	do {
-		flags = 0;
-		spin_lock_bh(&ap_dev->lock);
-		__ap_poll_device(ap_dev, &flags);
-		spin_unlock_bh(&ap_dev->lock);
-	} while ((flags & 1) || (flags & 2));
-
+	spin_lock_bh(&ap_dev->lock);
+	ap_dev->state = AP_STATE_SUSPEND_WAIT;
+	while (ap_sm_event(ap_dev, AP_EVENT_POLL) != AP_WAIT_NONE)
+		;
+	ap_dev->state = AP_STATE_BORKED;
+	spin_unlock_bh(&ap_dev->lock);
 	return 0;
 }
 
@@ -957,7 +1281,6 @@
 		xchg(ap_airq.lsi_ptr, 0);
 	tasklet_enable(&ap_tasklet);
 	queue_work(ap_work_queue, &ap_config_work);
-	wake_up(&ap_poll_wait);
 }
 
 static int ap_power_event(struct notifier_block *this, unsigned long event,
@@ -996,21 +1319,9 @@
 	int rc;
 
 	ap_dev->drv = ap_drv;
-
-	spin_lock_bh(&ap_device_list_lock);
-	list_add(&ap_dev->list, &ap_device_list);
-	spin_unlock_bh(&ap_device_list_lock);
-
 	rc = ap_drv->probe ? ap_drv->probe(ap_dev) : -ENODEV;
-	if (rc) {
-		spin_lock_bh(&ap_device_list_lock);
-		list_del_init(&ap_dev->list);
-		spin_unlock_bh(&ap_device_list_lock);
-	} else {
-		if (ap_dev->reset == AP_RESET_IN_PROGRESS ||
-			ap_dev->interrupt == AP_INTR_IN_PROGRESS)
-			__ap_schedule_poll_timer();
-	}
+	if (rc)
+		ap_dev->drv = NULL;
 	return rc;
 }
 
@@ -1064,6 +1375,11 @@
 	return 0;
 }
 
+static void ap_device_release(struct device *dev)
+{
+	kfree(to_ap_dev(dev));
+}
+
 int ap_driver_register(struct ap_driver *ap_drv, struct module *owner,
 		       char *name)
 {
@@ -1371,13 +1687,6 @@
 	return rc;
 }
 
-static void ap_interrupt_handler(struct airq_struct *airq)
-{
-	inc_irq_stat(IRQIO_APB);
-	if (!ap_suspend_flag)
-		tasklet_schedule(&ap_tasklet);
-}
-
 /**
  * __ap_scan_bus(): Scan the AP bus.
  * @dev: Pointer to device
@@ -1390,21 +1699,14 @@
 	return to_ap_dev(dev)->qid == (ap_qid_t)(unsigned long) data;
 }
 
-static void ap_device_release(struct device *dev)
-{
-	struct ap_device *ap_dev = to_ap_dev(dev);
-
-	kfree(ap_dev);
-}
-
 static void ap_scan_bus(struct work_struct *unused)
 {
 	struct ap_device *ap_dev;
 	struct device *dev;
 	ap_qid_t qid;
 	int queue_depth = 0, device_type = 0;
-	unsigned int device_functions;
-	int rc, i;
+	unsigned int device_functions = 0;
+	int rc, i, borked;
 
 	ap_query_configuration();
 	if (ap_select_domain() != 0)
@@ -1420,17 +1722,15 @@
 		if (dev) {
 			ap_dev = to_ap_dev(dev);
 			spin_lock_bh(&ap_dev->lock);
-			if (rc == -ENODEV || ap_dev->unregistered) {
-				spin_unlock_bh(&ap_dev->lock);
-				if (ap_dev->unregistered)
-					i--;
-				device_unregister(dev);
-				put_device(dev);
-				continue;
-			}
+			if (rc == -ENODEV)
+				ap_dev->state = AP_STATE_BORKED;
+			borked = ap_dev->state == AP_STATE_BORKED;
 			spin_unlock_bh(&ap_dev->lock);
+			if (borked)	/* Remove broken device */
+				device_unregister(dev);
 			put_device(dev);
-			continue;
+			if (!borked)
+				continue;
 		}
 		if (rc)
 			continue;
@@ -1438,16 +1738,12 @@
 		if (!ap_dev)
 			break;
 		ap_dev->qid = qid;
-		rc = ap_init_queue(ap_dev);
-		if ((rc != 0) && (rc != -EBUSY)) {
-			kfree(ap_dev);
-			continue;
-		}
+		ap_dev->state = AP_STATE_RESET_START;
+		ap_dev->interrupt = AP_INTR_DISABLED;
 		ap_dev->queue_depth = queue_depth;
 		ap_dev->raw_hwtype = device_type;
 		ap_dev->device_type = device_type;
 		ap_dev->functions = device_functions;
-		ap_dev->unregistered = 1;
 		spin_lock_init(&ap_dev->lock);
 		INIT_LIST_HEAD(&ap_dev->pendingq);
 		INIT_LIST_HEAD(&ap_dev->requestq);
@@ -1463,27 +1759,37 @@
 
 		ap_dev->device.bus = &ap_bus_type;
 		ap_dev->device.parent = ap_root_device;
-		if (dev_set_name(&ap_dev->device, "card%02x",
-				 AP_QID_DEVICE(ap_dev->qid))) {
+		rc = dev_set_name(&ap_dev->device, "card%02x",
+				  AP_QID_DEVICE(ap_dev->qid));
+		if (rc) {
 			kfree(ap_dev);
 			continue;
 		}
+		/* Add to list of devices */
+		spin_lock_bh(&ap_device_list_lock);
+		list_add(&ap_dev->list, &ap_device_list);
+		spin_unlock_bh(&ap_device_list_lock);
+		/* Start with a device reset */
+		spin_lock_bh(&ap_dev->lock);
+		ap_sm_wait(ap_sm_event(ap_dev, AP_EVENT_POLL));
+		spin_unlock_bh(&ap_dev->lock);
+		/* Register device */
 		ap_dev->device.release = ap_device_release;
 		rc = device_register(&ap_dev->device);
 		if (rc) {
+			spin_lock_bh(&ap_dev->lock);
+			list_del_init(&ap_dev->list);
+			spin_unlock_bh(&ap_dev->lock);
 			put_device(&ap_dev->device);
 			continue;
 		}
 		/* Add device attributes. */
 		rc = sysfs_create_group(&ap_dev->device.kobj,
 					&ap_dev_attr_group);
-		if (!rc) {
-			spin_lock_bh(&ap_dev->lock);
-			ap_dev->unregistered = 0;
-			spin_unlock_bh(&ap_dev->lock);
-		}
-		else
+		if (rc) {
 			device_unregister(&ap_dev->device);
+			continue;
+		}
 	}
 out:
 	mod_timer(&ap_config_timer, jiffies + ap_config_time * HZ);
@@ -1496,377 +1802,6 @@
 	queue_work(ap_work_queue, &ap_config_work);
 }
 
-/**
- * ap_poll_read(): Receive pending reply messages from an AP device.
- * @ap_dev: pointer to the AP device
- * @flags: pointer to control flags, bit 2^0 is set if another poll is
- *	   required, bit 2^1 is set if the poll timer needs to get armed
- *
- * Returns 0 if the device is still present, -ENODEV if not.
- */
-static int ap_poll_read(struct ap_device *ap_dev, unsigned long *flags)
-{
-	struct ap_queue_status status;
-	struct ap_message *ap_msg;
-
-	if (ap_dev->queue_count <= 0)
-		return 0;
-	status = __ap_recv(ap_dev->qid, &ap_dev->reply->psmid,
-			   ap_dev->reply->message, ap_dev->reply->length);
-	switch (status.response_code) {
-	case AP_RESPONSE_NORMAL:
-		ap_dev->interrupt = status.int_enabled;
-		atomic_dec(&ap_poll_requests);
-		ap_decrease_queue_count(ap_dev);
-		list_for_each_entry(ap_msg, &ap_dev->pendingq, list) {
-			if (ap_msg->psmid != ap_dev->reply->psmid)
-				continue;
-			list_del_init(&ap_msg->list);
-			ap_dev->pendingq_count--;
-			ap_msg->receive(ap_dev, ap_msg, ap_dev->reply);
-			break;
-		}
-		if (ap_dev->queue_count > 0)
-			*flags |= 1;
-		break;
-	case AP_RESPONSE_NO_PENDING_REPLY:
-		ap_dev->interrupt = status.int_enabled;
-		if (status.queue_empty) {
-			/* The card shouldn't forget requests but who knows. */
-			atomic_sub(ap_dev->queue_count, &ap_poll_requests);
-			ap_dev->queue_count = 0;
-			list_splice_init(&ap_dev->pendingq, &ap_dev->requestq);
-			ap_dev->requestq_count += ap_dev->pendingq_count;
-			ap_dev->pendingq_count = 0;
-		} else
-			*flags |= 2;
-		break;
-	default:
-		return -ENODEV;
-	}
-	return 0;
-}
-
-/**
- * ap_poll_write(): Send messages from the request queue to an AP device.
- * @ap_dev: pointer to the AP device
- * @flags: pointer to control flags, bit 2^0 is set if another poll is
- *	   required, bit 2^1 is set if the poll timer needs to get armed
- *
- * Returns 0 if the device is still present, -ENODEV if not.
- */
-static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags)
-{
-	struct ap_queue_status status;
-	struct ap_message *ap_msg;
-
-	if (ap_dev->requestq_count <= 0 ||
-	    (ap_dev->queue_count >= ap_dev->queue_depth) ||
-	    (ap_dev->reset == AP_RESET_IN_PROGRESS))
-		return 0;
-	/* Start the next request on the queue. */
-	ap_msg = list_entry(ap_dev->requestq.next, struct ap_message, list);
-	status = __ap_send(ap_dev->qid, ap_msg->psmid,
-			   ap_msg->message, ap_msg->length, ap_msg->special);
-	switch (status.response_code) {
-	case AP_RESPONSE_NORMAL:
-		atomic_inc(&ap_poll_requests);
-		ap_increase_queue_count(ap_dev);
-		list_move_tail(&ap_msg->list, &ap_dev->pendingq);
-		ap_dev->requestq_count--;
-		ap_dev->pendingq_count++;
-		if (ap_dev->queue_count < ap_dev->queue_depth &&
-		    ap_dev->requestq_count > 0)
-			*flags |= 1;
-		*flags |= 2;
-		break;
-	case AP_RESPONSE_RESET_IN_PROGRESS:
-		__ap_schedule_poll_timer();
-	case AP_RESPONSE_Q_FULL:
-		*flags |= 2;
-		break;
-	case AP_RESPONSE_MESSAGE_TOO_BIG:
-	case AP_RESPONSE_REQ_FAC_NOT_INST:
-		return -EINVAL;
-	default:
-		return -ENODEV;
-	}
-	return 0;
-}
-
-/**
- * ap_poll_queue(): Poll AP device for pending replies and send new messages.
- * Check if the queue has a pending reset. In case it's done re-enable
- * interrupts, otherwise reschedule the poll_timer for another attempt.
- * @ap_dev: pointer to the bus device
- * @flags: pointer to control flags, bit 2^0 is set if another poll is
- *	   required, bit 2^1 is set if the poll timer needs to get armed
- *
- * Poll AP device for pending replies and send new messages. If either
- * ap_poll_read or ap_poll_write returns -ENODEV unregister the device.
- * Returns 0.
- */
-static inline int ap_poll_queue(struct ap_device *ap_dev, unsigned long *flags)
-{
-	struct ap_queue_status status;
-	int rc;
-
-	if (ap_dev->reset == AP_RESET_IN_PROGRESS) {
-		status = ap_test_queue(ap_dev->qid, NULL);
-		switch (status.response_code) {
-		case AP_RESPONSE_NORMAL:
-			ap_dev->reset = AP_RESET_IGNORE;
-			if (ap_using_interrupts()) {
-				rc = ap_queue_enable_interruption(
-					ap_dev, ap_airq.lsi_ptr);
-				if (!rc)
-					ap_dev->interrupt = AP_INTR_IN_PROGRESS;
-				else if (rc == -ENODEV) {
-					pr_err("Registering adapter interrupts for "
-					"AP %d failed\n", AP_QID_DEVICE(ap_dev->qid));
-					return rc;
-				}
-			}
-			/* fall through */
-		case AP_RESPONSE_BUSY:
-		case AP_RESPONSE_RESET_IN_PROGRESS:
-			*flags |= AP_POLL_AFTER_TIMEOUT;
-			break;
-		case AP_RESPONSE_Q_NOT_AVAIL:
-		case AP_RESPONSE_DECONFIGURED:
-		case AP_RESPONSE_CHECKSTOPPED:
-			return -ENODEV;
-		default:
-			break;
-		}
-	}
-
-	if ((ap_dev->reset != AP_RESET_IN_PROGRESS) &&
-		(ap_dev->interrupt == AP_INTR_IN_PROGRESS)) {
-		status = ap_test_queue(ap_dev->qid, NULL);
-		if (ap_using_interrupts()) {
-			if (status.int_enabled == 1)
-				ap_dev->interrupt = AP_INTR_ENABLED;
-			else
-				*flags |= AP_POLL_AFTER_TIMEOUT;
-		} else
-			ap_dev->interrupt = AP_INTR_DISABLED;
-	}
-
-	rc = ap_poll_read(ap_dev, flags);
-	if (rc)
-		return rc;
-	return ap_poll_write(ap_dev, flags);
-}
-
-/**
- * __ap_queue_message(): Queue a message to a device.
- * @ap_dev: pointer to the AP device
- * @ap_msg: the message to be queued
- *
- * Queue a message to a device. Returns 0 if successful.
- */
-static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
-{
-	struct ap_queue_status status;
-
-	if (list_empty(&ap_dev->requestq) &&
-	    (ap_dev->queue_count < ap_dev->queue_depth) &&
-	    (ap_dev->reset != AP_RESET_IN_PROGRESS)) {
-		status = __ap_send(ap_dev->qid, ap_msg->psmid,
-				   ap_msg->message, ap_msg->length,
-				   ap_msg->special);
-		switch (status.response_code) {
-		case AP_RESPONSE_NORMAL:
-			list_add_tail(&ap_msg->list, &ap_dev->pendingq);
-			atomic_inc(&ap_poll_requests);
-			ap_dev->pendingq_count++;
-			ap_increase_queue_count(ap_dev);
-			ap_dev->total_request_count++;
-			break;
-		case AP_RESPONSE_Q_FULL:
-		case AP_RESPONSE_RESET_IN_PROGRESS:
-			list_add_tail(&ap_msg->list, &ap_dev->requestq);
-			ap_dev->requestq_count++;
-			ap_dev->total_request_count++;
-			return -EBUSY;
-		case AP_RESPONSE_REQ_FAC_NOT_INST:
-		case AP_RESPONSE_MESSAGE_TOO_BIG:
-			ap_msg->rc = -EINVAL;
-			ap_msg->receive(ap_dev, ap_msg, NULL);
-			return -EINVAL;
-		default:	/* Device is gone. */
-			ap_msg->rc = -ENODEV;
-			ap_msg->receive(ap_dev, ap_msg, NULL);
-			return -ENODEV;
-		}
-	} else {
-		list_add_tail(&ap_msg->list, &ap_dev->requestq);
-		ap_dev->requestq_count++;
-		ap_dev->total_request_count++;
-		return -EBUSY;
-	}
-	ap_schedule_poll_timer();
-	return 0;
-}
-
-void ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
-{
-	unsigned long flags;
-	int rc;
-
-	/* For asynchronous message handling a valid receive-callback
-	 * is required. */
-	BUG_ON(!ap_msg->receive);
-
-	spin_lock_bh(&ap_dev->lock);
-	if (!ap_dev->unregistered) {
-		/* Make room on the queue by polling for finished requests. */
-		rc = ap_poll_queue(ap_dev, &flags);
-		if (!rc)
-			rc = __ap_queue_message(ap_dev, ap_msg);
-		if (!rc)
-			wake_up(&ap_poll_wait);
-		if (rc == -ENODEV)
-			ap_dev->unregistered = 1;
-	} else {
-		ap_msg->rc = -ENODEV;
-		ap_msg->receive(ap_dev, ap_msg, NULL);
-		rc = -ENODEV;
-	}
-	spin_unlock_bh(&ap_dev->lock);
-}
-EXPORT_SYMBOL(ap_queue_message);
-
-/**
- * ap_cancel_message(): Cancel a crypto request.
- * @ap_dev: The AP device that has the message queued
- * @ap_msg: The message that is to be removed
- *
- * Cancel a crypto request. This is done by removing the request
- * from the device pending or request queue. Note that the
- * request stays on the AP queue. When it finishes the message
- * reply will be discarded because the psmid can't be found.
- */
-void ap_cancel_message(struct ap_device *ap_dev, struct ap_message *ap_msg)
-{
-	struct ap_message *tmp;
-
-	spin_lock_bh(&ap_dev->lock);
-	if (!list_empty(&ap_msg->list)) {
-		list_for_each_entry(tmp, &ap_dev->pendingq, list)
-			if (tmp->psmid == ap_msg->psmid) {
-				ap_dev->pendingq_count--;
-				goto found;
-			}
-		ap_dev->requestq_count--;
-	found:
-		list_del_init(&ap_msg->list);
-	}
-	spin_unlock_bh(&ap_dev->lock);
-}
-EXPORT_SYMBOL(ap_cancel_message);
-
-/**
- * ap_poll_timeout(): AP receive polling for finished AP requests.
- * @unused: Unused pointer.
- *
- * Schedules the AP tasklet using a high resolution timer.
- */
-static enum hrtimer_restart ap_poll_timeout(struct hrtimer *unused)
-{
-	if (!ap_suspend_flag)
-		tasklet_schedule(&ap_tasklet);
-	return HRTIMER_NORESTART;
-}
-
-/**
- * ap_reset(): Reset a not responding AP device.
- * @ap_dev: Pointer to the AP device
- *
- * Reset a not responding AP device and move all requests from the
- * pending queue to the request queue.
- */
-static void ap_reset(struct ap_device *ap_dev, unsigned long *flags)
-{
-	int rc;
-
-	atomic_sub(ap_dev->queue_count, &ap_poll_requests);
-	ap_dev->queue_count = 0;
-	list_splice_init(&ap_dev->pendingq, &ap_dev->requestq);
-	ap_dev->requestq_count += ap_dev->pendingq_count;
-	ap_dev->pendingq_count = 0;
-	rc = ap_init_queue(ap_dev);
-	if (rc == -ENODEV)
-		ap_dev->unregistered = 1;
-	else
-		*flags |= AP_POLL_AFTER_TIMEOUT;
-}
-
-static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags)
-{
-	if (!ap_dev->unregistered) {
-		if (ap_poll_queue(ap_dev, flags))
-			ap_dev->unregistered = 1;
-		if (ap_dev->reset == AP_RESET_DO)
-			ap_reset(ap_dev, flags);
-	}
-	return 0;
-}
-
-/**
- * ap_poll_all(): Poll all AP devices.
- * @dummy: Unused variable
- *
- * Poll all AP devices on the bus in a round robin fashion. Continue
- * polling until bit 2^0 of the control flags is not set. If bit 2^1
- * of the control flags has been set arm the poll timer.
- */
-static void ap_poll_all(unsigned long dummy)
-{
-	unsigned long flags;
-	struct ap_device *ap_dev;
-
-	/* Reset the indicator if interrupts are used. Thus new interrupts can
-	 * be received. Doing it in the beginning of the tasklet is therefor
-	 * important that no requests on any AP get lost.
-	 */
-	if (ap_using_interrupts())
-		xchg(ap_airq.lsi_ptr, 0);
-	do {
-		flags = 0;
-		spin_lock(&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(&ap_device_list_lock);
-	} while (flags & AP_POLL_IMMEDIATELY);
-	if (flags & AP_POLL_AFTER_TIMEOUT)
-		__ap_schedule_poll_timer();
-}
-
-/**
- * ap_request_timeout(): Handling of request timeouts
- * @data: Holds the AP device.
- *
- * Handles request timeouts.
- */
-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;
-
-		if (ap_using_interrupts())
-			tasklet_schedule(&ap_tasklet);
-	}
-}
-
 static void ap_reset_domain(void)
 {
 	int i;
@@ -1958,14 +1893,11 @@
 		goto out_root;
 	}
 
-	if (ap_select_domain() == 0)
-		ap_scan_bus(NULL);
-
 	/* Setup the AP bus rescan timer. */
 	setup_timer(&ap_config_timer, ap_config_timeout, 0);
-	add_timer(&ap_config_timer);
 
-	/* Setup the high resultion poll timer.
+	/*
+	 * Setup the high resultion poll timer.
 	 * If we are running under z/VM adjust polling to z/VM polling rate.
 	 */
 	if (MACHINE_IS_VM)
@@ -1985,12 +1917,13 @@
 	if (rc)
 		goto out_pm;
 
+	queue_work(ap_work_queue, &ap_config_work);
+
 	return 0;
 
 out_pm:
 	ap_poll_thread_stop();
 out_work:
-	del_timer_sync(&ap_config_timer);
 	hrtimer_cancel(&ap_poll_timer);
 	destroy_workqueue(ap_work_queue);
 out_root:
diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h
index af9b705..6adcbdf2 100644
--- a/drivers/s390/crypto/ap_bus.h
+++ b/drivers/s390/crypto/ap_bus.h
@@ -36,9 +36,6 @@
 #define AP_CONFIG_TIME 30	/* Time in seconds between AP bus rescans. */
 #define AP_POLL_TIME 1		/* Time in ticks between receive polls. */
 
-#define AP_POLL_IMMEDIATELY	1 /* continue running poll tasklet */
-#define AP_POLL_AFTER_TIMEOUT	2 /* run poll tasklet again after timout */
-
 extern int ap_domain_index;
 
 /**
@@ -120,19 +117,45 @@
 #define AP_FUNC_APXA  6
 
 /*
- * AP reset flag states
- */
-#define AP_RESET_IGNORE	0	/* request timeout will be ignored */
-#define AP_RESET_ARMED	1	/* request timeout timer is active */
-#define AP_RESET_DO	2	/* AP reset required */
-#define AP_RESET_IN_PROGRESS	3	/* AP reset in progress */
-
-/*
  * AP interrupt states
  */
 #define AP_INTR_DISABLED	0	/* AP interrupt disabled */
 #define AP_INTR_ENABLED		1	/* AP interrupt enabled */
-#define AP_INTR_IN_PROGRESS	3	/* AP interrupt in progress */
+
+/*
+ * AP device states
+ */
+enum ap_state {
+	AP_STATE_RESET_START,
+	AP_STATE_RESET_WAIT,
+	AP_STATE_SETIRQ_WAIT,
+	AP_STATE_IDLE,
+	AP_STATE_WORKING,
+	AP_STATE_QUEUE_FULL,
+	AP_STATE_SUSPEND_WAIT,
+	AP_STATE_BORKED,
+	NR_AP_STATES
+};
+
+/*
+ * AP device events
+ */
+enum ap_event {
+	AP_EVENT_POLL,
+	AP_EVENT_TIMEOUT,
+	NR_AP_EVENTS
+};
+
+/*
+ * AP wait behaviour
+ */
+enum ap_wait {
+	AP_WAIT_AGAIN,		/* retry immediately */
+	AP_WAIT_TIMEOUT,	/* wait for timeout */
+	AP_WAIT_INTERRUPT,	/* wait for thin interrupt (if available) */
+	AP_WAIT_NONE,		/* no wait */
+	NR_AP_WAIT
+};
 
 struct ap_device;
 struct ap_message;
@@ -151,20 +174,22 @@
 int ap_driver_register(struct ap_driver *, struct module *, char *);
 void ap_driver_unregister(struct ap_driver *);
 
+typedef enum ap_wait (ap_func_t)(struct ap_device *ap_dev);
+
 struct ap_device {
 	struct device device;
 	struct ap_driver *drv;		/* Pointer to AP device driver. */
 	spinlock_t lock;		/* Per device lock. */
 	struct list_head list;		/* private list of all AP devices. */
 
+	enum ap_state state;		/* State of the AP device. */
+
 	ap_qid_t qid;			/* AP queue id. */
 	int queue_depth;		/* AP queue depth.*/
 	int device_type;		/* AP device type. */
 	int raw_hwtype;			/* AP raw hardware type. */
 	unsigned int functions;		/* AP device function bitfield. */
-	int unregistered;		/* marks AP device as unregistered */
 	struct timer_list timeout;	/* Timer for request timeouts. */
-	int reset;			/* Reset required after req. timeout. */
 
 	int interrupt;			/* indicate if interrupts are enabled */
 	int queue_count;		/* # messages currently on AP queue. */