diff options
author | Thierry Reding <treding@nvidia.com> | 2014-08-01 08:15:10 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2014-09-04 16:41:43 -0400 |
commit | 8d38821cbcf51292cd5a23469d03bd38932a3ba9 (patch) | |
tree | a41f35c52ba3826dc4099a8bf24630a410633f62 | |
parent | 52addcf9d6669fa439387610bc65c92fa0980cef (diff) |
resources: Add device-managed request/release_resource()
Provide device-managed implementations of the request_resource() and
release_resource() functions. Upon failure to request a resource, the new
devm_request_resource() function will output an error message for
consistent error reporting.
Signed-off-by: Thierry Reding <treding@nvidia.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
Acked-by: Tejun Heo <tj@kernel.org>
-rw-r--r-- | Documentation/driver-model/devres.txt | 2 | ||||
-rw-r--r-- | include/linux/ioport.h | 5 | ||||
-rw-r--r-- | kernel/resource.c | 70 |
3 files changed, 77 insertions, 0 deletions
diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt index d14710b04439..befc3fe12ba6 100644 --- a/Documentation/driver-model/devres.txt +++ b/Documentation/driver-model/devres.txt | |||
@@ -264,8 +264,10 @@ IIO | |||
264 | IO region | 264 | IO region |
265 | devm_release_mem_region() | 265 | devm_release_mem_region() |
266 | devm_release_region() | 266 | devm_release_region() |
267 | devm_release_resource() | ||
267 | devm_request_mem_region() | 268 | devm_request_mem_region() |
268 | devm_request_region() | 269 | devm_request_region() |
270 | devm_request_resource() | ||
269 | 271 | ||
270 | IOMAP | 272 | IOMAP |
271 | devm_ioport_map() | 273 | devm_ioport_map() |
diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 142ec544167c..2c5250222278 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h | |||
@@ -215,6 +215,11 @@ static inline int __deprecated check_region(resource_size_t s, | |||
215 | 215 | ||
216 | /* Wrappers for managed devices */ | 216 | /* Wrappers for managed devices */ |
217 | struct device; | 217 | struct device; |
218 | |||
219 | extern int devm_request_resource(struct device *dev, struct resource *root, | ||
220 | struct resource *new); | ||
221 | extern void devm_release_resource(struct device *dev, struct resource *new); | ||
222 | |||
218 | #define devm_request_region(dev,start,n,name) \ | 223 | #define devm_request_region(dev,start,n,name) \ |
219 | __devm_request_region(dev, &ioport_resource, (start), (n), (name)) | 224 | __devm_request_region(dev, &ioport_resource, (start), (n), (name)) |
220 | #define devm_request_mem_region(dev,start,n,name) \ | 225 | #define devm_request_mem_region(dev,start,n,name) \ |
diff --git a/kernel/resource.c b/kernel/resource.c index da14b8d09296..ca24f19f9d18 100644 --- a/kernel/resource.c +++ b/kernel/resource.c | |||
@@ -1248,6 +1248,76 @@ int release_mem_region_adjustable(struct resource *parent, | |||
1248 | /* | 1248 | /* |
1249 | * Managed region resource | 1249 | * Managed region resource |
1250 | */ | 1250 | */ |
1251 | static void devm_resource_release(struct device *dev, void *ptr) | ||
1252 | { | ||
1253 | struct resource **r = ptr; | ||
1254 | |||
1255 | release_resource(*r); | ||
1256 | } | ||
1257 | |||
1258 | /** | ||
1259 | * devm_request_resource() - request and reserve an I/O or memory resource | ||
1260 | * @dev: device for which to request the resource | ||
1261 | * @root: root of the resource tree from which to request the resource | ||
1262 | * @new: descriptor of the resource to request | ||
1263 | * | ||
1264 | * This is a device-managed version of request_resource(). There is usually | ||
1265 | * no need to release resources requested by this function explicitly since | ||
1266 | * that will be taken care of when the device is unbound from its driver. | ||
1267 | * If for some reason the resource needs to be released explicitly, because | ||
1268 | * of ordering issues for example, drivers must call devm_release_resource() | ||
1269 | * rather than the regular release_resource(). | ||
1270 | * | ||
1271 | * When a conflict is detected between any existing resources and the newly | ||
1272 | * requested resource, an error message will be printed. | ||
1273 | * | ||
1274 | * Returns 0 on success or a negative error code on failure. | ||
1275 | */ | ||
1276 | int devm_request_resource(struct device *dev, struct resource *root, | ||
1277 | struct resource *new) | ||
1278 | { | ||
1279 | struct resource *conflict, **ptr; | ||
1280 | |||
1281 | ptr = devres_alloc(devm_resource_release, sizeof(*ptr), GFP_KERNEL); | ||
1282 | if (!ptr) | ||
1283 | return -ENOMEM; | ||
1284 | |||
1285 | *ptr = new; | ||
1286 | |||
1287 | conflict = request_resource_conflict(root, new); | ||
1288 | if (conflict) { | ||
1289 | dev_err(dev, "resource collision: %pR conflicts with %s %pR\n", | ||
1290 | new, conflict->name, conflict); | ||
1291 | devres_free(ptr); | ||
1292 | return -EBUSY; | ||
1293 | } | ||
1294 | |||
1295 | devres_add(dev, ptr); | ||
1296 | return 0; | ||
1297 | } | ||
1298 | EXPORT_SYMBOL(devm_request_resource); | ||
1299 | |||
1300 | static int devm_resource_match(struct device *dev, void *res, void *data) | ||
1301 | { | ||
1302 | struct resource **ptr = res; | ||
1303 | |||
1304 | return *ptr == data; | ||
1305 | } | ||
1306 | |||
1307 | /** | ||
1308 | * devm_release_resource() - release a previously requested resource | ||
1309 | * @dev: device for which to release the resource | ||
1310 | * @new: descriptor of the resource to release | ||
1311 | * | ||
1312 | * Releases a resource previously requested using devm_request_resource(). | ||
1313 | */ | ||
1314 | void devm_release_resource(struct device *dev, struct resource *new) | ||
1315 | { | ||
1316 | WARN_ON(devres_release(dev, devm_resource_release, devm_resource_match, | ||
1317 | new)); | ||
1318 | } | ||
1319 | EXPORT_SYMBOL(devm_release_resource); | ||
1320 | |||
1251 | struct region_devres { | 1321 | struct region_devres { |
1252 | struct resource *parent; | 1322 | struct resource *parent; |
1253 | resource_size_t start; | 1323 | resource_size_t start; |