diff options
| -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; |
