diff options
author | Myron Stowe <mstowe@redhat.com> | 2011-11-07 18:23:41 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2012-01-17 04:36:40 -0500 |
commit | 700130b41f4ee54520ac2ef2f7f1d072789711a4 (patch) | |
tree | 8fe06ea2fdce1afab67db16ca9ecf8b25e150ff5 /drivers/acpi | |
parent | 6f68c91c55ea3576d366797fa8d45e31c4aa79f8 (diff) |
ACPI APEI: Convert atomicio routines
APEI needs memory access in interrupt context. The obvious choice is
acpi_read(), but originally it couldn't be used in interrupt context
because it makes temporary mappings with ioremap(). Therefore, we added
drivers/acpi/atomicio.c, which provides:
acpi_pre_map_gar() -- ioremap in process context
acpi_atomic_read() -- memory access in interrupt context
acpi_post_unmap_gar() -- iounmap
Later we added acpi_os_map_generic_address() (2971852) and enhanced
acpi_read() so it works in interrupt context as long as the address has
been previously mapped (620242a). Now this sequence:
acpi_os_map_generic_address() -- ioremap in process context
acpi_read()/apei_read() -- now OK in interrupt context
acpi_os_unmap_generic_address()
is equivalent to what atomicio.c provides.
This patch introduces apei_read() and apei_write(), which currently are
functional equivalents of acpi_read() and acpi_write(). This is mainly
proactive, to prevent APEI breakages if acpi_read() and acpi_write()
are ever augmented to support the 'bit_offset' field of GAS, as APEI's
__apei_exec_write_register() precludes splitting up functionality
related to 'bit_offset' and APEI's 'mask' (see its
APEI_EXEC_PRESERVE_REGISTER block).
With apei_read() and apei_write() in place, usages of atomicio routines
are converted to apei_read()/apei_write() and existing calls within
osl.c and the CA, based on the re-factoring that was done in an earlier
patch series - http://marc.info/?l=linux-acpi&m=128769263327206&w=2:
acpi_pre_map_gar() --> acpi_os_map_generic_address()
acpi_post_unmap_gar() --> acpi_os_unmap_generic_address()
acpi_atomic_read() --> apei_read()
acpi_atomic_write() --> apei_write()
Note that acpi_read() and acpi_write() currently use 'bit_width'
for accessing GARs which seems incorrect. 'bit_width' is the size of
the register, while 'access_width' is the size of the access the
processor must generate on the bus. The 'access_width' may be larger,
for example, if the hardware only supports 32-bit or 64-bit reads. I
wanted to minimize any possible impacts with this patch series so I
did *not* change this behavior.
Signed-off-by: Myron Stowe <myron.stowe@redhat.com>
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/acpi')
-rw-r--r-- | drivers/acpi/apei/apei-base.c | 102 | ||||
-rw-r--r-- | drivers/acpi/apei/apei-internal.h | 3 | ||||
-rw-r--r-- | drivers/acpi/apei/ghes.c | 10 |
3 files changed, 104 insertions, 11 deletions
diff --git a/drivers/acpi/apei/apei-base.c b/drivers/acpi/apei/apei-base.c index 4abb6c74a938..e45350cb6ac8 100644 --- a/drivers/acpi/apei/apei-base.c +++ b/drivers/acpi/apei/apei-base.c | |||
@@ -34,13 +34,13 @@ | |||
34 | #include <linux/module.h> | 34 | #include <linux/module.h> |
35 | #include <linux/init.h> | 35 | #include <linux/init.h> |
36 | #include <linux/acpi.h> | 36 | #include <linux/acpi.h> |
37 | #include <linux/acpi_io.h> | ||
37 | #include <linux/slab.h> | 38 | #include <linux/slab.h> |
38 | #include <linux/io.h> | 39 | #include <linux/io.h> |
39 | #include <linux/kref.h> | 40 | #include <linux/kref.h> |
40 | #include <linux/rculist.h> | 41 | #include <linux/rculist.h> |
41 | #include <linux/interrupt.h> | 42 | #include <linux/interrupt.h> |
42 | #include <linux/debugfs.h> | 43 | #include <linux/debugfs.h> |
43 | #include <acpi/atomicio.h> | ||
44 | 44 | ||
45 | #include "apei-internal.h" | 45 | #include "apei-internal.h" |
46 | 46 | ||
@@ -70,7 +70,7 @@ int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val) | |||
70 | { | 70 | { |
71 | int rc; | 71 | int rc; |
72 | 72 | ||
73 | rc = acpi_atomic_read(val, &entry->register_region); | 73 | rc = apei_read(val, &entry->register_region); |
74 | if (rc) | 74 | if (rc) |
75 | return rc; | 75 | return rc; |
76 | *val >>= entry->register_region.bit_offset; | 76 | *val >>= entry->register_region.bit_offset; |
@@ -116,13 +116,13 @@ int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val) | |||
116 | val <<= entry->register_region.bit_offset; | 116 | val <<= entry->register_region.bit_offset; |
117 | if (entry->flags & APEI_EXEC_PRESERVE_REGISTER) { | 117 | if (entry->flags & APEI_EXEC_PRESERVE_REGISTER) { |
118 | u64 valr = 0; | 118 | u64 valr = 0; |
119 | rc = acpi_atomic_read(&valr, &entry->register_region); | 119 | rc = apei_read(&valr, &entry->register_region); |
120 | if (rc) | 120 | if (rc) |
121 | return rc; | 121 | return rc; |
122 | valr &= ~(entry->mask << entry->register_region.bit_offset); | 122 | valr &= ~(entry->mask << entry->register_region.bit_offset); |
123 | val |= valr; | 123 | val |= valr; |
124 | } | 124 | } |
125 | rc = acpi_atomic_write(val, &entry->register_region); | 125 | rc = apei_write(val, &entry->register_region); |
126 | 126 | ||
127 | return rc; | 127 | return rc; |
128 | } | 128 | } |
@@ -243,7 +243,7 @@ static int pre_map_gar_callback(struct apei_exec_context *ctx, | |||
243 | u8 ins = entry->instruction; | 243 | u8 ins = entry->instruction; |
244 | 244 | ||
245 | if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER) | 245 | if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER) |
246 | return acpi_pre_map_gar(&entry->register_region); | 246 | return acpi_os_map_generic_address(&entry->register_region); |
247 | 247 | ||
248 | return 0; | 248 | return 0; |
249 | } | 249 | } |
@@ -276,7 +276,7 @@ static int post_unmap_gar_callback(struct apei_exec_context *ctx, | |||
276 | u8 ins = entry->instruction; | 276 | u8 ins = entry->instruction; |
277 | 277 | ||
278 | if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER) | 278 | if (ctx->ins_table[ins].flags & APEI_EXEC_INS_ACCESS_REGISTER) |
279 | acpi_post_unmap_gar(&entry->register_region); | 279 | acpi_os_unmap_generic_address(&entry->register_region); |
280 | 280 | ||
281 | return 0; | 281 | return 0; |
282 | } | 282 | } |
@@ -591,6 +591,96 @@ static int apei_check_gar(struct acpi_generic_address *reg, u64 *paddr) | |||
591 | return 0; | 591 | return 0; |
592 | } | 592 | } |
593 | 593 | ||
594 | /* read GAR in interrupt (including NMI) or process context */ | ||
595 | int apei_read(u64 *val, struct acpi_generic_address *reg) | ||
596 | { | ||
597 | int rc; | ||
598 | u64 address; | ||
599 | u32 tmp, width = reg->bit_width; | ||
600 | acpi_status status; | ||
601 | |||
602 | rc = apei_check_gar(reg, &address); | ||
603 | if (rc) | ||
604 | return rc; | ||
605 | |||
606 | if (width == 64) | ||
607 | width = 32; /* Break into two 32-bit transfers */ | ||
608 | |||
609 | *val = 0; | ||
610 | switch(reg->space_id) { | ||
611 | case ACPI_ADR_SPACE_SYSTEM_MEMORY: | ||
612 | status = acpi_os_read_memory((acpi_physical_address) | ||
613 | address, &tmp, width); | ||
614 | if (ACPI_FAILURE(status)) | ||
615 | return -EIO; | ||
616 | *val = tmp; | ||
617 | |||
618 | if (reg->bit_width == 64) { | ||
619 | /* Read the top 32 bits */ | ||
620 | status = acpi_os_read_memory((acpi_physical_address) | ||
621 | (address + 4), &tmp, 32); | ||
622 | if (ACPI_FAILURE(status)) | ||
623 | return -EIO; | ||
624 | *val |= ((u64)tmp << 32); | ||
625 | } | ||
626 | break; | ||
627 | case ACPI_ADR_SPACE_SYSTEM_IO: | ||
628 | status = acpi_os_read_port(address, (u32 *)val, reg->bit_width); | ||
629 | if (ACPI_FAILURE(status)) | ||
630 | return -EIO; | ||
631 | break; | ||
632 | default: | ||
633 | return -EINVAL; | ||
634 | } | ||
635 | |||
636 | return 0; | ||
637 | } | ||
638 | EXPORT_SYMBOL_GPL(apei_read); | ||
639 | |||
640 | /* write GAR in interrupt (including NMI) or process context */ | ||
641 | int apei_write(u64 val, struct acpi_generic_address *reg) | ||
642 | { | ||
643 | int rc; | ||
644 | u64 address; | ||
645 | u32 width = reg->bit_width; | ||
646 | acpi_status status; | ||
647 | |||
648 | rc = apei_check_gar(reg, &address); | ||
649 | if (rc) | ||
650 | return rc; | ||
651 | |||
652 | if (width == 64) | ||
653 | width = 32; /* Break into two 32-bit transfers */ | ||
654 | |||
655 | switch (reg->space_id) { | ||
656 | case ACPI_ADR_SPACE_SYSTEM_MEMORY: | ||
657 | status = acpi_os_write_memory((acpi_physical_address) | ||
658 | address, ACPI_LODWORD(val), | ||
659 | width); | ||
660 | if (ACPI_FAILURE(status)) | ||
661 | return -EIO; | ||
662 | |||
663 | if (reg->bit_width == 64) { | ||
664 | status = acpi_os_write_memory((acpi_physical_address) | ||
665 | (address + 4), | ||
666 | ACPI_HIDWORD(val), 32); | ||
667 | if (ACPI_FAILURE(status)) | ||
668 | return -EIO; | ||
669 | } | ||
670 | break; | ||
671 | case ACPI_ADR_SPACE_SYSTEM_IO: | ||
672 | status = acpi_os_write_port(address, val, reg->bit_width); | ||
673 | if (ACPI_FAILURE(status)) | ||
674 | return -EIO; | ||
675 | break; | ||
676 | default: | ||
677 | return -EINVAL; | ||
678 | } | ||
679 | |||
680 | return 0; | ||
681 | } | ||
682 | EXPORT_SYMBOL_GPL(apei_write); | ||
683 | |||
594 | static int collect_res_callback(struct apei_exec_context *ctx, | 684 | static int collect_res_callback(struct apei_exec_context *ctx, |
595 | struct acpi_whea_header *entry, | 685 | struct acpi_whea_header *entry, |
596 | void *data) | 686 | void *data) |
diff --git a/drivers/acpi/apei/apei-internal.h b/drivers/acpi/apei/apei-internal.h index d778edd34fba..cca240a33038 100644 --- a/drivers/acpi/apei/apei-internal.h +++ b/drivers/acpi/apei/apei-internal.h | |||
@@ -68,6 +68,9 @@ static inline int apei_exec_run_optional(struct apei_exec_context *ctx, u8 actio | |||
68 | /* IP has been set in instruction function */ | 68 | /* IP has been set in instruction function */ |
69 | #define APEI_EXEC_SET_IP 1 | 69 | #define APEI_EXEC_SET_IP 1 |
70 | 70 | ||
71 | int apei_read(u64 *val, struct acpi_generic_address *reg); | ||
72 | int apei_write(u64 val, struct acpi_generic_address *reg); | ||
73 | |||
71 | int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val); | 74 | int __apei_exec_read_register(struct acpi_whea_header *entry, u64 *val); |
72 | int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val); | 75 | int __apei_exec_write_register(struct acpi_whea_header *entry, u64 val); |
73 | int apei_exec_read_register(struct apei_exec_context *ctx, | 76 | int apei_exec_read_register(struct apei_exec_context *ctx, |
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index aaf36090de1e..b3207e16670e 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <linux/module.h> | 33 | #include <linux/module.h> |
34 | #include <linux/init.h> | 34 | #include <linux/init.h> |
35 | #include <linux/acpi.h> | 35 | #include <linux/acpi.h> |
36 | #include <linux/acpi_io.h> | ||
36 | #include <linux/io.h> | 37 | #include <linux/io.h> |
37 | #include <linux/interrupt.h> | 38 | #include <linux/interrupt.h> |
38 | #include <linux/timer.h> | 39 | #include <linux/timer.h> |
@@ -48,7 +49,6 @@ | |||
48 | #include <linux/pci.h> | 49 | #include <linux/pci.h> |
49 | #include <linux/aer.h> | 50 | #include <linux/aer.h> |
50 | #include <acpi/apei.h> | 51 | #include <acpi/apei.h> |
51 | #include <acpi/atomicio.h> | ||
52 | #include <acpi/hed.h> | 52 | #include <acpi/hed.h> |
53 | #include <asm/mce.h> | 53 | #include <asm/mce.h> |
54 | #include <asm/tlbflush.h> | 54 | #include <asm/tlbflush.h> |
@@ -301,7 +301,7 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic) | |||
301 | if (!ghes) | 301 | if (!ghes) |
302 | return ERR_PTR(-ENOMEM); | 302 | return ERR_PTR(-ENOMEM); |
303 | ghes->generic = generic; | 303 | ghes->generic = generic; |
304 | rc = acpi_pre_map_gar(&generic->error_status_address); | 304 | rc = acpi_os_map_generic_address(&generic->error_status_address); |
305 | if (rc) | 305 | if (rc) |
306 | goto err_free; | 306 | goto err_free; |
307 | error_block_length = generic->error_block_length; | 307 | error_block_length = generic->error_block_length; |
@@ -321,7 +321,7 @@ static struct ghes *ghes_new(struct acpi_hest_generic *generic) | |||
321 | return ghes; | 321 | return ghes; |
322 | 322 | ||
323 | err_unmap: | 323 | err_unmap: |
324 | acpi_post_unmap_gar(&generic->error_status_address); | 324 | acpi_os_unmap_generic_address(&generic->error_status_address); |
325 | err_free: | 325 | err_free: |
326 | kfree(ghes); | 326 | kfree(ghes); |
327 | return ERR_PTR(rc); | 327 | return ERR_PTR(rc); |
@@ -330,7 +330,7 @@ err_free: | |||
330 | static void ghes_fini(struct ghes *ghes) | 330 | static void ghes_fini(struct ghes *ghes) |
331 | { | 331 | { |
332 | kfree(ghes->estatus); | 332 | kfree(ghes->estatus); |
333 | acpi_post_unmap_gar(&ghes->generic->error_status_address); | 333 | acpi_os_unmap_generic_address(&ghes->generic->error_status_address); |
334 | } | 334 | } |
335 | 335 | ||
336 | enum { | 336 | enum { |
@@ -401,7 +401,7 @@ static int ghes_read_estatus(struct ghes *ghes, int silent) | |||
401 | u32 len; | 401 | u32 len; |
402 | int rc; | 402 | int rc; |
403 | 403 | ||
404 | rc = acpi_atomic_read(&buf_paddr, &g->error_status_address); | 404 | rc = apei_read(&buf_paddr, &g->error_status_address); |
405 | if (rc) { | 405 | if (rc) { |
406 | if (!silent && printk_ratelimit()) | 406 | if (!silent && printk_ratelimit()) |
407 | pr_warning(FW_WARN GHES_PFX | 407 | pr_warning(FW_WARN GHES_PFX |