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