@@ -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) \
@@ -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)
{
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(+)