Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

hwspinlock/core: register a bank of hwspinlocks in a single API call

Hardware Spinlock devices usually contain numerous locks (known
devices today support between 32 to 256 locks).

Originally hwspinlock core required drivers to register (and later,
when needed, unregister) each lock separately.

That worked, but required hwspinlocks drivers to do a bit extra work
when they were probed/removed.

This patch changes hwspin_lock_{un}register() to allow a bank of
hwspinlocks to be {un}registered in a single invocation.

A new 'struct hwspinlock_device', which contains an array of 'struct
hwspinlock's is now being passed to the core upon registration (so
instead of wrapping each struct hwspinlock, a priv member has been added
to allow drivers to piggyback their private data with each hwspinlock).

While at it, several per-lock members were moved to be per-device:
1. struct device *dev
2. struct hwspinlock_ops *ops

In addition, now that the array of locks is handled by the core,
there's no reason to maintain a per-lock 'int id' member: the id of the
lock anyway equals to its index in the bank's array plus the bank's
base_id.
Remove this per-lock id member too, and instead use a simple pointers
arithmetic to derive it.

As a result of this change, hwspinlocks drivers are now simpler and smaller
(about %20 code reduction) and the memory footprint of the hwspinlock
framework is reduced.

Signed-off-by: Ohad Ben-Cohen <ohad@wizery.com>

