Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/linville/wirel...
[firefly-linux-kernel-4.4.55.git] / drivers / s390 / crypto / ap_bus.c
index ae258a4b4e5ea8866333fec3204dba7513fbda5c..b8b340ac53321f1028366d1a0cd18446050ed034 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright IBM Corp. 2006
+ * Copyright IBM Corp. 2006, 2012
  * Author(s): Cornelia Huck <cornelia.huck@de.ibm.com>
  *           Martin Schwidefsky <schwidefsky@de.ibm.com>
  *           Ralph Wuerthner <rwuerthn@de.ibm.com>
@@ -62,13 +62,14 @@ 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);
 static int ap_select_domain(void);
+static void ap_query_configuration(void);
 
 /*
  * Module description.
  */
 MODULE_AUTHOR("IBM Corporation");
-MODULE_DESCRIPTION("Adjunct Processor Bus driver, "
-                  "Copyright IBM Corp. 2006");
+MODULE_DESCRIPTION("Adjunct Processor Bus driver, " \
+                  "Copyright IBM Corp. 2006, 2012");
 MODULE_LICENSE("GPL");
 
 /*
@@ -84,6 +85,7 @@ module_param_named(poll_thread, ap_thread_flag, int, 0000);
 MODULE_PARM_DESC(poll_thread, "Turn on/off poll thread, default is 0 (off).");
 
 static struct device *ap_root_device = NULL;
+static struct ap_config_info *ap_configuration;
 static DEFINE_SPINLOCK(ap_device_list_lock);
 static LIST_HEAD(ap_device_list);
 
@@ -157,6 +159,19 @@ static int ap_interrupts_available(void)
        return test_facility(2) && test_facility(65);
 }
 
+/**
+ * ap_configuration_available(): Test if AP configuration
+ * information is available.
+ *
+ * Returns 1 if AP configuration information is available.
+ */
+#ifdef CONFIG_64BIT
+static int ap_configuration_available(void)
+{
+       return test_facility(2) && test_facility(12);
+}
+#endif
+
 /**
  * ap_test_queue(): Test adjunct processor queue.
  * @qid: The AP queue number
@@ -242,6 +257,26 @@ __ap_query_functions(ap_qid_t qid, unsigned int *functions)
 }
 #endif
 
+#ifdef CONFIG_64BIT
+static inline int __ap_query_configuration(struct ap_config_info *config)
+{
+       register unsigned long reg0 asm ("0") = 0x04000000UL;
+       register unsigned long reg1 asm ("1") = -EINVAL;
+       register unsigned char *reg2 asm ("2") = (unsigned char *)config;
+
+       asm volatile(
+               ".long 0xb2af0000\n"            /* PQAP(QCI) */
+               "0: la    %1,0\n"
+               "1:\n"
+               EX_TABLE(0b, 1b)
+               : "+d" (reg0), "+d" (reg1), "+d" (reg2)
+               :
+               : "cc");
+
+       return reg1;
+}
+#endif
+
 /**
  * ap_query_functions(): Query supported functions.
  * @qid: The AP queue number
@@ -291,25 +326,6 @@ static int ap_query_functions(ap_qid_t qid, unsigned int *functions)
 #endif
 }
 
-/**
- * ap_4096_commands_availablen(): Check for availability of 4096 bit RSA
- * support.
- * @qid: The AP queue number
- *
- * Returns 1 if 4096 bit RSA keys are support fo the AP, returns 0 if not.
- */
-int ap_4096_commands_available(ap_qid_t qid)
-{
-       unsigned int functions;
-
-       if (ap_query_functions(qid, &functions))
-               return 0;
-
-       return test_ap_facility(functions, 1) &&
-              test_ap_facility(functions, 2);
-}
-EXPORT_SYMBOL(ap_4096_commands_available);
-
 /**
  * ap_queue_enable_interruption(): Enable interruption on an AP.
  * @qid: The AP queue number
@@ -657,6 +673,34 @@ static ssize_t ap_request_count_show(struct device *dev,
 
 static DEVICE_ATTR(request_count, 0444, ap_request_count_show, NULL);
 
+static ssize_t ap_requestq_count_show(struct device *dev,
+                                     struct device_attribute *attr, char *buf)
+{
+       struct ap_device *ap_dev = to_ap_dev(dev);
+       int rc;
+
+       spin_lock_bh(&ap_dev->lock);
+       rc = snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->requestq_count);
+       spin_unlock_bh(&ap_dev->lock);
+       return rc;
+}
+
+static DEVICE_ATTR(requestq_count, 0444, ap_requestq_count_show, NULL);
+
+static ssize_t ap_pendingq_count_show(struct device *dev,
+                                     struct device_attribute *attr, char *buf)
+{
+       struct ap_device *ap_dev = to_ap_dev(dev);
+       int rc;
+
+       spin_lock_bh(&ap_dev->lock);
+       rc = snprintf(buf, PAGE_SIZE, "%d\n", ap_dev->pendingq_count);
+       spin_unlock_bh(&ap_dev->lock);
+       return rc;
+}
+
+static DEVICE_ATTR(pendingq_count, 0444, ap_pendingq_count_show, NULL);
+
 static ssize_t ap_modalias_show(struct device *dev,
                                struct device_attribute *attr, char *buf)
 {
@@ -665,11 +709,23 @@ static ssize_t ap_modalias_show(struct device *dev,
 
 static DEVICE_ATTR(modalias, 0444, ap_modalias_show, NULL);
 
+static ssize_t ap_functions_show(struct device *dev,
+                                struct device_attribute *attr, char *buf)
+{
+       struct ap_device *ap_dev = to_ap_dev(dev);
+       return snprintf(buf, PAGE_SIZE, "0x%08X\n", ap_dev->functions);
+}
+
+static DEVICE_ATTR(ap_functions, 0444, ap_functions_show, NULL);
+
 static struct attribute *ap_dev_attrs[] = {
        &dev_attr_hwtype.attr,
        &dev_attr_depth.attr,
        &dev_attr_request_count.attr,
+       &dev_attr_requestq_count.attr,
+       &dev_attr_pendingq_count.attr,
        &dev_attr_modalias.attr,
+       &dev_attr_ap_functions.attr,
        NULL
 };
 static struct attribute_group ap_dev_attr_group = {
@@ -772,6 +828,7 @@ static int ap_bus_resume(struct device *dev)
                ap_suspend_flag = 0;
                if (!ap_interrupts_available())
                        ap_interrupt_indicator = NULL;
+               ap_query_configuration();
                if (!user_set_domain) {
                        ap_domain_index = -1;
                        ap_select_domain();
@@ -895,6 +952,20 @@ void ap_driver_unregister(struct ap_driver *ap_drv)
 }
 EXPORT_SYMBOL(ap_driver_unregister);
 
+void ap_bus_force_rescan(void)
+{
+       /* Delete the AP bus rescan timer. */
+       del_timer(&ap_config_timer);
+
+       /* processing a synchonuous bus rescan */
+       ap_scan_bus(NULL);
+
+       /* Setup the AP bus rescan timer again. */
+       ap_config_timer.expires = jiffies + ap_config_time * HZ;
+       add_timer(&ap_config_timer);
+}
+EXPORT_SYMBOL(ap_bus_force_rescan);
+
 /*
  * AP bus attributes.
  */
