diff options
-rw-r--r-- | drivers/acpi/osl.c | 6 | ||||
-rw-r--r-- | drivers/acpi/resource.c | 160 | ||||
-rw-r--r-- | drivers/pnp/system.c | 35 | ||||
-rw-r--r-- | include/linux/acpi.h | 10 |
4 files changed, 197 insertions, 14 deletions
diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 7ccba395c9dd..5226a8b921ae 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c | |||
@@ -175,11 +175,7 @@ static void __init acpi_request_region (struct acpi_generic_address *gas, | |||
175 | if (!addr || !length) | 175 | if (!addr || !length) |
176 | return; | 176 | return; |
177 | 177 | ||
178 | /* Resources are never freed */ | 178 | acpi_reserve_region(addr, length, gas->space_id, 0, desc); |
179 | if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_IO) | ||
180 | request_region(addr, length, desc); | ||
181 | else if (gas->space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) | ||
182 | request_mem_region(addr, length, desc); | ||
183 | } | 179 | } |
184 | 180 | ||
185 | static void __init acpi_reserve_resources(void) | 181 | static void __init acpi_reserve_resources(void) |
diff --git a/drivers/acpi/resource.c b/drivers/acpi/resource.c index 8244f013f210..fcb7807ea8b7 100644 --- a/drivers/acpi/resource.c +++ b/drivers/acpi/resource.c | |||
@@ -26,6 +26,7 @@ | |||
26 | #include <linux/device.h> | 26 | #include <linux/device.h> |
27 | #include <linux/export.h> | 27 | #include <linux/export.h> |
28 | #include <linux/ioport.h> | 28 | #include <linux/ioport.h> |
29 | #include <linux/list.h> | ||
29 | #include <linux/slab.h> | 30 | #include <linux/slab.h> |
30 | 31 | ||
31 | #ifdef CONFIG_X86 | 32 | #ifdef CONFIG_X86 |
@@ -621,3 +622,162 @@ int acpi_dev_filter_resource_type(struct acpi_resource *ares, | |||
621 | return (type & types) ? 0 : 1; | 622 | return (type & types) ? 0 : 1; |
622 | } | 623 | } |
623 | EXPORT_SYMBOL_GPL(acpi_dev_filter_resource_type); | 624 | EXPORT_SYMBOL_GPL(acpi_dev_filter_resource_type); |
625 | |||
626 | struct reserved_region { | ||
627 | struct list_head node; | ||
628 | u64 start; | ||
629 | u64 end; | ||
630 | }; | ||
631 | |||
632 | static LIST_HEAD(reserved_io_regions); | ||
633 | static LIST_HEAD(reserved_mem_regions); | ||
634 | |||
635 | static int request_range(u64 start, u64 end, u8 space_id, unsigned long flags, | ||
636 | char *desc) | ||
637 | { | ||
638 | unsigned int length = end - start + 1; | ||
639 | struct resource *res; | ||
640 | |||
641 | res = space_id == ACPI_ADR_SPACE_SYSTEM_IO ? | ||
642 | request_region(start, length, desc) : | ||
643 | request_mem_region(start, length, desc); | ||
644 | if (!res) | ||
645 | return -EIO; | ||
646 | |||
647 | res->flags &= ~flags; | ||
648 | return 0; | ||
649 | } | ||
650 | |||
651 | static int add_region_before(u64 start, u64 end, u8 space_id, | ||
652 | unsigned long flags, char *desc, | ||
653 | struct list_head *head) | ||
654 | { | ||
655 | struct reserved_region *reg; | ||
656 | int error; | ||
657 | |||
658 | reg = kmalloc(sizeof(*reg), GFP_KERNEL); | ||
659 | if (!reg) | ||
660 | return -ENOMEM; | ||
661 | |||
662 | error = request_range(start, end, space_id, flags, desc); | ||
663 | if (error) | ||
664 | return error; | ||
665 | |||
666 | reg->start = start; | ||
667 | reg->end = end; | ||
668 | list_add_tail(®->node, head); | ||
669 | return 0; | ||
670 | } | ||
671 | |||
672 | /** | ||
673 | * acpi_reserve_region - Reserve an I/O or memory region as a system resource. | ||
674 | * @start: Starting address of the region. | ||
675 | * @length: Length of the region. | ||
676 | * @space_id: Identifier of address space to reserve the region from. | ||
677 | * @flags: Resource flags to clear for the region after requesting it. | ||
678 | * @desc: Region description (for messages). | ||
679 | * | ||
680 | * Reserve an I/O or memory region as a system resource to prevent others from | ||
681 | * using it. If the new region overlaps with one of the regions (in the given | ||
682 | * address space) already reserved by this routine, only the non-overlapping | ||
683 | * parts of it will be reserved. | ||
684 | * | ||
685 | * Returned is either 0 (success) or a negative error code indicating a resource | ||
686 | * reservation problem. It is the code of the first encountered error, but the | ||
687 | * routine doesn't abort until it has attempted to request all of the parts of | ||
688 | * the new region that don't overlap with other regions reserved previously. | ||
689 | * | ||
690 | * The resources requested by this routine are never released. | ||
691 | */ | ||
692 | int acpi_reserve_region(u64 start, unsigned int length, u8 space_id, | ||
693 | unsigned long flags, char *desc) | ||
694 | { | ||
695 | struct list_head *regions; | ||
696 | struct reserved_region *reg; | ||
697 | u64 end = start + length - 1; | ||
698 | int ret = 0, error = 0; | ||
699 | |||
700 | if (space_id == ACPI_ADR_SPACE_SYSTEM_IO) | ||
701 | regions = &reserved_io_regions; | ||
702 | else if (space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) | ||
703 | regions = &reserved_mem_regions; | ||
704 | else | ||
705 | return -EINVAL; | ||
706 | |||
707 | if (list_empty(regions)) | ||
708 | return add_region_before(start, end, space_id, flags, desc, regions); | ||
709 | |||
710 | list_for_each_entry(reg, regions, node) | ||
711 | if (reg->start == end + 1) { | ||
712 | /* The new region can be prepended to this one. */ | ||
713 | ret = request_range(start, end, space_id, flags, desc); | ||
714 | if (!ret) | ||
715 | reg->start = start; | ||
716 | |||
717 | return ret; | ||
718 | } else if (reg->start > end) { | ||
719 | /* No overlap. Add the new region here and get out. */ | ||
720 | return add_region_before(start, end, space_id, flags, | ||
721 | desc, ®->node); | ||
722 | } else if (reg->end == start - 1) { | ||
723 | goto combine; | ||
724 | } else if (reg->end >= start) { | ||
725 | goto overlap; | ||
726 | } | ||
727 | |||
728 | /* The new region goes after the last existing one. */ | ||
729 | return add_region_before(start, end, space_id, flags, desc, regions); | ||
730 | |||
731 | overlap: | ||
732 | /* | ||
733 | * The new region overlaps an existing one. | ||
734 | * | ||
735 | * The head part of the new region immediately preceding the existing | ||
736 | * overlapping one can be combined with it right away. | ||
737 | */ | ||
738 | if (reg->start > start) { | ||
739 | error = request_range(start, reg->start - 1, space_id, flags, desc); | ||
740 | if (error) | ||
741 | ret = error; | ||
742 | else | ||
743 | reg->start = start; | ||
744 | } | ||
745 | |||
746 | combine: | ||
747 | /* | ||
748 | * The new region is adjacent to an existing one. If it extends beyond | ||
749 | * that region all the way to the next one, it is possible to combine | ||
750 | * all three of them. | ||
751 | */ | ||
752 | while (reg->end < end) { | ||
753 | struct reserved_region *next = NULL; | ||
754 | u64 a = reg->end + 1, b = end; | ||
755 | |||
756 | if (!list_is_last(®->node, regions)) { | ||
757 | next = list_next_entry(reg, node); | ||
758 | if (next->start <= end) | ||
759 | b = next->start - 1; | ||
760 | } | ||
761 | error = request_range(a, b, space_id, flags, desc); | ||
762 | if (!error) { | ||
763 | if (next && next->start == b + 1) { | ||
764 | reg->end = next->end; | ||
765 | list_del(&next->node); | ||
766 | kfree(next); | ||
767 | } else { | ||
768 | reg->end = end; | ||
769 | break; | ||
770 | } | ||
771 | } else if (next) { | ||
772 | if (!ret) | ||
773 | ret = error; | ||
774 | |||
775 | reg = next; | ||
776 | } else { | ||
777 | break; | ||
778 | } | ||
779 | } | ||
780 | |||
781 | return ret ? ret : error; | ||
782 | } | ||
783 | EXPORT_SYMBOL_GPL(acpi_reserve_region); | ||
diff --git a/drivers/pnp/system.c b/drivers/pnp/system.c index 49c1720df59a..515f33882ab8 100644 --- a/drivers/pnp/system.c +++ b/drivers/pnp/system.c | |||
@@ -7,6 +7,7 @@ | |||
7 | * Bjorn Helgaas <bjorn.helgaas@hp.com> | 7 | * Bjorn Helgaas <bjorn.helgaas@hp.com> |
8 | */ | 8 | */ |
9 | 9 | ||
10 | #include <linux/acpi.h> | ||
10 | #include <linux/pnp.h> | 11 | #include <linux/pnp.h> |
11 | #include <linux/device.h> | 12 | #include <linux/device.h> |
12 | #include <linux/init.h> | 13 | #include <linux/init.h> |
@@ -22,25 +23,41 @@ static const struct pnp_device_id pnp_dev_table[] = { | |||
22 | {"", 0} | 23 | {"", 0} |
23 | }; | 24 | }; |
24 | 25 | ||
26 | #ifdef CONFIG_ACPI | ||
27 | static bool __reserve_range(u64 start, unsigned int length, bool io, char *desc) | ||
28 | { | ||
29 | u8 space_id = io ? ACPI_ADR_SPACE_SYSTEM_IO : ACPI_ADR_SPACE_SYSTEM_MEMORY; | ||
30 | return !acpi_reserve_region(start, length, space_id, IORESOURCE_BUSY, desc); | ||
31 | } | ||
32 | #else | ||
33 | static bool __reserve_range(u64 start, unsigned int length, bool io, char *desc) | ||
34 | { | ||
35 | struct resource *res; | ||
36 | |||
37 | res = io ? request_region(start, length, desc) : | ||
38 | request_mem_region(start, length, desc); | ||
39 | if (res) { | ||
40 | res->flags &= ~IORESOURCE_BUSY; | ||
41 | return true; | ||
42 | } | ||
43 | return false; | ||
44 | } | ||
45 | #endif | ||
46 | |||
25 | static void reserve_range(struct pnp_dev *dev, struct resource *r, int port) | 47 | static void reserve_range(struct pnp_dev *dev, struct resource *r, int port) |
26 | { | 48 | { |
27 | char *regionid; | 49 | char *regionid; |
28 | const char *pnpid = dev_name(&dev->dev); | 50 | const char *pnpid = dev_name(&dev->dev); |
29 | resource_size_t start = r->start, end = r->end; | 51 | resource_size_t start = r->start, end = r->end; |
30 | struct resource *res; | 52 | bool reserved; |
31 | 53 | ||
32 | regionid = kmalloc(16, GFP_KERNEL); | 54 | regionid = kmalloc(16, GFP_KERNEL); |
33 | if (!regionid) | 55 | if (!regionid) |
34 | return; | 56 | return; |
35 | 57 | ||
36 | snprintf(regionid, 16, "pnp %s", pnpid); | 58 | snprintf(regionid, 16, "pnp %s", pnpid); |
37 | if (port) | 59 | reserved = __reserve_range(start, end - start + 1, !!port, regionid); |
38 | res = request_region(start, end - start + 1, regionid); | 60 | if (!reserved) |
39 | else | ||
40 | res = request_mem_region(start, end - start + 1, regionid); | ||
41 | if (res) | ||
42 | res->flags &= ~IORESOURCE_BUSY; | ||
43 | else | ||
44 | kfree(regionid); | 61 | kfree(regionid); |
45 | 62 | ||
46 | /* | 63 | /* |
@@ -49,7 +66,7 @@ static void reserve_range(struct pnp_dev *dev, struct resource *r, int port) | |||
49 | * have double reservations. | 66 | * have double reservations. |
50 | */ | 67 | */ |
51 | dev_info(&dev->dev, "%pR %s reserved\n", r, | 68 | dev_info(&dev->dev, "%pR %s reserved\n", r, |
52 | res ? "has been" : "could not be"); | 69 | reserved ? "has been" : "could not be"); |
53 | } | 70 | } |
54 | 71 | ||
55 | static void reserve_resources_of_dev(struct pnp_dev *dev) | 72 | static void reserve_resources_of_dev(struct pnp_dev *dev) |
diff --git a/include/linux/acpi.h b/include/linux/acpi.h index e4da5e35e29c..299763df1b27 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h | |||
@@ -332,6 +332,9 @@ int acpi_check_region(resource_size_t start, resource_size_t n, | |||
332 | 332 | ||
333 | int acpi_resources_are_enforced(void); | 333 | int acpi_resources_are_enforced(void); |
334 | 334 | ||
335 | int acpi_reserve_region(u64 start, unsigned int length, u8 space_id, | ||
336 | unsigned long flags, char *desc); | ||
337 | |||
335 | #ifdef CONFIG_HIBERNATION | 338 | #ifdef CONFIG_HIBERNATION |
336 | void __init acpi_no_s4_hw_signature(void); | 339 | void __init acpi_no_s4_hw_signature(void); |
337 | #endif | 340 | #endif |
@@ -525,6 +528,13 @@ static inline int acpi_check_region(resource_size_t start, resource_size_t n, | |||
525 | return 0; | 528 | return 0; |
526 | } | 529 | } |
527 | 530 | ||
531 | static inline int acpi_reserve_region(u64 start, unsigned int length, | ||
532 | u8 space_id, unsigned long flags, | ||
533 | char *desc) | ||
534 | { | ||
535 | return -ENXIO; | ||
536 | } | ||
537 | |||
528 | struct acpi_table_header; | 538 | struct acpi_table_header; |
529 | static inline int acpi_table_parse(char *id, | 539 | static inline int acpi_table_parse(char *id, |
530 | int (*handler)(struct acpi_table_header *)) | 540 | int (*handler)(struct acpi_table_header *)) |