diff options
author | Huang Ying <ying.huang@intel.com> | 2010-08-02 03:48:24 -0400 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2010-08-08 14:55:52 -0400 |
commit | 7ad6e9435596f692ff65f399da12816c94960185 (patch) | |
tree | 85786993b961b240f2c95fa320b5eeff859dae2c | |
parent | ad4ecef2f13c790f95b55320f2925c205d8f971f (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>
-rw-r--r-- | drivers/acpi/apei/ghes.c | 140 | ||||
-rw-r--r-- | drivers/acpi/apei/hest.c | 76 |
2 files changed, 145 insertions, 71 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 | */ |
89 | static LIST_HEAD(ghes_sci); | 91 | static LIST_HEAD(ghes_sci); |
92 | static DEFINE_MUTEX(ghes_list_mutex); | ||
90 | 93 | ||
91 | static struct ghes *ghes_new(struct acpi_hest_generic *generic) | 94 | static 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 | ||
296 | static int hest_ghes_parse(struct acpi_hest_header *hest_hdr, void *data) | 299 | static 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; |
360 | err: | 366 | err: |
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 | ||
366 | static void ghes_cleanup(void) | 374 | static 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 | ||
404 | static 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 | |||
382 | static int __init ghes_init(void) | 413 | static 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; | ||
412 | err_cleanup: | ||
413 | ghes_cleanup(); | ||
414 | return rc; | ||
415 | } | 424 | } |
416 | 425 | ||
417 | static void __exit ghes_exit(void) | 426 | static void __exit ghes_exit(void) |
418 | { | 427 | { |
419 | ghes_cleanup(); | 428 | platform_driver_unregister(&ghes_platform_driver); |
420 | } | 429 | } |
421 | 430 | ||
422 | module_init(ghes_init); | 431 | module_init(ghes_init); |
@@ -425,3 +434,4 @@ module_exit(ghes_exit); | |||
425 | MODULE_AUTHOR("Huang Ying"); | 434 | MODULE_AUTHOR("Huang Ying"); |
426 | MODULE_DESCRIPTION("APEI Generic Hardware Error Source support"); | 435 | MODULE_DESCRIPTION("APEI Generic Hardware Error Source support"); |
427 | MODULE_LICENSE("GPL"); | 436 | MODULE_LICENSE("GPL"); |
437 | MODULE_ALIAS("platform:GHES"); | ||
diff --git a/drivers/acpi/apei/hest.c b/drivers/acpi/apei/hest.c index e7f40d362cb3..343168d18266 100644 --- a/drivers/acpi/apei/hest.c +++ b/drivers/acpi/apei/hest.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include <linux/kdebug.h> | 34 | #include <linux/kdebug.h> |
35 | #include <linux/highmem.h> | 35 | #include <linux/highmem.h> |
36 | #include <linux/io.h> | 36 | #include <linux/io.h> |
37 | #include <linux/platform_device.h> | ||
37 | #include <acpi/apei.h> | 38 | #include <acpi/apei.h> |
38 | 39 | ||
39 | #include "apei-internal.h" | 40 | #include "apei-internal.h" |
@@ -47,11 +48,6 @@ EXPORT_SYMBOL_GPL(hest_disable); | |||
47 | 48 | ||
48 | static struct acpi_table_hest *hest_tab; | 49 | static struct acpi_table_hest *hest_tab; |
49 | 50 | ||
50 | static int hest_void_parse(struct acpi_hest_header *hest_hdr, void *data) | ||
51 | { | ||
52 | return 0; | ||
53 | } | ||
54 | |||
55 | static int hest_esrc_len_tab[ACPI_HEST_TYPE_RESERVED] = { | 51 | static int hest_esrc_len_tab[ACPI_HEST_TYPE_RESERVED] = { |
56 | [ACPI_HEST_TYPE_IA32_CHECK] = -1, /* need further calculation */ | 52 | [ACPI_HEST_TYPE_IA32_CHECK] = -1, /* need further calculation */ |
57 | [ACPI_HEST_TYPE_IA32_CORRECTED_CHECK] = -1, | 53 | [ACPI_HEST_TYPE_IA32_CORRECTED_CHECK] = -1, |
@@ -125,6 +121,69 @@ int apei_hest_parse(apei_hest_func_t func, void *data) | |||
125 | } | 121 | } |
126 | EXPORT_SYMBOL_GPL(apei_hest_parse); | 122 | EXPORT_SYMBOL_GPL(apei_hest_parse); |
127 | 123 | ||
124 | struct ghes_arr { | ||
125 | struct platform_device **ghes_devs; | ||
126 | unsigned int count; | ||
127 | }; | ||
128 | |||
129 | static int hest_parse_ghes_count(struct acpi_hest_header *hest_hdr, void *data) | ||
130 | { | ||
131 | int *count = data; | ||
132 | |||
133 | if (hest_hdr->type == ACPI_HEST_TYPE_GENERIC_ERROR) | ||
134 | (*count)++; | ||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static int hest_parse_ghes(struct acpi_hest_header *hest_hdr, void *data) | ||
139 | { | ||
140 | struct acpi_hest_generic *generic; | ||
141 | struct platform_device *ghes_dev; | ||
142 | struct ghes_arr *ghes_arr = data; | ||
143 | int rc; | ||
144 | |||
145 | if (hest_hdr->type != ACPI_HEST_TYPE_GENERIC_ERROR) | ||
146 | return 0; | ||
147 | generic = (struct acpi_hest_generic *)hest_hdr; | ||
148 | if (!generic->enabled) | ||
149 | return 0; | ||
150 | ghes_dev = platform_device_alloc("GHES", hest_hdr->source_id); | ||
151 | if (!ghes_dev) | ||
152 | return -ENOMEM; | ||
153 | ghes_dev->dev.platform_data = generic; | ||
154 | rc = platform_device_add(ghes_dev); | ||
155 | if (rc) | ||
156 | goto err; | ||
157 | ghes_arr->ghes_devs[ghes_arr->count++] = ghes_dev; | ||
158 | |||
159 | return 0; | ||
160 | err: | ||
161 | platform_device_put(ghes_dev); | ||
162 | return rc; | ||
163 | } | ||
164 | |||
165 | static int hest_ghes_dev_register(unsigned int ghes_count) | ||
166 | { | ||
167 | int rc, i; | ||
168 | struct ghes_arr ghes_arr; | ||
169 | |||
170 | ghes_arr.count = 0; | ||
171 | ghes_arr.ghes_devs = kmalloc(sizeof(void *) * ghes_count, GFP_KERNEL); | ||
172 | if (!ghes_arr.ghes_devs) | ||
173 | return -ENOMEM; | ||
174 | |||
175 | rc = apei_hest_parse(hest_parse_ghes, &ghes_arr); | ||
176 | if (rc) | ||
177 | goto err; | ||
178 | out: | ||
179 | kfree(ghes_arr.ghes_devs); | ||
180 | return rc; | ||
181 | err: | ||
182 | for (i = 0; i < ghes_arr.count; i++) | ||
183 | platform_device_unregister(ghes_arr.ghes_devs[i]); | ||
184 | goto out; | ||
185 | } | ||
186 | |||
128 | static int __init setup_hest_disable(char *str) | 187 | static int __init setup_hest_disable(char *str) |
129 | { | 188 | { |
130 | hest_disable = 1; | 189 | hest_disable = 1; |
@@ -137,6 +196,7 @@ static int __init hest_init(void) | |||
137 | { | 196 | { |
138 | acpi_status status; | 197 | acpi_status status; |
139 | int rc = -ENODEV; | 198 | int rc = -ENODEV; |
199 | unsigned int ghes_count = 0; | ||
140 | 200 | ||
141 | if (acpi_disabled) | 201 | if (acpi_disabled) |
142 | goto err; | 202 | goto err; |
@@ -158,7 +218,11 @@ static int __init hest_init(void) | |||
158 | goto err; | 218 | goto err; |
159 | } | 219 | } |
160 | 220 | ||
161 | rc = apei_hest_parse(hest_void_parse, NULL); | 221 | rc = apei_hest_parse(hest_parse_ghes_count, &ghes_count); |
222 | if (rc) | ||
223 | goto err; | ||
224 | |||
225 | rc = hest_ghes_dev_register(ghes_count); | ||
162 | if (rc) | 226 | if (rc) |
163 | goto err; | 227 | goto err; |
164 | 228 | ||