@@ -997,6 +1068,65 @@ static struct bus_attribute *const ap_bus_attrs[] = {
        NULL,
 };
 
+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)
+               return 1;
+       return ap_test_config(ap_configuration->aqm, domain);
+}
+
+/**
+ * ap_query_configuration(): Query AP configuration information.
+ *
+ * Query information of installed cards and configured domains from AP.
+ */
+static void ap_query_configuration(void)
+{
+#ifdef CONFIG_64BIT
+       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;
+#else
+       ap_configuration = NULL;
+#endif
+}
+
 /**
  * ap_select_domain(): Select an AP domain.
  *
@@ -1005,6 +1135,7 @@ static struct bus_attribute *const ap_bus_attrs[] = {
 static int ap_select_domain(void)
 {
        int queue_depth, device_type, count, max_count, best_domain;
+       ap_qid_t qid;
        int rc, i, j;
 
        /*
@@ -1018,9 +1149,13 @@ static int ap_select_domain(void)
        best_domain = -1;
        max_count = 0;
        for (i = 0; i < AP_DOMAINS; i++) {
+               if (!ap_test_config_domain(i))
+                       continue;
                count = 0;
                for (j = 0; j < AP_DEVICES; j++) {
-                       ap_qid_t qid = AP_MKQID(j, i);
+                       if (!ap_test_config_card_id(j))
+                               continue;
+                       qid = AP_MKQID(j, i);
                        rc = ap_query_queue(qid, &queue_depth, &device_type);
                        if (rc)
                                continue;
@@ -1137,7 +1272,7 @@ out:
 
 static void ap_interrupt_handler(void *unused1, void *unused2)
 {
-       kstat_cpu(smp_processor_id()).irqs[IOINT_APB]++;
+       inc_irq_stat(IRQIO_APB);
        tasklet_schedule(&ap_tasklet);
 }
 
@@ -1169,6 +1304,7 @@ static void ap_scan_bus(struct work_struct *unused)
        unsigned int device_functions;
        int rc, i;
 
+       ap_query_configuration();
        if (ap_select_domain() != 0)
                return;
        for (i = 0; i < AP_DEVICES; i++) {
@@ -1176,7 +1312,10 @@ static void ap_scan_bus(struct work_struct *unused)
                dev = bus_find_device(&ap_bus_type, NULL,
                                      (void *)(unsigned long)qid,
                                      __ap_scan_bus);
-               rc = ap_query_queue(qid, &queue_depth, &device_type);
+               if (ap_test_config_card_id(i))
+                       rc = ap_query_queue(qid, &queue_depth, &device_type);
+               else
+                       rc = -ENODEV;
                if (dev) {
                        if (rc == -EBUSY) {
                                set_current_state(TASK_UNINTERRUPTIBLE);
@@ -1217,29 +1356,22 @@ static void ap_scan_bus(struct work_struct *unused)
                            (unsigned long) ap_dev);
                switch (device_type) {
                case 0:
+                       /* device type probing for old cards */
                        if (ap_probe_device_type(ap_dev)) {
                                kfree(ap_dev);
                                continue;
                        }
                        break;
-               case 10:
-                       if (ap_query_functions(qid, &device_functions)) {
-                               kfree(ap_dev);
-                               continue;
-                       }
-                       if (test_ap_facility(device_functions, 3))
-                               ap_dev->device_type = AP_DEVICE_TYPE_CEX3C;
-                       else if (test_ap_facility(device_functions, 4))
-                               ap_dev->device_type = AP_DEVICE_TYPE_CEX3A;
-                       else {
-                               kfree(ap_dev);
-                               continue;
-                       }
-                       break;
                default:
                        ap_dev->device_type = device_type;
                }
 
+               rc = ap_query_functions(qid, &device_functions);
+               if (!rc)
+                       ap_dev->functions = device_functions;
+               else
+                       ap_dev->functions = 0u;
+
                ap_dev->device.bus = &ap_bus_type;
                ap_dev->device.parent = ap_root_device;
                if (dev_set_name(&ap_dev->device, "card%02x",
@@ -1785,6 +1917,7 @@ int __init ap_module_init(void)
                goto out_root;
        }
 
+       ap_query_configuration();
        if (ap_select_domain() == 0)
                ap_scan_bus(NULL);