+211 -144
+40 -20
Documentation/hwspinlock.txt
··· 227 227 228 228 4. API for implementors 229 229 230 - int hwspin_lock_register(struct hwspinlock *hwlock); 230 + int hwspin_lock_register(struct hwspinlock_device *bank, struct device *dev, 231 + const struct hwspinlock_ops *ops, int base_id, int num_locks); 231 232 - to be called from the underlying platform-specific implementation, in 232 - order to register a new hwspinlock instance. Should be called from 233 - a process context (this function might sleep). 233 + order to register a new hwspinlock device (which is usually a bank of 234 + numerous locks). Should be called from a process context (this function 235 + might sleep). 234 236 Returns 0 on success, or appropriate error code on failure. 235 237 236 - struct hwspinlock *hwspin_lock_unregister(unsigned int id); 238 + int hwspin_lock_unregister(struct hwspinlock_device *bank); 237 239 - to be called from the underlying vendor-specific implementation, in order 238 - to unregister an existing (and unused) hwspinlock instance. 240 + to unregister an hwspinlock device (which is usually a bank of numerous 241 + locks). 239 242 Should be called from a process context (this function might sleep). 240 243 Returns the address of hwspinlock on success, or NULL on error (e.g. 241 244 if the hwspinlock is sill in use). 242 245 243 - 5. struct hwspinlock 246 + 5. Important structs 244 247 245 - This struct represents an hwspinlock instance. It is registered by the 246 - underlying hwspinlock implementation using the hwspin_lock_register() API. 248 + struct hwspinlock_device is a device which usually contains a bank 249 + of hardware locks. It is registered by the underlying hwspinlock 250 + implementation using the hwspin_lock_register() API. 247 251 248 252 /** 249 - * struct hwspinlock - vendor-specific hwspinlock implementation 250 - * 251 - * @dev: underlying device, will be used with runtime PM api 252 - * @ops: vendor-specific hwspinlock handlers 253 - * @id: a global, unique, system-wide, index of the lock. 254 - * @lock: initialized and used by hwspinlock core 253 + * struct hwspinlock_device - a device which usually spans numerous hwspinlocks 254 + * @dev: underlying device, will be used to invoke runtime PM api 255 + * @ops: platform-specific hwspinlock handlers 256 + * @base_id: id index of the first lock in this device 257 + * @num_locks: number of locks in this device 258 + * @lock: dynamically allocated array of 'struct hwspinlock' 255 259 */ 256 - struct hwspinlock { 260 + struct hwspinlock_device { 257 261 struct device *dev; 258 262 const struct hwspinlock_ops *ops; 259 - int id; 260 - spinlock_t lock; 263 + int base_id; 264 + int num_locks; 265 + struct hwspinlock lock[0]; 261 266 }; 262 267 263 - The underlying implementation is responsible to assign the dev, ops and id 264 - members. The lock member, OTOH, is initialized and used by the hwspinlock 265 - core. 268 + struct hwspinlock_device contains an array of hwspinlock structs, each 269 + of which represents a single hardware lock: 270 + 271 + /** 272 + * struct hwspinlock - this struct represents a single hwspinlock instance 273 + * @bank: the hwspinlock_device structure which owns this lock 274 + * @lock: initialized and used by hwspinlock core 275 + * @priv: private data, owned by the underlying platform-specific hwspinlock drv 276 + */ 277 + struct hwspinlock { 278 + struct hwspinlock_device *bank; 279 + spinlock_t lock; 280 + void *priv; 281 + }; 282 + 283 + When registering a bank of locks, the hwspinlock driver only needs to 284 + set the priv members of the locks. The rest of the members are set and 285 + initialized by the hwspinlock core itself. 266 286 267 287 6. Implementation callbacks 268 288
+109 -56
drivers/hwspinlock/hwspinlock_core.c
··· 117 117 return -EBUSY; 118 118 119 119 /* try to take the hwspinlock device */ 120 - ret = hwlock->ops->trylock(hwlock); 120 + ret = hwlock->bank->ops->trylock(hwlock); 121 121 122 122 /* if hwlock is already taken, undo spin_trylock_* and exit */ 123 123 if (!ret) { ··· 199 199 * Allow platform-specific relax handlers to prevent 200 200 * hogging the interconnect (no sleeping, though) 201 201 */ 202 - if (hwlock->ops->relax) 203 - hwlock->ops->relax(hwlock); 202 + if (hwlock->bank->ops->relax) 203 + hwlock->bank->ops->relax(hwlock); 204 204 } 205 205 206 206 return ret; ··· 245 245 */ 246 246 mb(); 247 247 248 - hwlock->ops->unlock(hwlock); 248 + hwlock->bank->ops->unlock(hwlock); 249 249 250 250 /* Undo the spin_trylock{_irq, _irqsave} called while locking */ 251 251 if (mode == HWLOCK_IRQSTATE) ··· 257 257 } 258 258 EXPORT_SYMBOL_GPL(__hwspin_unlock); 259 259 260 - /** 261 - * hwspin_lock_register() - register a new hw spinlock 262 - * @hwlock: hwspinlock to register. 263 - * 264 - * This function should be called from the underlying platform-specific 265 - * implementation, to register a new hwspinlock instance. 266 - * 267 - * Should be called from a process context (might sleep) 268 - * 269 - * Returns 0 on success, or an appropriate error code on failure 270 - */ 271 - int hwspin_lock_register(struct hwspinlock *hwlock) 260 + static int hwspin_lock_register_single(struct hwspinlock *hwlock, int id) 272 261 { 273 262 struct hwspinlock *tmp; 274 263 int ret; 275 264 276 - if (!hwlock || !hwlock->ops || 277 - !hwlock->ops->trylock || !hwlock->ops->unlock) { 278 - pr_err("invalid parameters\n"); 279 - return -EINVAL; 280 - } 281 - 282 - spin_lock_init(&hwlock->lock); 283 - 284 265 mutex_lock(&hwspinlock_tree_lock); 285 266 286 - ret = radix_tree_insert(&hwspinlock_tree, hwlock->id, hwlock); 287 - if (ret == -EEXIST) 288 - pr_err("hwspinlock id %d already exists!\n", hwlock->id); 289 - if (ret) 267 + ret = radix_tree_insert(&hwspinlock_tree, id, hwlock); 268 + if (ret) { 269 + if (ret == -EEXIST) 270 + pr_err("hwspinlock id %d already exists!\n", id); 290 271 goto out; 272 + } 291 273 292 274 /* mark this hwspinlock as available */ 293 - tmp = radix_tree_tag_set(&hwspinlock_tree, hwlock->id, 294 - HWSPINLOCK_UNUSED); 275 + tmp = radix_tree_tag_set(&hwspinlock_tree, id, HWSPINLOCK_UNUSED); 295 276 296 277 /* self-sanity check which should never fail */ 297 278 WARN_ON(tmp != hwlock); 298 279 299 280 out: 300 281 mutex_unlock(&hwspinlock_tree_lock); 301 - return ret; 282 + return 0; 302 283 } 303 - EXPORT_SYMBOL_GPL(hwspin_lock_register); 304 284 305 - /** 306 - * hwspin_lock_unregister() - unregister an hw spinlock 307 - * @id: index of the specific hwspinlock to unregister 308 - * 309 - * This function should be called from the underlying platform-specific 310 - * implementation, to unregister an existing (and unused) hwspinlock. 311 - * 312 - * Should be called from a process context (might sleep) 313 - * 314 - * Returns the address of hwspinlock @id on success, or NULL on failure 315 - */ 316 - struct hwspinlock *hwspin_lock_unregister(unsigned int id) 285 + static struct hwspinlock *hwspin_lock_unregister_single(unsigned int id) 317 286 { 318 287 struct hwspinlock *hwlock = NULL; 319 288 int ret; ··· 306 337 mutex_unlock(&hwspinlock_tree_lock); 307 338 return hwlock; 308 339 } 340 + 341 + /** 342 + * hwspin_lock_register() - register a new hw spinlock device 343 + * @bank: the hwspinlock device, which usually provides numerous hw locks 344 + * @dev: the backing device 345 + * @ops: hwspinlock handlers for this device 346 + * @base_id: id of the first hardware spinlock in this bank 347 + * @num_locks: number of hwspinlocks provided by this device 348 + * 349 + * This function should be called from the underlying platform-specific 350 + * implementation, to register a new hwspinlock device instance. 351 + * 352 + * Should be called from a process context (might sleep) 353 + * 354 + * Returns 0 on success, or an appropriate error code on failure 355 + */ 356 + int hwspin_lock_register(struct hwspinlock_device *bank, struct device *dev, 357 + const struct hwspinlock_ops *ops, int base_id, int num_locks) 358 + { 359 + struct hwspinlock *hwlock; 360 + int ret = 0, i; 361 + 362 + if (!bank || !ops || !dev || !num_locks || !ops->trylock || 363 + !ops->unlock) { 364 + pr_err("invalid parameters\n"); 365 + return -EINVAL; 366 + } 367 + 368 + bank->dev = dev; 369 + bank->ops = ops; 370 + bank->base_id = base_id; 371 + bank->num_locks = num_locks; 372 + 373 + for (i = 0; i < num_locks; i++) { 374 + hwlock = &bank->lock[i]; 375 + 376 + spin_lock_init(&hwlock->lock); 377 + hwlock->bank = bank; 378 + 379 + ret = hwspin_lock_register_single(hwlock, i); 380 + if (ret) 381 + goto reg_failed; 382 + } 383 + 384 + return 0; 385 + 386 + reg_failed: 387 + while (--i >= 0) 388 + hwspin_lock_unregister_single(i); 389 + return ret; 390 + } 391 + EXPORT_SYMBOL_GPL(hwspin_lock_register); 392 + 393 + /** 394 + * hwspin_lock_unregister() - unregister an hw spinlock device 395 + * @bank: the hwspinlock device, which usually provides numerous hw locks 396 + * 397 + * This function should be called from the underlying platform-specific 398 + * implementation, to unregister an existing (and unused) hwspinlock. 399 + * 400 + * Should be called from a process context (might sleep) 401 + * 402 + * Returns 0 on success, or an appropriate error code on failure 403 + */ 404 + int hwspin_lock_unregister(struct hwspinlock_device *bank) 405 + { 406 + struct hwspinlock *hwlock, *tmp; 407 + int i; 408 + 409 + for (i = 0; i < bank->num_locks; i++) { 410 + hwlock = &bank->lock[i]; 411 + 412 + tmp = hwspin_lock_unregister_single(bank->base_id + i); 413 + if (!tmp) 414 + return -EBUSY; 415 + 416 + /* self-sanity check that should never fail */ 417 + WARN_ON(tmp != hwlock); 418 + } 419 + 420 + return 0; 421 + } 309 422 EXPORT_SYMBOL_GPL(hwspin_lock_unregister); 310 423 311 424 /** ··· 402 351 */ 403 352 static int __hwspin_lock_request(struct hwspinlock *hwlock) 404 353 { 354 + struct device *dev = hwlock->bank->dev; 405 355 struct hwspinlock *tmp; 406 356 int ret; 407 357 408 358 /* prevent underlying implementation from being removed */ 409 - if (!try_module_get(hwlock->dev->driver->owner)) { 410 - dev_err(hwlock->dev, "%s: can't get owner\n", __func__); 359 + if (!try_module_get(dev->driver->owner)) { 360 + dev_err(dev, "%s: can't get owner\n", __func__); 411 361 return -EINVAL; 412 362 } 413 363 414 364 /* notify PM core that power is now needed */ 415 - ret = pm_runtime_get_sync(hwlock->dev); 365 + ret = pm_runtime_get_sync(dev); 416 366 if (ret < 0) { 417 - dev_err(hwlock->dev, "%s: can't power on device\n", __func__); 367 + dev_err(dev, "%s: can't power on device\n", __func__); 418 368 return ret; 419 369 } 420 370 421 371 /* mark hwspinlock as used, should not fail */ 422 - tmp = radix_tree_tag_clear(&hwspinlock_tree, hwlock->id, 372 + tmp = radix_tree_tag_clear(&hwspinlock_tree, hwlock_to_id(hwlock), 423 373 HWSPINLOCK_UNUSED); 424 374 425 375 /* self-sanity check that should never fail */ ··· 442 390 return -EINVAL; 443 391 } 444 392 445 - return hwlock->id; 393 + return hwlock_to_id(hwlock); 446 394 } 447 395 EXPORT_SYMBOL_GPL(hwspin_lock_get_id); 448 396 ··· 517 465 } 518 466 519 467 /* sanity check (this shouldn't happen) */ 520 - WARN_ON(hwlock->id != id); 468 + WARN_ON(hwlock_to_id(hwlock) != id); 521 469 522 470 /* make sure this hwspinlock is unused */ 523 471 ret = radix_tree_tag_get(&hwspinlock_tree, id, HWSPINLOCK_UNUSED); ··· 552 500 */ 553 501 int hwspin_lock_free(struct hwspinlock *hwlock) 554 502 { 503 + struct device *dev = hwlock->bank->dev; 555 504 struct hwspinlock *tmp; 556 505 int ret; 557 506 ··· 564 511 mutex_lock(&hwspinlock_tree_lock); 565 512 566 513 /* make sure the hwspinlock is used */ 567 - ret = radix_tree_tag_get(&hwspinlock_tree, hwlock->id, 514 + ret = radix_tree_tag_get(&hwspinlock_tree, hwlock_to_id(hwlock), 568 515 HWSPINLOCK_UNUSED); 569 516 if (ret == 1) { 570 - dev_err(hwlock->dev, "%s: hwlock is already free\n", __func__); 517 + dev_err(dev, "%s: hwlock is already free\n", __func__); 571 518 dump_stack(); 572 519 ret = -EINVAL; 573 520 goto out; 574 521 } 575 522 576 523 /* notify the underlying device that power is not needed */ 577 - ret = pm_runtime_put(hwlock->dev); 524 + ret = pm_runtime_put(dev); 578 525 if (ret < 0) 579 526 goto out; 580 527 581 528 /* mark this hwspinlock as available */ 582 - tmp = radix_tree_tag_set(&hwspinlock_tree, hwlock->id, 529 + tmp = radix_tree_tag_set(&hwspinlock_tree, hwlock_to_id(hwlock), 583 530 HWSPINLOCK_UNUSED); 584 531 585 532 /* sanity check (this shouldn't happen) */ 586 533 WARN_ON(tmp != hwlock); 587 534 588 - module_put(hwlock->dev->driver->owner); 535 + module_put(dev->driver->owner); 589 536 590 537 out: 591 538 mutex_unlock(&hwspinlock_tree_lock);
+28 -10
drivers/hwspinlock/hwspinlock_internal.h
··· 21 21 #include <linux/spinlock.h> 22 22 #include <linux/device.h> 23 23 24 + struct hwspinlock_device; 25 + 24 26 /** 25 27 * struct hwspinlock_ops - platform-specific hwspinlock handlers 26 28 * ··· 41 39 42 40 /** 43 41 * struct hwspinlock - this struct represents a single hwspinlock instance 44 - * 45 - * @dev: underlying device, will be used to invoke runtime PM api 46 - * @ops: platform-specific hwspinlock handlers 47 - * @id: a global, unique, system-wide, index of the lock. 42 + * @bank: the hwspinlock_device structure which owns this lock 48 43 * @lock: initialized and used by hwspinlock core 49 - * 50 - * Note: currently simplicity was opted for, but later we can squeeze some 51 - * memory bytes by grouping dev, ops in a single 52 - * per-platform struct, and have all hwspinlocks point at it. 44 + * @priv: private data, owned by the underlying platform-specific hwspinlock drv 53 45 */ 54 46 struct hwspinlock { 47 + struct hwspinlock_device *bank; 48 + spinlock_t lock; 49 + void *priv; 50 + }; 51 + 52 + /** 53 + * struct hwspinlock_device - a device which usually spans numerous hwspinlocks 54 + * @dev: underlying device, will be used to invoke runtime PM api 55 + * @ops: platform-specific hwspinlock handlers 56 + * @base_id: id index of the first lock in this device 57 + * @num_locks: number of locks in this device 58 + * @lock: dynamically allocated array of 'struct hwspinlock' 59 + */ 60 + struct hwspinlock_device { 55 61 struct device *dev; 56 62 const struct hwspinlock_ops *ops; 57 - int id; 58 - spinlock_t lock; 63 + int base_id; 64 + int num_locks; 65 + struct hwspinlock lock[0]; 59 66 }; 67 + 68 + static inline int hwlock_to_id(struct hwspinlock *hwlock) 69 + { 70 + int local_id = hwlock - &hwlock->bank->lock[0]; 71 + 72 + return hwlock->bank->base_id + local_id; 73 + } 60 74 61 75 #endif /* __HWSPINLOCK_HWSPINLOCK_H */
+28 -56
drivers/hwspinlock/omap_hwspinlock.c
··· 41 41 #define SPINLOCK_NOTTAKEN (0) /* free */ 42 42 #define SPINLOCK_TAKEN (1) /* locked */ 43 43 44 - #define to_omap_hwspinlock(lock) \ 45 - container_of(lock, struct omap_hwspinlock, lock) 46 - 47 - struct omap_hwspinlock { 48 - struct hwspinlock lock; 49 - void __iomem *addr; 50 - }; 51 - 52 - struct omap_hwspinlock_state { 53 - int num_locks; /* Total number of locks in system */ 54 - void __iomem *io_base; /* Mapped base address */ 55 - struct omap_hwspinlock lock[0]; /* Array of 'num_locks' locks */ 56 - }; 57 - 58 44 static int omap_hwspinlock_trylock(struct hwspinlock *lock) 59 45 { 60 - struct omap_hwspinlock *omap_lock = to_omap_hwspinlock(lock); 46 + void __iomem *lock_addr = lock->priv; 61 47 62 48 /* attempt to acquire the lock by reading its value */ 63 - return (SPINLOCK_NOTTAKEN == readl(omap_lock->addr)); 49 + return (SPINLOCK_NOTTAKEN == readl(lock_addr)); 64 50 } 65 51 66 52 static void omap_hwspinlock_unlock(struct hwspinlock *lock) 67 53 { 68 - struct omap_hwspinlock *omap_lock = to_omap_hwspinlock(lock); 54 + void __iomem *lock_addr = lock->priv; 69 55 70 56 /* release the lock by writing 0 to it */ 71 - writel(SPINLOCK_NOTTAKEN, omap_lock->addr); 57 + writel(SPINLOCK_NOTTAKEN, lock_addr); 72 58 } 73 59 74 60 /* ··· 81 95 static int __devinit omap_hwspinlock_probe(struct platform_device *pdev) 82 96 { 83 97 struct hwspinlock_pdata *pdata = pdev->dev.platform_data; 84 - struct omap_hwspinlock *omap_lock; 85 - struct omap_hwspinlock_state *state; 98 + struct hwspinlock_device *bank; 99 + struct hwspinlock *hwlock; 86 100 struct resource *res; 87 101 void __iomem *io_base; 88 - int i, ret; 102 + int num_locks, i, ret; 89 103 90 104 if (!pdata) 91 105 return -ENODEV; ··· 108 122 goto iounmap_base; 109 123 } 110 124 111 - i *= 32; /* actual number of locks in this device */ 125 + num_locks = i * 32; /* actual number of locks in this device */ 112 126 113 - state = kzalloc(sizeof(*state) + i * sizeof(*omap_lock), GFP_KERNEL); 114 - if (!state) { 127 + bank = kzalloc(sizeof(*bank) + num_locks * sizeof(*hwlock), GFP_KERNEL); 128 + if (!bank) { 115 129 ret = -ENOMEM; 116 130 goto iounmap_base; 117 131 } 118 132 119 - state->num_locks = i; 120 - state->io_base = io_base; 133 + platform_set_drvdata(pdev, bank); 121 134 122 - platform_set_drvdata(pdev, state); 135 + for (i = 0, hwlock = &bank->lock[0]; i < num_locks; i++, hwlock++) 136 + hwlock->priv = io_base + LOCK_BASE_OFFSET + sizeof(u32) * i; 123 137 124 138 /* 125 139 * runtime PM will make sure the clock of this module is ··· 127 141 */ 128 142 pm_runtime_enable(&pdev->dev); 129 143 130 - for (i = 0; i < state->num_locks; i++) { 131 - omap_lock = &state->lock[i]; 132 - 133 - omap_lock->lock.dev = &pdev->dev; 134 - omap_lock->lock.id = pdata->base_id + i; 135 - omap_lock->lock.ops = &omap_hwspinlock_ops; 136 - omap_lock->addr = io_base + LOCK_BASE_OFFSET + sizeof(u32) * i; 137 - 138 - ret = hwspin_lock_register(&omap_lock->lock); 139 - if (ret) 140 - goto free_locks; 141 - } 144 + ret = hwspin_lock_register(bank, &pdev->dev, &omap_hwspinlock_ops, 145 + pdata->base_id, num_locks); 146 + if (ret) 147 + goto reg_fail; 142 148 143 149 return 0; 144 150 145 - free_locks: 146 - while (--i >= 0) 147 - hwspin_lock_unregister(i); 151 + reg_fail: 148 152 pm_runtime_disable(&pdev->dev); 149 - kfree(state); 153 + kfree(bank); 150 154 iounmap_base: 151 155 iounmap(io_base); 152 156 return ret; ··· 144 168 145 169 static int omap_hwspinlock_remove(struct platform_device *pdev) 146 170 { 147 - struct omap_hwspinlock_state *state = platform_get_drvdata(pdev); 148 - struct hwspinlock *lock; 149 - int i; 171 + struct hwspinlock_device *bank = platform_get_drvdata(pdev); 172 + void __iomem *io_base = bank->lock[0].priv - LOCK_BASE_OFFSET; 173 + int ret; 150 174 151 - for (i = 0; i < state->num_locks; i++) { 152 - lock = hwspin_lock_unregister(i); 153 - /* this shouldn't happen at this point. if it does, at least 154 - * don't continue with the remove */ 155 - if (!lock) { 156 - dev_err(&pdev->dev, "%s: failed on %d\n", __func__, i); 157 - return -EBUSY; 158 - } 175 + ret = hwspin_lock_unregister(bank); 176 + if (ret) { 177 + dev_err(&pdev->dev, "%s failed: %d\n", __func__, ret); 178 + return ret; 159 179 } 160 180 161 181 pm_runtime_disable(&pdev->dev); 162 - iounmap(state->io_base); 163 - kfree(state); 182 + iounmap(io_base); 183 + kfree(bank); 164 184 165 185 return 0; 166 186 }
+6 -2
include/linux/hwspinlock.h
··· 20 20 21 21 #include <linux/err.h> 22 22 #include <linux/sched.h> 23 + #include <linux/device.h> 23 24 24 25 /* hwspinlock mode argument */ 25 26 #define HWLOCK_IRQSTATE 0x01 /* Disable interrupts, save state */ 26 27 #define HWLOCK_IRQ 0x02 /* Disable interrupts, don't save state */ 27 28 28 29 struct hwspinlock; 30 + struct hwspinlock_device; 31 + struct hwspinlock_ops; 29 32 30 33 /** 31 34 * struct hwspinlock_pdata - platform data for hwspinlock drivers ··· 60 57 61 58 #if defined(CONFIG_HWSPINLOCK) || defined(CONFIG_HWSPINLOCK_MODULE) 62 59 63 - int hwspin_lock_register(struct hwspinlock *lock); 64 - struct hwspinlock *hwspin_lock_unregister(unsigned int id); 60 + int hwspin_lock_register(struct hwspinlock_device *bank, struct device *dev, 61 + const struct hwspinlock_ops *ops, int base_id, int num_locks); 62 + int hwspin_lock_unregister(struct hwspinlock_device *bank); 65 63 struct hwspinlock *hwspin_lock_request(void); 66 64 struct hwspinlock *hwspin_lock_request_specific(unsigned int id); 67 65 int hwspin_lock_free(struct hwspinlock *hwlock);