|
@@ -60,7 +60,7 @@ 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);
|
|
|
+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);
|
|
@@ -310,35 +310,26 @@ static inline int __ap_query_configuration(struct ap_config_info *config)
|
|
|
static int ap_query_functions(ap_qid_t qid, unsigned int *functions)
|
|
|
{
|
|
|
struct ap_queue_status status;
|
|
|
- int i;
|
|
|
+
|
|
|
status = __ap_query_functions(qid, functions);
|
|
|
|
|
|
- for (i = 0; i < AP_MAX_RESET; i++) {
|
|
|
- if (ap_queue_status_invalid_test(&status))
|
|
|
- return -ENODEV;
|
|
|
+ if (ap_queue_status_invalid_test(&status))
|
|
|
+ return -ENODEV;
|
|
|
|
|
|
- switch (status.response_code) {
|
|
|
- case AP_RESPONSE_NORMAL:
|
|
|
- return 0;
|
|
|
- case AP_RESPONSE_RESET_IN_PROGRESS:
|
|
|
- case AP_RESPONSE_BUSY:
|
|
|
- break;
|
|
|
- case AP_RESPONSE_Q_NOT_AVAIL:
|
|
|
- case AP_RESPONSE_DECONFIGURED:
|
|
|
- case AP_RESPONSE_CHECKSTOPPED:
|
|
|
- case AP_RESPONSE_INVALID_ADDRESS:
|
|
|
- return -ENODEV;
|
|
|
- case AP_RESPONSE_OTHERWISE_CHANGED:
|
|
|
- break;
|
|
|
- default:
|
|
|
- break;
|
|
|
- }
|
|
|
- if (i < AP_MAX_RESET - 1) {
|
|
|
- udelay(5);
|
|
|
- status = __ap_query_functions(qid, functions);
|
|
|
- }
|
|
|
+ switch (status.response_code) {
|
|
|
+ case AP_RESPONSE_NORMAL:
|
|
|
+ return 0;
|
|
|
+ case AP_RESPONSE_Q_NOT_AVAIL:
|
|
|
+ case AP_RESPONSE_DECONFIGURED:
|
|
|
+ case AP_RESPONSE_CHECKSTOPPED:
|
|
|
+ case AP_RESPONSE_INVALID_ADDRESS:
|
|
|
+ return -ENODEV;
|
|
|
+ case AP_RESPONSE_RESET_IN_PROGRESS:
|
|
|
+ case AP_RESPONSE_BUSY:
|
|
|
+ case AP_RESPONSE_OTHERWISE_CHANGED:
|
|
|
+ default:
|
|
|
+ return -EBUSY;
|
|
|
}
|
|
|
- return -EBUSY;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -350,47 +341,25 @@ static int ap_query_functions(ap_qid_t qid, unsigned int *functions)
|
|
|
* on the return value it waits a while and tests the AP queue if interrupts
|
|
|
* have been switched on using ap_test_queue().
|
|
|
*/
|
|
|
-static int ap_queue_enable_interruption(ap_qid_t qid, void *ind)
|
|
|
+static int ap_queue_enable_interruption(struct ap_device *ap_dev, void *ind)
|
|
|
{
|
|
|
struct ap_queue_status status;
|
|
|
- int t_depth, t_device_type, rc, i;
|
|
|
|
|
|
- rc = -EBUSY;
|
|
|
- status = ap_queue_interruption_control(qid, ind);
|
|
|
-
|
|
|
- for (i = 0; i < AP_MAX_RESET; i++) {
|
|
|
- switch (status.response_code) {
|
|
|
- case AP_RESPONSE_NORMAL:
|
|
|
- if (status.int_enabled)
|
|
|
- return 0;
|
|
|
- break;
|
|
|
- case AP_RESPONSE_RESET_IN_PROGRESS:
|
|
|
- case AP_RESPONSE_BUSY:
|
|
|
- if (i < AP_MAX_RESET - 1) {
|
|
|
- udelay(5);
|
|
|
- status = ap_queue_interruption_control(qid,
|
|
|
- ind);
|
|
|
- continue;
|
|
|
- }
|
|
|
- break;
|
|
|
- case AP_RESPONSE_Q_NOT_AVAIL:
|
|
|
- case AP_RESPONSE_DECONFIGURED:
|
|
|
- case AP_RESPONSE_CHECKSTOPPED:
|
|
|
- case AP_RESPONSE_INVALID_ADDRESS:
|
|
|
- return -ENODEV;
|
|
|
- case AP_RESPONSE_OTHERWISE_CHANGED:
|
|
|
- if (status.int_enabled)
|
|
|
- return 0;
|
|
|
- break;
|
|
|
- default:
|
|
|
- break;
|
|
|
- }
|
|
|
- if (i < AP_MAX_RESET - 1) {
|
|
|
- udelay(5);
|
|
|
- status = ap_test_queue(qid, &t_depth, &t_device_type);
|
|
|
- }
|
|
|
+ status = ap_queue_interruption_control(ap_dev->qid, ind);
|
|
|
+ switch (status.response_code) {
|
|
|
+ case AP_RESPONSE_NORMAL:
|
|
|
+ case AP_RESPONSE_OTHERWISE_CHANGED:
|
|
|
+ return 0;
|
|
|
+ case AP_RESPONSE_Q_NOT_AVAIL:
|
|
|
+ case AP_RESPONSE_DECONFIGURED:
|
|
|
+ case AP_RESPONSE_CHECKSTOPPED:
|
|
|
+ case AP_RESPONSE_INVALID_ADDRESS:
|
|
|
+ return -ENODEV;
|
|
|
+ case AP_RESPONSE_RESET_IN_PROGRESS:
|
|
|
+ case AP_RESPONSE_BUSY:
|
|
|
+ default:
|
|
|
+ return -EBUSY;
|
|
|
}
|
|
|
- return rc;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -510,110 +479,95 @@ int ap_recv(ap_qid_t qid, unsigned long long *psmid, void *msg, size_t length)
|
|
|
}
|
|
|
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
|
|
|
* @device_type: Pointer to device type value
|
|
|
- *
|
|
|
- * The test is repeated for AP_MAX_RESET times.
|
|
|
*/
|
|
|
static int ap_query_queue(ap_qid_t qid, int *queue_depth, int *device_type)
|
|
|
{
|
|
|
struct ap_queue_status status;
|
|
|
- int t_depth, t_device_type, rc, i;
|
|
|
+ int t_depth, t_device_type;
|
|
|
|
|
|
- rc = -EBUSY;
|
|
|
- for (i = 0; i < AP_MAX_RESET; i++) {
|
|
|
- status = ap_test_queue(qid, &t_depth, &t_device_type);
|
|
|
- switch (status.response_code) {
|
|
|
- case AP_RESPONSE_NORMAL:
|
|
|
- *queue_depth = t_depth + 1;
|
|
|
- *device_type = t_device_type;
|
|
|
- rc = 0;
|
|
|
- break;
|
|
|
- case AP_RESPONSE_Q_NOT_AVAIL:
|
|
|
- rc = -ENODEV;
|
|
|
- break;
|
|
|
- case AP_RESPONSE_RESET_IN_PROGRESS:
|
|
|
- break;
|
|
|
- case AP_RESPONSE_DECONFIGURED:
|
|
|
- rc = -ENODEV;
|
|
|
- break;
|
|
|
- case AP_RESPONSE_CHECKSTOPPED:
|
|
|
- rc = -ENODEV;
|
|
|
- break;
|
|
|
- case AP_RESPONSE_INVALID_ADDRESS:
|
|
|
- rc = -ENODEV;
|
|
|
- break;
|
|
|
- case AP_RESPONSE_OTHERWISE_CHANGED:
|
|
|
- break;
|
|
|
- case AP_RESPONSE_BUSY:
|
|
|
- break;
|
|
|
- default:
|
|
|
- BUG();
|
|
|
- }
|
|
|
- if (rc != -EBUSY)
|
|
|
- break;
|
|
|
- if (i < AP_MAX_RESET - 1)
|
|
|
- udelay(5);
|
|
|
+ status = ap_test_queue(qid, &t_depth, &t_device_type);
|
|
|
+ switch (status.response_code) {
|
|
|
+ case AP_RESPONSE_NORMAL:
|
|
|
+ *queue_depth = t_depth + 1;
|
|
|
+ *device_type = t_device_type;
|
|
|
+ return 0;
|
|
|
+ case AP_RESPONSE_Q_NOT_AVAIL:
|
|
|
+ case AP_RESPONSE_DECONFIGURED:
|
|
|
+ case AP_RESPONSE_CHECKSTOPPED:
|
|
|
+ case AP_RESPONSE_INVALID_ADDRESS:
|
|
|
+ return -ENODEV;
|
|
|
+ case AP_RESPONSE_RESET_IN_PROGRESS:
|
|
|
+ case AP_RESPONSE_OTHERWISE_CHANGED:
|
|
|
+ case AP_RESPONSE_BUSY:
|
|
|
+ return -EBUSY;
|
|
|
+ default:
|
|
|
+ BUG();
|
|
|
}
|
|
|
- return rc;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* ap_init_queue(): Reset an AP queue.
|
|
|
* @qid: The AP queue number
|
|
|
*
|
|
|
- * Reset an AP queue and wait for it to become available again.
|
|
|
+ * 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(ap_qid_t qid)
|
|
|
+static int ap_init_queue(struct ap_device *ap_dev)
|
|
|
{
|
|
|
struct ap_queue_status status;
|
|
|
- int rc, dummy, i;
|
|
|
|
|
|
- rc = -ENODEV;
|
|
|
- status = ap_reset_queue(qid);
|
|
|
- for (i = 0; i < AP_MAX_RESET; i++) {
|
|
|
- switch (status.response_code) {
|
|
|
- case AP_RESPONSE_NORMAL:
|
|
|
- if (status.queue_empty)
|
|
|
- rc = 0;
|
|
|
- break;
|
|
|
- case AP_RESPONSE_Q_NOT_AVAIL:
|
|
|
- case AP_RESPONSE_DECONFIGURED:
|
|
|
- case AP_RESPONSE_CHECKSTOPPED:
|
|
|
- i = AP_MAX_RESET; /* return with -ENODEV */
|
|
|
- break;
|
|
|
- case AP_RESPONSE_RESET_IN_PROGRESS:
|
|
|
- rc = -EBUSY;
|
|
|
- case AP_RESPONSE_BUSY:
|
|
|
- default:
|
|
|
- break;
|
|
|
- }
|
|
|
- if (rc != -ENODEV && rc != -EBUSY)
|
|
|
- break;
|
|
|
- if (i < AP_MAX_RESET - 1) {
|
|
|
- /* Time we are waiting until we give up (0.7sec * 90).
|
|
|
- * Since the actual request (in progress) will not
|
|
|
- * interrupted immediately for the reset command,
|
|
|
- * we have to be patient. In worst case we have to
|
|
|
- * wait 60sec + reset time (some msec).
|
|
|
- */
|
|
|
- schedule_timeout(AP_RESET_TIMEOUT);
|
|
|
- status = ap_test_queue(qid, &dummy, &dummy);
|
|
|
- }
|
|
|
- }
|
|
|
- if (rc == 0 && ap_using_interrupts()) {
|
|
|
- rc = ap_queue_enable_interruption(qid, ap_airq.lsi_ptr);
|
|
|
- /* If interruption mode is supported by the machine,
|
|
|
- * but an AP can not be enabled for interruption then
|
|
|
- * the AP will be discarded. */
|
|
|
- if (rc)
|
|
|
- pr_err("Registering adapter interrupts for "
|
|
|
- "AP %d failed\n", AP_QID_DEVICE(qid));
|
|
|
+ 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:
|
|
|
+ case AP_RESPONSE_BUSY:
|
|
|
+ return -EBUSY;
|
|
|
+ case AP_RESPONSE_Q_NOT_AVAIL:
|
|
|
+ case AP_RESPONSE_DECONFIGURED:
|
|
|
+ case AP_RESPONSE_CHECKSTOPPED:
|
|
|
+ default:
|
|
|
+ return -ENODEV;
|
|
|
}
|
|
|
- return rc;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -729,10 +683,63 @@ static ssize_t ap_pendingq_count_show(struct device *dev,
|
|
|
|
|
|
static DEVICE_ATTR(pendingq_count, 0444, ap_pendingq_count_show, NULL);
|
|
|
|
|
|
+static ssize_t ap_reset_show(struct device *dev,
|
|
|
+ struct device_attribute *attr, char *buf)
|
|
|
+{
|
|
|
+ struct ap_device *ap_dev = to_ap_dev(dev);
|
|
|
+ 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:
|
|
|
+ rc = snprintf(buf, PAGE_SIZE, "Reset in progress.\n");
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ spin_unlock_bh(&ap_dev->lock);
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+static DEVICE_ATTR(reset, 0444, ap_reset_show, NULL);
|
|
|
+
|
|
|
+static ssize_t ap_interrupt_show(struct device *dev,
|
|
|
+ struct device_attribute *attr, char *buf)
|
|
|
+{
|
|
|
+ struct ap_device *ap_dev = to_ap_dev(dev);
|
|
|
+ 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:
|
|
|
+ rc = snprintf(buf, PAGE_SIZE, "Enable Interrupt pending.\n");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ spin_unlock_bh(&ap_dev->lock);
|
|
|
+ return rc;
|
|
|
+}
|
|
|
+
|
|
|
+static DEVICE_ATTR(interrupt, 0444, ap_interrupt_show, NULL);
|
|
|
+
|
|
|
static ssize_t ap_modalias_show(struct device *dev,
|
|
|
struct device_attribute *attr, char *buf)
|
|
|
{
|
|
|
- return sprintf(buf, "ap:t%02X", to_ap_dev(dev)->device_type);
|
|
|
+ return sprintf(buf, "ap:t%02X\n", to_ap_dev(dev)->device_type);
|
|
|
}
|
|
|
|
|
|
static DEVICE_ATTR(modalias, 0444, ap_modalias_show, NULL);
|
|
@@ -753,6 +760,8 @@ static struct attribute *ap_dev_attrs[] = {
|
|
|
&dev_attr_request_count.attr,
|
|
|
&dev_attr_requestq_count.attr,
|
|
|
&dev_attr_pendingq_count.attr,
|
|
|
+ &dev_attr_reset.attr,
|
|
|
+ &dev_attr_interrupt.attr,
|
|
|
&dev_attr_modalias.attr,
|
|
|
&dev_attr_ap_functions.attr,
|
|
|
NULL
|
|
@@ -926,6 +935,10 @@ static int ap_device_probe(struct device *dev)
|
|
|
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();
|
|
|
}
|
|
|
return rc;
|
|
|
}
|
|
@@ -1411,7 +1424,7 @@ static void ap_scan_bus(struct work_struct *unused)
|
|
|
struct ap_device *ap_dev;
|
|
|
struct device *dev;
|
|
|
ap_qid_t qid;
|
|
|
- int queue_depth, device_type;
|
|
|
+ int queue_depth = 0, device_type = 0;
|
|
|
unsigned int device_functions;
|
|
|
int rc, i;
|
|
|
|
|
@@ -1429,15 +1442,9 @@ static void ap_scan_bus(struct work_struct *unused)
|
|
|
else
|
|
|
rc = -ENODEV;
|
|
|
if (dev) {
|
|
|
- if (rc == -EBUSY) {
|
|
|
- set_current_state(TASK_UNINTERRUPTIBLE);
|
|
|
- schedule_timeout(AP_RESET_TIMEOUT);
|
|
|
- rc = ap_query_queue(qid, &queue_depth,
|
|
|
- &device_type);
|
|
|
- }
|
|
|
ap_dev = to_ap_dev(dev);
|
|
|
spin_lock_bh(&ap_dev->lock);
|
|
|
- if (rc || ap_dev->unregistered) {
|
|
|
+ if (rc == -ENODEV || ap_dev->unregistered) {
|
|
|
spin_unlock_bh(&ap_dev->lock);
|
|
|
if (ap_dev->unregistered)
|
|
|
i--;
|
|
@@ -1449,15 +1456,17 @@ static void ap_scan_bus(struct work_struct *unused)
|
|
|
put_device(dev);
|
|
|
continue;
|
|
|
}
|
|
|
- if (rc)
|
|
|
- continue;
|
|
|
- rc = ap_init_queue(qid);
|
|
|
if (rc)
|
|
|
continue;
|
|
|
ap_dev = kzalloc(sizeof(*ap_dev), GFP_KERNEL);
|
|
|
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->queue_depth = queue_depth;
|
|
|
ap_dev->unregistered = 1;
|
|
|
spin_lock_init(&ap_dev->lock);
|
|
@@ -1519,36 +1528,6 @@ ap_config_timeout(unsigned long ptr)
|
|
|
add_timer(&ap_config_timer);
|
|
|
}
|
|
|
|
|
|
-/**
|
|
|
- * __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_poll_read(): Receive pending reply messages from an AP device.
|
|
|
* @ap_dev: pointer to the AP device
|
|
@@ -1568,6 +1547,7 @@ static int ap_poll_read(struct ap_device *ap_dev, unsigned long *flags)
|
|
|
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) {
|
|
@@ -1582,6 +1562,7 @@ static int ap_poll_read(struct ap_device *ap_dev, unsigned long *flags)
|
|
|
*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);
|
|
@@ -1612,7 +1593,8 @@ static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags)
|
|
|
struct ap_message *ap_msg;
|
|
|
|
|
|
if (ap_dev->requestq_count <= 0 ||
|
|
|
- ap_dev->queue_count >= ap_dev->queue_depth)
|
|
|
+ (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);
|
|
@@ -1646,6 +1628,8 @@ static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags)
|
|
|
|
|
|
/**
|
|
|
* 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
|
|
@@ -1656,7 +1640,51 @@ static int ap_poll_write(struct ap_device *ap_dev, unsigned long *flags)
|
|
|
*/
|
|
|
static inline int ap_poll_queue(struct ap_device *ap_dev, unsigned long *flags)
|
|
|
{
|
|
|
- int rc;
|
|
|
+ int rc, depth, type;
|
|
|
+ struct ap_queue_status status;
|
|
|
+
|
|
|
+
|
|
|
+ if (ap_dev->reset == AP_RESET_IN_PROGRESS) {
|
|
|
+ status = ap_test_queue(ap_dev->qid, &depth, &type);
|
|
|
+ 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, &depth, &type);
|
|
|
+ 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)
|
|
@@ -1676,7 +1704,8 @@ static int __ap_queue_message(struct ap_device *ap_dev, struct ap_message *ap_ms
|
|
|
struct ap_queue_status status;
|
|
|
|
|
|
if (list_empty(&ap_dev->requestq) &&
|
|
|
- ap_dev->queue_count < ap_dev->queue_depth) {
|
|
|
+ (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);
|
|
@@ -1789,21 +1818,20 @@ static enum hrtimer_restart ap_poll_timeout(struct hrtimer *unused)
|
|
|
* 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)
|
|
|
+static void ap_reset(struct ap_device *ap_dev, unsigned long *flags)
|
|
|
{
|
|
|
int rc;
|
|
|
|
|
|
- ap_dev->reset = AP_RESET_IGNORE;
|
|
|
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->qid);
|
|
|
+ rc = ap_init_queue(ap_dev);
|
|
|
if (rc == -ENODEV)
|
|
|
ap_dev->unregistered = 1;
|
|
|
else
|
|
|
- __ap_schedule_poll_timer();
|
|
|
+ *flags |= AP_POLL_AFTER_TIMEOUT;
|
|
|
}
|
|
|
|
|
|
static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags)
|
|
@@ -1812,7 +1840,7 @@ static int __ap_poll_device(struct ap_device *ap_dev, unsigned long *flags)
|
|
|
if (ap_poll_queue(ap_dev, flags))
|
|
|
ap_dev->unregistered = 1;
|
|
|
if (ap_dev->reset == AP_RESET_DO)
|
|
|
- ap_reset(ap_dev);
|
|
|
+ ap_reset(ap_dev, flags);
|
|
|
}
|
|
|
return 0;
|
|
|
}
|
|
@@ -1845,9 +1873,9 @@ static void ap_poll_all(unsigned long dummy)
|
|
|
spin_unlock(&ap_dev->lock);
|
|
|
}
|
|
|
spin_unlock(&ap_device_list_lock);
|
|
|
- } while (flags & 1);
|
|
|
- if (flags & 2)
|
|
|
- ap_schedule_poll_timer();
|
|
|
+ } while (flags & AP_POLL_IMMEDIATELY);
|
|
|
+ if (flags & AP_POLL_AFTER_TIMEOUT)
|
|
|
+ __ap_schedule_poll_timer();
|
|
|
}
|
|
|
|
|
|
/**
|