diff mbox

[1/2] resource: add devm_request_mem_region_ram() for reserving RAM regions

Message ID 1374604978-3195-1-git-send-email-jbarnes@virtuousgeek.org (mailing list archive)
State New, archived
Headers show

Commit Message

Jesse Barnes July 23, 2013, 6:42 p.m. UTC
Early x86 code reserves a buffer called "RAM buffer" to prevent MMIO
reservations from landing on top of potential RAM space, which can fail
due to decode priority in the chipset.

However, some drivers need to reserve RAM regions that may overlap the
padding, and may not be described in the E820 map (like the i915
driver).  So add a new entry point to allow this, that will shrink the
"RAM buffer" padding if needed.

Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
---
 include/linux/ioport.h |    6 ++++
 kernel/resource.c      |   75 ++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 81 insertions(+)
diff mbox

Patch

diff --git a/include/linux/ioport.h b/include/linux/ioport.h
index 89b7c24..f44a4ad 100644
--- a/include/linux/ioport.h
+++ b/include/linux/ioport.h
@@ -209,11 +209,17 @@  struct device;
 	__devm_request_region(dev, &ioport_resource, (start), (n), (name))
 #define devm_request_mem_region(dev,start,n,name) \
 	__devm_request_region(dev, &iomem_resource, (start), (n), (name))
+#define devm_request_mem_region_ram(dev,start,n,name) \
+	__devm_request_region_ram(dev, &iomem_resource, (start), (n), (name))
 
 extern struct resource * __devm_request_region(struct device *dev,
 				struct resource *parent, resource_size_t start,
 				resource_size_t n, const char *name);
 
+extern struct resource * __devm_request_region_ram(struct device *dev,
+				struct resource *parent, resource_size_t start,
+				resource_size_t n, const char *name);
+
 #define devm_release_region(dev, start, n) \
 	__devm_release_region(dev, &ioport_resource, (start), (n))
 #define devm_release_mem_region(dev, start, n) \
diff --git a/kernel/resource.c b/kernel/resource.c
index 3f285dc..d8f8783 100644
--- a/kernel/resource.c
+++ b/kernel/resource.c
@@ -1216,6 +1216,81 @@  struct resource * __devm_request_region(struct device *dev,
 }
 EXPORT_SYMBOL(__devm_request_region);
 
+/* Same as above, but conflicts with "RAM buffer" are ok */
+struct resource * __devm_request_region_ram(struct device *dev,
+				struct resource *parent, resource_size_t start,
+				resource_size_t n, const char *name)
+{
+	struct region_devres *dr = NULL;
+	struct resource *res;
+
+	dr = devres_alloc(devm_region_release, sizeof(struct region_devres),
+			  GFP_KERNEL);
+	if (!dr)
+		return NULL;
+
+	dr->parent = parent;
+	dr->start = start;
+	dr->n = n;
+
+	res = __request_region(parent, start, n, name, 0);
+	if (res) {
+		goto out;
+	} else {
+		struct resource *conflict;
+
+		res = alloc_resource(GFP_ATOMIC);
+		if (!res)
+			goto fail;
+
+		res->name = name;
+		res->start = start;
+		res->end = start + n;
+		res->flags = IORESOURCE_BUSY;
+
+		conflict = request_resource_conflict(parent, res);
+		if (!conflict)
+			goto out;
+
+		if (strcmp(conflict->name, "RAM buffer"))
+			goto fail_free;
+
+		if (conflict->start <= res->start &&
+		    conflict->end <= res->end) {
+			resource_size_t new_size;
+
+			new_size = res->start - conflict->start;
+			adjust_resource(conflict, conflict->start, new_size);
+		} else if (conflict->start >= res->start &&
+			   conflict->end >= res->end) {
+			resource_size_t new_size;
+
+			new_size = conflict->end - res->end;
+			adjust_resource(conflict, res->start, new_size);
+		}
+
+		conflict = request_resource_conflict(parent, res);
+		if (!conflict)
+			goto out;
+		else {
+			dev_err("failed to adjust RAM buffer for %s\n",
+				res->name);
+			goto fail_free;
+		}
+	}
+
+out:
+	devres_add(dev, dr);
+	return res;
+
+fail_free:
+	free_resource(res);
+fail:
+	devres_free(dr);
+	return res;
+}
+EXPORT_SYMBOL(__devm_request_region_ram);
+
 void __devm_release_region(struct device *dev, struct resource *parent,
 			   resource_size_t start, resource_size_t n)
 {