aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/apei/ghes.c
diff options
context:
space:
mode:
authorHuang Ying <ying.huang@intel.com>2010-08-02 03:48:24 -0400
committerLen Brown <len.brown@intel.com>2010-08-08 14:55:52 -0400
commit7ad6e9435596f692ff65f399da12816c94960185 (patch)
tree85786993b961b240f2c95fa320b5eeff859dae2c /drivers/acpi/apei/ghes.c
parentad4ecef2f13c790f95b55320f2925c205d8f971f (diff)
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>
Diffstat (limited to 'drivers/acpi/apei/ghes.c')
-rw-r--r--drivers/acpi/apei/ghes.c140
1 files changed, 75 insertions, 65 deletions
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c
index c76aff6c861d..385a6059714a 100644
--- a/drivers/acpi/apei/ghes.c
+++ b/drivers/acpi/apei/ghes.c
@@ -41,6 +41,8 @@
41#include <linux/interrupt.h> 41#include <linux/interrupt.h>
42#include <linux/cper.h> 42#include <linux/cper.h>
43#include <linux/kdebug.h> 43#include <linux/kdebug.h>
44#include <linux/platform_device.h>
45#include <linux/mutex.h>
44#include <acpi/apei.h> 46#include <acpi/apei.h>
45#include <acpi/atomicio.h> 47#include <acpi/atomicio.h>
46#include <acpi/hed.h> 48#include <acpi/hed.h>
@@ -87,6 +89,7 @@ struct ghes {
87 * used for that. 89 * used for that.
88 */ 90 */
89static LIST_HEAD(ghes_sci); 91static LIST_HEAD(ghes_sci);
92static DEFINE_MUTEX(ghes_list_mutex);
90 93
91static struct ghes *ghes_new(struct acpi_hest_generic *generic) 94static struct ghes *ghes_new(struct acpi_hest_generic *generic)
92{ 95{
@@ -293,18 +296,15 @@ static struct notifier_block ghes_notifier_sci = {
293 .notifier_call = ghes_notify_sci, 296 .notifier_call = ghes_notify_sci,
294}; 297};
295 298
296static int hest_ghes_parse(struct acpi_hest_header *hest_hdr, void *data) 299static int __devinit ghes_probe(struct platform_device *ghes_dev)
297{ 300{
298 struct acpi_hest_generic *generic; 301 struct acpi_hest_generic *generic;
299 struct ghes *ghes = NULL; 302 struct ghes *ghes = NULL;
300 int rc = 0; 303 int rc = -EINVAL;
301 304
302 if (hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR) 305 generic = ghes_dev->dev.platform_data;
303 return 0;
304
305 generic = (struct acpi_hest_generic *)hest_hdr;
306 if (!generic->enabled) 306 if (!generic->enabled)
307 return 0; 307 return -ENODEV;
308 308
309 if (generic->error_block_length < 309 if (generic->error_block_length <
310 sizeof(struct acpi_hest_generic_status)) { 310 sizeof(struct acpi_hest_generic_status)) {
@@ -327,62 +327,91 @@ static int hest_ghes_parse(struct acpi_hest_header *hest_hdr, void *data)
327 ghes = NULL; 327 ghes = NULL;
328 goto err; 328 goto err;
329 } 329 }
330 switch (generic->notify.type) { 330 if (generic->notify.type == ACPI_HEST_NOTIFY_SCI) {
331 case ACPI_HEST_NOTIFY_POLLED: 331 mutex_lock(&ghes_list_mutex);
332 pr_warning(GHES_PFX
333"Generic hardware error source: %d notified via POLL is not supported!\n",
334 generic->header.source_id);
335 break;
336 case ACPI_HEST_NOTIFY_EXTERNAL:
337 case ACPI_HEST_NOTIFY_LOCAL:
338 pr_warning(GHES_PFX
339"Generic hardware error source: %d notified via IRQ is not supported!\n",
340 generic->header.source_id);
341 break;
342 case ACPI_HEST_NOTIFY_SCI:
343 if (list_empty(&ghes_sci)) 332 if (list_empty(&ghes_sci))
344 register_acpi_hed_notifier(&ghes_notifier_sci); 333 register_acpi_hed_notifier(&ghes_notifier_sci);
345 list_add_rcu(&ghes->list, &ghes_sci); 334 list_add_rcu(&ghes->list, &ghes_sci);
346 break; 335 mutex_unlock(&ghes_list_mutex);
347 case ACPI_HEST_NOTIFY_NMI: 336 } else {
348 pr_warning(GHES_PFX 337 unsigned char *notify = NULL;
349"Generic hardware error source: %d notified via NMI is not supported!\n", 338
350 generic->header.source_id); 339 switch (generic->notify.type) {
351 break; 340 case ACPI_HEST_NOTIFY_POLLED:
352 default: 341 notify = "POLL";
353 pr_warning(FW_WARN GHES_PFX 342 break;
354 "Unknown notification type: %u for generic hardware error source: %d\n", 343 case ACPI_HEST_NOTIFY_EXTERNAL:
355 generic->notify.type, generic->header.source_id); 344 case ACPI_HEST_NOTIFY_LOCAL:
356 break; 345 notify = "IRQ";
346 break;
347 case ACPI_HEST_NOTIFY_NMI:
348 notify = "NMI";
349 break;
350 }
351 if (notify) {
352 pr_warning(GHES_PFX
353"Generic hardware error source: %d notified via %s is not supported!\n",
354 generic->header.source_id, notify);
355 } else {
356 pr_warning(FW_WARN GHES_PFX
357"Unknown notification type: %u for generic hardware error source: %d\n",
358 generic->notify.type, generic->header.source_id);
359 }
360 rc = -ENODEV;
361 goto err;
357 } 362 }
363 platform_set_drvdata(ghes_dev, ghes);
358 364
359 return 0; 365 return 0;
360err: 366err:
361 if (ghes) 367 if (ghes) {
362 ghes_fini(ghes); 368 ghes_fini(ghes);
369 kfree(ghes);
370 }
363 return rc; 371 return rc;
364} 372}
365 373
366static void ghes_cleanup(void) 374static int __devexit ghes_remove(struct platform_device *ghes_dev)
367{ 375{
368 struct ghes *ghes, *nghes; 376 struct ghes *ghes;
377 struct acpi_hest_generic *generic;
369 378
370 if (!list_empty(&ghes_sci)) 379 ghes = platform_get_drvdata(ghes_dev);
371 unregister_acpi_hed_notifier(&ghes_notifier_sci); 380 generic = ghes->generic;
381
382 switch (generic->notify.type) {
383 case ACPI_HEST_NOTIFY_SCI:
384 mutex_lock(&ghes_list_mutex);
385 list_del_rcu(&ghes->list);
386 if (list_empty(&ghes_sci))
387 unregister_acpi_hed_notifier(&ghes_notifier_sci);
388 mutex_unlock(&ghes_list_mutex);
389 break;
390 default:
391 BUG();
392 break;
393 }
372 394
373 synchronize_rcu(); 395 synchronize_rcu();
396 ghes_fini(ghes);
397 kfree(ghes);
374 398
375 list_for_each_entry_safe(ghes, nghes, &ghes_sci, list) { 399 platform_set_drvdata(ghes_dev, NULL);
376 list_del(&ghes->list); 400
377 ghes_fini(ghes); 401 return 0;
378 kfree(ghes);
379 }
380} 402}
381 403
404static struct platform_driver ghes_platform_driver = {
405 .driver = {
406 .name = "GHES",
407 .owner = THIS_MODULE,
408 },
409 .probe = ghes_probe,
410 .remove = ghes_remove,
411};
412
382static int __init ghes_init(void) 413static int __init ghes_init(void)
383{ 414{
384 int rc;
385
386 if (acpi_disabled) 415 if (acpi_disabled)
387 return -ENODEV; 416 return -ENODEV;
388 417
@@ -391,32 +420,12 @@ static int __init ghes_init(void)
391 return -EINVAL; 420 return -EINVAL;
392 } 421 }
393 422
394 rc = apei_hest_parse(hest_ghes_parse, NULL); 423 return platform_driver_register(&ghes_platform_driver);
395 if (rc) {
396 pr_err(GHES_PFX
397 "Error during parsing HEST generic hardware error sources.\n");
398 goto err_cleanup;
399 }
400
401 if (list_empty(&ghes_sci)) {
402 pr_info(GHES_PFX
403 "No functional generic hardware error sources.\n");
404 rc = -ENODEV;
405 goto err_cleanup;
406 }
407
408 pr_info(GHES_PFX
409 "Generic Hardware Error Source support is initialized.\n");
410
411 return 0;
412err_cleanup:
413 ghes_cleanup();
414 return rc;
415} 424}
416 425
417static void __exit ghes_exit(void) 426static void __exit ghes_exit(void)
418{ 427{
419 ghes_cleanup(); 428 platform_driver_unregister(&ghes_platform_driver);
420} 429}
421 430
422module_init(ghes_init); 431module_init(ghes_init);
@@ -425,3 +434,4 @@ module_exit(ghes_exit);
425MODULE_AUTHOR("Huang Ying"); 434MODULE_AUTHOR("Huang Ying");
426MODULE_DESCRIPTION("APEI Generic Hardware Error Source support"); 435MODULE_DESCRIPTION("APEI Generic Hardware Error Source support");
427MODULE_LICENSE("GPL"); 436MODULE_LICENSE("GPL");
437MODULE_ALIAS("platform:GHES");