summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/acpi/osl.c6
-rw-r--r--drivers/acpi/resource.c160
-rw-r--r--drivers/pnp/system.c35
-rw-r--r--include/linux/acpi.h10
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
185static void __init acpi_reserve_resources(void) 181static 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}
623EXPORT_SYMBOL_GPL(acpi_dev_filter_resource_type); 624EXPORT_SYMBOL_GPL(acpi_dev_filter_resource_type);
625
626struct reserved_region {
627 struct list_head node;
628 u64 start;
629 u64 end;
630};
631
632static LIST_HEAD(reserved_io_regions);
633static LIST_HEAD(reserved_mem_regions);
634
635static 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
651static 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(&reg->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 */
692int 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, &reg->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(&reg->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}
783EXPORT_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
27static 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
33static 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
25static void reserve_range(struct pnp_dev *dev, struct resource *r, int port) 47static 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
55static void reserve_resources_of_dev(struct pnp_dev *dev) 72static 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
333int acpi_resources_are_enforced(void); 333int acpi_resources_are_enforced(void);
334 334
335int 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
336void __init acpi_no_s4_hw_signature(void); 339void __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
531static 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
528struct acpi_table_header; 538struct acpi_table_header;
529static inline int acpi_table_parse(char *id, 539static inline int acpi_table_parse(char *id,
530 int (*handler)(struct acpi_table_header *)) 540 int (*handler)(struct acpi_table_header *))