ACPI, APEI, Manage GHES as platform devices

Register GHES during HEST initialization as platform devices. And make
GHES driver into platform device driver. So that the GHES driver
module can be loaded automatically when there are GHES available.

Signed-off-by: Huang Ying <ying.huang@intel.com>
Signed-off-by: Andi Kleen <ak@linux.intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index c76aff6..385a605 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -41,6 +41,8 @@
 #include <linux/interrupt.h>
 #include <linux/cper.h>
 #include <linux/kdebug.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
 #include <acpi/apei.h>
 #include <acpi/atomicio.h>
 #include <acpi/hed.h>
@@ -87,6 +89,7 @@
  * used for that.
  */
 static LIST_HEAD(ghes_sci);
+static DEFINE_MUTEX(ghes_list_mutex);
 
 static struct ghes *ghes_new(struct acpi_hest_generic *generic)
 {
@@ -293,18 +296,15 @@
 	.notifier_call = ghes_notify_sci,
 };
 
-static int hest_ghes_parse(struct acpi_hest_header *hest_hdr, void *data)
+static int __devinit ghes_probe(struct platform_device *ghes_dev)
 {
 	struct acpi_hest_generic *generic;
 	struct ghes *ghes = NULL;
-	int rc = 0;
+	int rc = -EINVAL;
 
-	if (hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR)
-		return 0;
-
-	generic = (struct acpi_hest_generic *)hest_hdr;
+	generic = ghes_dev->dev.platform_data;
 	if (!generic->enabled)
-		return 0;
+		return -ENODEV;
 
 	if (generic->error_block_length <
 	    sizeof(struct acpi_hest_generic_status)) {
@@ -327,62 +327,91 @@
 		ghes = NULL;
 		goto err;
 	}
-	switch (generic->notify.type) {
-	case ACPI_HEST_NOTIFY_POLLED:
-		pr_warning(GHES_PFX
-"Generic hardware error source: %d notified via POLL is not supported!\n",
-			   generic->header.source_id);
-		break;
-	case ACPI_HEST_NOTIFY_EXTERNAL:
-	case ACPI_HEST_NOTIFY_LOCAL:
-		pr_warning(GHES_PFX
-"Generic hardware error source: %d notified via IRQ is not supported!\n",
-			   generic->header.source_id);
-		break;
-	case ACPI_HEST_NOTIFY_SCI:
+	if (generic->notify.type == ACPI_HEST_NOTIFY_SCI) {
+		mutex_lock(&ghes_list_mutex);
 		if (list_empty(&ghes_sci))
 			register_acpi_hed_notifier(&ghes_notifier_sci);
 		list_add_rcu(&ghes->list, &ghes_sci);
-		break;
-	case ACPI_HEST_NOTIFY_NMI:
-		pr_warning(GHES_PFX
-"Generic hardware error source: %d notified via NMI is not supported!\n",
-			   generic->header.source_id);
-		break;
-	default:
-		pr_warning(FW_WARN GHES_PFX
-	"Unknown notification type: %u for generic hardware error source: %d\n",
-			   generic->notify.type, generic->header.source_id);
-		break;
+		mutex_unlock(&ghes_list_mutex);
+	} else {
+		unsigned char *notify = NULL;
+
+		switch (generic->notify.type) {
+		case ACPI_HEST_NOTIFY_POLLED:
+			notify = "POLL";
+			break;
+		case ACPI_HEST_NOTIFY_EXTERNAL:
+		case ACPI_HEST_NOTIFY_LOCAL:
+			notify = "IRQ";
+			break;
+		case ACPI_HEST_NOTIFY_NMI:
+			notify = "NMI";
+			break;
+		}
+		if (notify) {
+			pr_warning(GHES_PFX
+"Generic hardware error source: %d notified via %s is not supported!\n",
+				   generic->header.source_id, notify);
+		} else {
+			pr_warning(FW_WARN GHES_PFX
+"Unknown notification type: %u for generic hardware error source: %d\n",
+			generic->notify.type, generic->header.source_id);
+		}
+		rc = -ENODEV;
+		goto err;
 	}
+	platform_set_drvdata(ghes_dev, ghes);
 
 	return 0;
 err:
-	if (ghes)
-		ghes_fini(ghes);
-	return rc;
-}
-
-static void ghes_cleanup(void)
-{
-	struct ghes *ghes, *nghes;
-
-	if (!list_empty(&ghes_sci))
-		unregister_acpi_hed_notifier(&ghes_notifier_sci);
-
-	synchronize_rcu();
-
-	list_for_each_entry_safe(ghes, nghes, &ghes_sci, list) {
-		list_del(&ghes->list);
+	if (ghes) {
 		ghes_fini(ghes);
 		kfree(ghes);
 	}
+	return rc;
 }
 
+static int __devexit ghes_remove(struct platform_device *ghes_dev)
+{
+	struct ghes *ghes;
+	struct acpi_hest_generic *generic;
+
+	ghes = platform_get_drvdata(ghes_dev);
+	generic = ghes->generic;
+
+	switch (generic->notify.type) {
+	case ACPI_HEST_NOTIFY_SCI:
+		mutex_lock(&ghes_list_mutex);
+		list_del_rcu(&ghes->list);
+		if (list_empty(&ghes_sci))
+			unregister_acpi_hed_notifier(&ghes_notifier_sci);
+		mutex_unlock(&ghes_list_mutex);
+		break;
+	default:
+		BUG();
+		break;
+	}
+
+	synchronize_rcu();
+	ghes_fini(ghes);
+	kfree(ghes);
+
+	platform_set_drvdata(ghes_dev, NULL);
+
+	return 0;
+}
+
+static struct platform_driver ghes_platform_driver = {
+	.driver		= {
+		.name	= "GHES",
+		.owner	= THIS_MODULE,
+	},
+	.probe		= ghes_probe,
+	.remove		= ghes_remove,
+};
+
 static int __init ghes_init(void)
 {
-	int rc;
-
 	if (acpi_disabled)
 		return -ENODEV;
 
@@ -391,32 +420,12 @@
 		return -EINVAL;
 	}
 
-	rc = apei_hest_parse(hest_ghes_parse, NULL);
-	if (rc) {
-		pr_err(GHES_PFX
-		"Error during parsing HEST generic hardware error sources.\n");
-		goto err_cleanup;
-	}
-
-	if (list_empty(&ghes_sci)) {
-		pr_info(GHES_PFX
-			"No functional generic hardware error sources.\n");
-		rc = -ENODEV;
-		goto err_cleanup;
-	}
-
-	pr_info(GHES_PFX
-		"Generic Hardware Error Source support is initialized.\n");
-
-	return 0;
-err_cleanup:
-	ghes_cleanup();
-	return rc;
+	return platform_driver_register(&ghes_platform_driver);
 }
 
 static void __exit ghes_exit(void)
 {
-	ghes_cleanup();
+	platform_driver_unregister(&ghes_platform_driver);
 }
 
 module_init(ghes_init);
@@ -425,3 +434,4 @@
 MODULE_AUTHOR("Huang Ying");
 MODULE_DESCRIPTION("APEI Generic Hardware Error Source support");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:GHES");