xref: /openbmc/linux/Documentation/locking/hwspinlock.rst (revision ac8f933664c3a0e2d42f6ee9a2a6d25f87cb23f6)
195ca6d73SMauro Carvalho Chehab===========================
295ca6d73SMauro Carvalho ChehabHardware Spinlock Framework
395ca6d73SMauro Carvalho Chehab===========================
495ca6d73SMauro Carvalho Chehab
595ca6d73SMauro Carvalho ChehabIntroduction
695ca6d73SMauro Carvalho Chehab============
795ca6d73SMauro Carvalho Chehab
895ca6d73SMauro Carvalho ChehabHardware spinlock modules provide hardware assistance for synchronization
995ca6d73SMauro Carvalho Chehaband mutual exclusion between heterogeneous processors and those not operating
1095ca6d73SMauro Carvalho Chehabunder a single, shared operating system.
1195ca6d73SMauro Carvalho Chehab
1295ca6d73SMauro Carvalho ChehabFor example, OMAP4 has dual Cortex-A9, dual Cortex-M3 and a C64x+ DSP,
1395ca6d73SMauro Carvalho Chehabeach of which is running a different Operating System (the master, A9,
1495ca6d73SMauro Carvalho Chehabis usually running Linux and the slave processors, the M3 and the DSP,
1595ca6d73SMauro Carvalho Chehabare running some flavor of RTOS).
1695ca6d73SMauro Carvalho Chehab
1795ca6d73SMauro Carvalho ChehabA generic hwspinlock framework allows platform-independent drivers to use
1895ca6d73SMauro Carvalho Chehabthe hwspinlock device in order to access data structures that are shared
1995ca6d73SMauro Carvalho Chehabbetween remote processors, that otherwise have no alternative mechanism
2095ca6d73SMauro Carvalho Chehabto accomplish synchronization and mutual exclusion operations.
2195ca6d73SMauro Carvalho Chehab
2295ca6d73SMauro Carvalho ChehabThis is necessary, for example, for Inter-processor communications:
2395ca6d73SMauro Carvalho Chehabon OMAP4, cpu-intensive multimedia tasks are offloaded by the host to the
2495ca6d73SMauro Carvalho Chehabremote M3 and/or C64x+ slave processors (by an IPC subsystem called Syslink).
2595ca6d73SMauro Carvalho Chehab
2695ca6d73SMauro Carvalho ChehabTo achieve fast message-based communications, a minimal kernel support
2795ca6d73SMauro Carvalho Chehabis needed to deliver messages arriving from a remote processor to the
2895ca6d73SMauro Carvalho Chehabappropriate user process.
2995ca6d73SMauro Carvalho Chehab
3095ca6d73SMauro Carvalho ChehabThis communication is based on simple data structures that is shared between
3195ca6d73SMauro Carvalho Chehabthe remote processors, and access to it is synchronized using the hwspinlock
3295ca6d73SMauro Carvalho Chehabmodule (remote processor directly places new messages in this shared data
3395ca6d73SMauro Carvalho Chehabstructure).
3495ca6d73SMauro Carvalho Chehab
3595ca6d73SMauro Carvalho ChehabA common hwspinlock interface makes it possible to have generic, platform-
3695ca6d73SMauro Carvalho Chehabindependent, drivers.
3795ca6d73SMauro Carvalho Chehab
3895ca6d73SMauro Carvalho ChehabUser API
3995ca6d73SMauro Carvalho Chehab========
4095ca6d73SMauro Carvalho Chehab
4195ca6d73SMauro Carvalho Chehab::
4295ca6d73SMauro Carvalho Chehab
4395ca6d73SMauro Carvalho Chehab  struct hwspinlock *hwspin_lock_request(void);
4495ca6d73SMauro Carvalho Chehab
4595ca6d73SMauro Carvalho ChehabDynamically assign an hwspinlock and return its address, or NULL
4695ca6d73SMauro Carvalho Chehabin case an unused hwspinlock isn't available. Users of this
4795ca6d73SMauro Carvalho ChehabAPI will usually want to communicate the lock's id to the remote core
4895ca6d73SMauro Carvalho Chehabbefore it can be used to achieve synchronization.
4995ca6d73SMauro Carvalho Chehab
5095ca6d73SMauro Carvalho ChehabShould be called from a process context (might sleep).
5195ca6d73SMauro Carvalho Chehab
5295ca6d73SMauro Carvalho Chehab::
5395ca6d73SMauro Carvalho Chehab
5495ca6d73SMauro Carvalho Chehab  struct hwspinlock *hwspin_lock_request_specific(unsigned int id);
5595ca6d73SMauro Carvalho Chehab
5695ca6d73SMauro Carvalho ChehabAssign a specific hwspinlock id and return its address, or NULL
5795ca6d73SMauro Carvalho Chehabif that hwspinlock is already in use. Usually board code will
5895ca6d73SMauro Carvalho Chehabbe calling this function in order to reserve specific hwspinlock
5995ca6d73SMauro Carvalho Chehabids for predefined purposes.
6095ca6d73SMauro Carvalho Chehab
6195ca6d73SMauro Carvalho ChehabShould be called from a process context (might sleep).
6295ca6d73SMauro Carvalho Chehab
6395ca6d73SMauro Carvalho Chehab::
6495ca6d73SMauro Carvalho Chehab
6595ca6d73SMauro Carvalho Chehab  int of_hwspin_lock_get_id(struct device_node *np, int index);
6695ca6d73SMauro Carvalho Chehab
6795ca6d73SMauro Carvalho ChehabRetrieve the global lock id for an OF phandle-based specific lock.
6895ca6d73SMauro Carvalho ChehabThis function provides a means for DT users of a hwspinlock module
6995ca6d73SMauro Carvalho Chehabto get the global lock id of a specific hwspinlock, so that it can
7095ca6d73SMauro Carvalho Chehabbe requested using the normal hwspin_lock_request_specific() API.
7195ca6d73SMauro Carvalho Chehab
7295ca6d73SMauro Carvalho ChehabThe function returns a lock id number on success, -EPROBE_DEFER if
7395ca6d73SMauro Carvalho Chehabthe hwspinlock device is not yet registered with the core, or other
7495ca6d73SMauro Carvalho Chehaberror values.
7595ca6d73SMauro Carvalho Chehab
7695ca6d73SMauro Carvalho ChehabShould be called from a process context (might sleep).
7795ca6d73SMauro Carvalho Chehab
7895ca6d73SMauro Carvalho Chehab::
7995ca6d73SMauro Carvalho Chehab
8095ca6d73SMauro Carvalho Chehab  int hwspin_lock_free(struct hwspinlock *hwlock);
8195ca6d73SMauro Carvalho Chehab
8295ca6d73SMauro Carvalho ChehabFree a previously-assigned hwspinlock; returns 0 on success, or an
8395ca6d73SMauro Carvalho Chehabappropriate error code on failure (e.g. -EINVAL if the hwspinlock
8495ca6d73SMauro Carvalho Chehabis already free).
8595ca6d73SMauro Carvalho Chehab
8695ca6d73SMauro Carvalho ChehabShould be called from a process context (might sleep).
8795ca6d73SMauro Carvalho Chehab
8895ca6d73SMauro Carvalho Chehab::
8995ca6d73SMauro Carvalho Chehab
90*a6978d1bSRichard Maina  int hwspin_lock_bust(struct hwspinlock *hwlock, unsigned int id);
91*a6978d1bSRichard Maina
92*a6978d1bSRichard MainaAfter verifying the owner of the hwspinlock, release a previously acquired
93*a6978d1bSRichard Mainahwspinlock; returns 0 on success, or an appropriate error code on failure
94*a6978d1bSRichard Maina(e.g. -EOPNOTSUPP if the bust operation is not defined for the specific
95*a6978d1bSRichard Mainahwspinlock).
96*a6978d1bSRichard Maina
97*a6978d1bSRichard MainaShould be called from a process context (might sleep).
98*a6978d1bSRichard Maina
99*a6978d1bSRichard Maina::
100*a6978d1bSRichard Maina
10195ca6d73SMauro Carvalho Chehab  int hwspin_lock_timeout(struct hwspinlock *hwlock, unsigned int timeout);
10295ca6d73SMauro Carvalho Chehab
10395ca6d73SMauro Carvalho ChehabLock a previously-assigned hwspinlock with a timeout limit (specified in
10495ca6d73SMauro Carvalho Chehabmsecs). If the hwspinlock is already taken, the function will busy loop
10595ca6d73SMauro Carvalho Chehabwaiting for it to be released, but give up when the timeout elapses.
10695ca6d73SMauro Carvalho ChehabUpon a successful return from this function, preemption is disabled so
10795ca6d73SMauro Carvalho Chehabthe caller must not sleep, and is advised to release the hwspinlock as
10895ca6d73SMauro Carvalho Chehabsoon as possible, in order to minimize remote cores polling on the
10995ca6d73SMauro Carvalho Chehabhardware interconnect.
11095ca6d73SMauro Carvalho Chehab
11195ca6d73SMauro Carvalho ChehabReturns 0 when successful and an appropriate error code otherwise (most
11295ca6d73SMauro Carvalho Chehabnotably -ETIMEDOUT if the hwspinlock is still busy after timeout msecs).
11395ca6d73SMauro Carvalho ChehabThe function will never sleep.
11495ca6d73SMauro Carvalho Chehab
11595ca6d73SMauro Carvalho Chehab::
11695ca6d73SMauro Carvalho Chehab
11795ca6d73SMauro Carvalho Chehab  int hwspin_lock_timeout_irq(struct hwspinlock *hwlock, unsigned int timeout);
11895ca6d73SMauro Carvalho Chehab
11995ca6d73SMauro Carvalho ChehabLock a previously-assigned hwspinlock with a timeout limit (specified in
12095ca6d73SMauro Carvalho Chehabmsecs). If the hwspinlock is already taken, the function will busy loop
12195ca6d73SMauro Carvalho Chehabwaiting for it to be released, but give up when the timeout elapses.
12295ca6d73SMauro Carvalho ChehabUpon a successful return from this function, preemption and the local
12395ca6d73SMauro Carvalho Chehabinterrupts are disabled, so the caller must not sleep, and is advised to
12495ca6d73SMauro Carvalho Chehabrelease the hwspinlock as soon as possible.
12595ca6d73SMauro Carvalho Chehab
12695ca6d73SMauro Carvalho ChehabReturns 0 when successful and an appropriate error code otherwise (most
12795ca6d73SMauro Carvalho Chehabnotably -ETIMEDOUT if the hwspinlock is still busy after timeout msecs).
12895ca6d73SMauro Carvalho ChehabThe function will never sleep.
12995ca6d73SMauro Carvalho Chehab
13095ca6d73SMauro Carvalho Chehab::
13195ca6d73SMauro Carvalho Chehab
13295ca6d73SMauro Carvalho Chehab  int hwspin_lock_timeout_irqsave(struct hwspinlock *hwlock, unsigned int to,
13395ca6d73SMauro Carvalho Chehab				  unsigned long *flags);
13495ca6d73SMauro Carvalho Chehab
13595ca6d73SMauro Carvalho ChehabLock a previously-assigned hwspinlock with a timeout limit (specified in
13695ca6d73SMauro Carvalho Chehabmsecs). If the hwspinlock is already taken, the function will busy loop
13795ca6d73SMauro Carvalho Chehabwaiting for it to be released, but give up when the timeout elapses.
13895ca6d73SMauro Carvalho ChehabUpon a successful return from this function, preemption is disabled,
13995ca6d73SMauro Carvalho Chehablocal interrupts are disabled and their previous state is saved at the
14095ca6d73SMauro Carvalho Chehabgiven flags placeholder. The caller must not sleep, and is advised to
14195ca6d73SMauro Carvalho Chehabrelease the hwspinlock as soon as possible.
14295ca6d73SMauro Carvalho Chehab
14395ca6d73SMauro Carvalho ChehabReturns 0 when successful and an appropriate error code otherwise (most
14495ca6d73SMauro Carvalho Chehabnotably -ETIMEDOUT if the hwspinlock is still busy after timeout msecs).
14595ca6d73SMauro Carvalho Chehab
14695ca6d73SMauro Carvalho ChehabThe function will never sleep.
14795ca6d73SMauro Carvalho Chehab
14895ca6d73SMauro Carvalho Chehab::
14995ca6d73SMauro Carvalho Chehab
15095ca6d73SMauro Carvalho Chehab  int hwspin_lock_timeout_raw(struct hwspinlock *hwlock, unsigned int timeout);
15195ca6d73SMauro Carvalho Chehab
15295ca6d73SMauro Carvalho ChehabLock a previously-assigned hwspinlock with a timeout limit (specified in
15395ca6d73SMauro Carvalho Chehabmsecs). If the hwspinlock is already taken, the function will busy loop
15495ca6d73SMauro Carvalho Chehabwaiting for it to be released, but give up when the timeout elapses.
15595ca6d73SMauro Carvalho Chehab
15695ca6d73SMauro Carvalho ChehabCaution: User must protect the routine of getting hardware lock with mutex
15795ca6d73SMauro Carvalho Chehabor spinlock to avoid dead-lock, that will let user can do some time-consuming
15895ca6d73SMauro Carvalho Chehabor sleepable operations under the hardware lock.
15995ca6d73SMauro Carvalho Chehab
16095ca6d73SMauro Carvalho ChehabReturns 0 when successful and an appropriate error code otherwise (most
16195ca6d73SMauro Carvalho Chehabnotably -ETIMEDOUT if the hwspinlock is still busy after timeout msecs).
16295ca6d73SMauro Carvalho Chehab
16395ca6d73SMauro Carvalho ChehabThe function will never sleep.
16495ca6d73SMauro Carvalho Chehab
16595ca6d73SMauro Carvalho Chehab::
16695ca6d73SMauro Carvalho Chehab
16795ca6d73SMauro Carvalho Chehab  int hwspin_lock_timeout_in_atomic(struct hwspinlock *hwlock, unsigned int to);
16895ca6d73SMauro Carvalho Chehab
16995ca6d73SMauro Carvalho ChehabLock a previously-assigned hwspinlock with a timeout limit (specified in
17095ca6d73SMauro Carvalho Chehabmsecs). If the hwspinlock is already taken, the function will busy loop
17195ca6d73SMauro Carvalho Chehabwaiting for it to be released, but give up when the timeout elapses.
17295ca6d73SMauro Carvalho Chehab
17395ca6d73SMauro Carvalho ChehabThis function shall be called only from an atomic context and the timeout
17495ca6d73SMauro Carvalho Chehabvalue shall not exceed a few msecs.
17595ca6d73SMauro Carvalho Chehab
17695ca6d73SMauro Carvalho ChehabReturns 0 when successful and an appropriate error code otherwise (most
17795ca6d73SMauro Carvalho Chehabnotably -ETIMEDOUT if the hwspinlock is still busy after timeout msecs).
17895ca6d73SMauro Carvalho Chehab
17995ca6d73SMauro Carvalho ChehabThe function will never sleep.
18095ca6d73SMauro Carvalho Chehab
18195ca6d73SMauro Carvalho Chehab::
18295ca6d73SMauro Carvalho Chehab
18395ca6d73SMauro Carvalho Chehab  int hwspin_trylock(struct hwspinlock *hwlock);
18495ca6d73SMauro Carvalho Chehab
18595ca6d73SMauro Carvalho Chehab
18695ca6d73SMauro Carvalho ChehabAttempt to lock a previously-assigned hwspinlock, but immediately fail if
18795ca6d73SMauro Carvalho Chehabit is already taken.
18895ca6d73SMauro Carvalho Chehab
18995ca6d73SMauro Carvalho ChehabUpon a successful return from this function, preemption is disabled so
19095ca6d73SMauro Carvalho Chehabcaller must not sleep, and is advised to release the hwspinlock as soon as
19195ca6d73SMauro Carvalho Chehabpossible, in order to minimize remote cores polling on the hardware
19295ca6d73SMauro Carvalho Chehabinterconnect.
19395ca6d73SMauro Carvalho Chehab
19495ca6d73SMauro Carvalho ChehabReturns 0 on success and an appropriate error code otherwise (most
19595ca6d73SMauro Carvalho Chehabnotably -EBUSY if the hwspinlock was already taken).
19695ca6d73SMauro Carvalho ChehabThe function will never sleep.
19795ca6d73SMauro Carvalho Chehab
19895ca6d73SMauro Carvalho Chehab::
19995ca6d73SMauro Carvalho Chehab
20095ca6d73SMauro Carvalho Chehab  int hwspin_trylock_irq(struct hwspinlock *hwlock);
20195ca6d73SMauro Carvalho Chehab
20295ca6d73SMauro Carvalho Chehab
20395ca6d73SMauro Carvalho ChehabAttempt to lock a previously-assigned hwspinlock, but immediately fail if
20495ca6d73SMauro Carvalho Chehabit is already taken.
20595ca6d73SMauro Carvalho Chehab
20695ca6d73SMauro Carvalho ChehabUpon a successful return from this function, preemption and the local
20795ca6d73SMauro Carvalho Chehabinterrupts are disabled so caller must not sleep, and is advised to
20895ca6d73SMauro Carvalho Chehabrelease the hwspinlock as soon as possible.
20995ca6d73SMauro Carvalho Chehab
21095ca6d73SMauro Carvalho ChehabReturns 0 on success and an appropriate error code otherwise (most
21195ca6d73SMauro Carvalho Chehabnotably -EBUSY if the hwspinlock was already taken).
21295ca6d73SMauro Carvalho Chehab
21395ca6d73SMauro Carvalho ChehabThe function will never sleep.
21495ca6d73SMauro Carvalho Chehab
21595ca6d73SMauro Carvalho Chehab::
21695ca6d73SMauro Carvalho Chehab
21795ca6d73SMauro Carvalho Chehab  int hwspin_trylock_irqsave(struct hwspinlock *hwlock, unsigned long *flags);
21895ca6d73SMauro Carvalho Chehab
21995ca6d73SMauro Carvalho ChehabAttempt to lock a previously-assigned hwspinlock, but immediately fail if
22095ca6d73SMauro Carvalho Chehabit is already taken.
22195ca6d73SMauro Carvalho Chehab
22295ca6d73SMauro Carvalho ChehabUpon a successful return from this function, preemption is disabled,
22395ca6d73SMauro Carvalho Chehabthe local interrupts are disabled and their previous state is saved
22495ca6d73SMauro Carvalho Chehabat the given flags placeholder. The caller must not sleep, and is advised
22595ca6d73SMauro Carvalho Chehabto release the hwspinlock as soon as possible.
22695ca6d73SMauro Carvalho Chehab
22795ca6d73SMauro Carvalho ChehabReturns 0 on success and an appropriate error code otherwise (most
22895ca6d73SMauro Carvalho Chehabnotably -EBUSY if the hwspinlock was already taken).
22995ca6d73SMauro Carvalho ChehabThe function will never sleep.
23095ca6d73SMauro Carvalho Chehab
23195ca6d73SMauro Carvalho Chehab::
23295ca6d73SMauro Carvalho Chehab
23395ca6d73SMauro Carvalho Chehab  int hwspin_trylock_raw(struct hwspinlock *hwlock);
23495ca6d73SMauro Carvalho Chehab
23595ca6d73SMauro Carvalho ChehabAttempt to lock a previously-assigned hwspinlock, but immediately fail if
23695ca6d73SMauro Carvalho Chehabit is already taken.
23795ca6d73SMauro Carvalho Chehab
23895ca6d73SMauro Carvalho ChehabCaution: User must protect the routine of getting hardware lock with mutex
23995ca6d73SMauro Carvalho Chehabor spinlock to avoid dead-lock, that will let user can do some time-consuming
24095ca6d73SMauro Carvalho Chehabor sleepable operations under the hardware lock.
24195ca6d73SMauro Carvalho Chehab
24295ca6d73SMauro Carvalho ChehabReturns 0 on success and an appropriate error code otherwise (most
24395ca6d73SMauro Carvalho Chehabnotably -EBUSY if the hwspinlock was already taken).
24495ca6d73SMauro Carvalho ChehabThe function will never sleep.
24595ca6d73SMauro Carvalho Chehab
24695ca6d73SMauro Carvalho Chehab::
24795ca6d73SMauro Carvalho Chehab
24895ca6d73SMauro Carvalho Chehab  int hwspin_trylock_in_atomic(struct hwspinlock *hwlock);
24995ca6d73SMauro Carvalho Chehab
25095ca6d73SMauro Carvalho ChehabAttempt to lock a previously-assigned hwspinlock, but immediately fail if
25195ca6d73SMauro Carvalho Chehabit is already taken.
25295ca6d73SMauro Carvalho Chehab
25395ca6d73SMauro Carvalho ChehabThis function shall be called only from an atomic context.
25495ca6d73SMauro Carvalho Chehab
25595ca6d73SMauro Carvalho ChehabReturns 0 on success and an appropriate error code otherwise (most
25695ca6d73SMauro Carvalho Chehabnotably -EBUSY if the hwspinlock was already taken).
25795ca6d73SMauro Carvalho ChehabThe function will never sleep.
25895ca6d73SMauro Carvalho Chehab
25995ca6d73SMauro Carvalho Chehab::
26095ca6d73SMauro Carvalho Chehab
26195ca6d73SMauro Carvalho Chehab  void hwspin_unlock(struct hwspinlock *hwlock);
26295ca6d73SMauro Carvalho Chehab
26395ca6d73SMauro Carvalho ChehabUnlock a previously-locked hwspinlock. Always succeed, and can be called
26495ca6d73SMauro Carvalho Chehabfrom any context (the function never sleeps).
26595ca6d73SMauro Carvalho Chehab
26695ca6d73SMauro Carvalho Chehab.. note::
26795ca6d73SMauro Carvalho Chehab
26895ca6d73SMauro Carvalho Chehab  code should **never** unlock an hwspinlock which is already unlocked
26995ca6d73SMauro Carvalho Chehab  (there is no protection against this).
27095ca6d73SMauro Carvalho Chehab
27195ca6d73SMauro Carvalho Chehab::
27295ca6d73SMauro Carvalho Chehab
27395ca6d73SMauro Carvalho Chehab  void hwspin_unlock_irq(struct hwspinlock *hwlock);
27495ca6d73SMauro Carvalho Chehab
27595ca6d73SMauro Carvalho ChehabUnlock a previously-locked hwspinlock and enable local interrupts.
27695ca6d73SMauro Carvalho ChehabThe caller should **never** unlock an hwspinlock which is already unlocked.
27795ca6d73SMauro Carvalho Chehab
27895ca6d73SMauro Carvalho ChehabDoing so is considered a bug (there is no protection against this).
27995ca6d73SMauro Carvalho ChehabUpon a successful return from this function, preemption and local
28095ca6d73SMauro Carvalho Chehabinterrupts are enabled. This function will never sleep.
28195ca6d73SMauro Carvalho Chehab
28295ca6d73SMauro Carvalho Chehab::
28395ca6d73SMauro Carvalho Chehab
28495ca6d73SMauro Carvalho Chehab  void
28595ca6d73SMauro Carvalho Chehab  hwspin_unlock_irqrestore(struct hwspinlock *hwlock, unsigned long *flags);
28695ca6d73SMauro Carvalho Chehab
28795ca6d73SMauro Carvalho ChehabUnlock a previously-locked hwspinlock.
28895ca6d73SMauro Carvalho Chehab
28995ca6d73SMauro Carvalho ChehabThe caller should **never** unlock an hwspinlock which is already unlocked.
29095ca6d73SMauro Carvalho ChehabDoing so is considered a bug (there is no protection against this).
29195ca6d73SMauro Carvalho ChehabUpon a successful return from this function, preemption is reenabled,
29295ca6d73SMauro Carvalho Chehaband the state of the local interrupts is restored to the state saved at
29395ca6d73SMauro Carvalho Chehabthe given flags. This function will never sleep.
29495ca6d73SMauro Carvalho Chehab
29595ca6d73SMauro Carvalho Chehab::
29695ca6d73SMauro Carvalho Chehab
29795ca6d73SMauro Carvalho Chehab  void hwspin_unlock_raw(struct hwspinlock *hwlock);
29895ca6d73SMauro Carvalho Chehab
29995ca6d73SMauro Carvalho ChehabUnlock a previously-locked hwspinlock.
30095ca6d73SMauro Carvalho Chehab
30195ca6d73SMauro Carvalho ChehabThe caller should **never** unlock an hwspinlock which is already unlocked.
30295ca6d73SMauro Carvalho ChehabDoing so is considered a bug (there is no protection against this).
30395ca6d73SMauro Carvalho ChehabThis function will never sleep.
30495ca6d73SMauro Carvalho Chehab
30595ca6d73SMauro Carvalho Chehab::
30695ca6d73SMauro Carvalho Chehab
30795ca6d73SMauro Carvalho Chehab  void hwspin_unlock_in_atomic(struct hwspinlock *hwlock);
30895ca6d73SMauro Carvalho Chehab
30995ca6d73SMauro Carvalho ChehabUnlock a previously-locked hwspinlock.
31095ca6d73SMauro Carvalho Chehab
31195ca6d73SMauro Carvalho ChehabThe caller should **never** unlock an hwspinlock which is already unlocked.
31295ca6d73SMauro Carvalho ChehabDoing so is considered a bug (there is no protection against this).
31395ca6d73SMauro Carvalho ChehabThis function will never sleep.
31495ca6d73SMauro Carvalho Chehab
31595ca6d73SMauro Carvalho Chehab::
31695ca6d73SMauro Carvalho Chehab
31795ca6d73SMauro Carvalho Chehab  int hwspin_lock_get_id(struct hwspinlock *hwlock);
31895ca6d73SMauro Carvalho Chehab
31995ca6d73SMauro Carvalho ChehabRetrieve id number of a given hwspinlock. This is needed when an
32095ca6d73SMauro Carvalho Chehabhwspinlock is dynamically assigned: before it can be used to achieve
32195ca6d73SMauro Carvalho Chehabmutual exclusion with a remote cpu, the id number should be communicated
32295ca6d73SMauro Carvalho Chehabto the remote task with which we want to synchronize.
32395ca6d73SMauro Carvalho Chehab
32495ca6d73SMauro Carvalho ChehabReturns the hwspinlock id number, or -EINVAL if hwlock is null.
32595ca6d73SMauro Carvalho Chehab
32695ca6d73SMauro Carvalho ChehabTypical usage
32795ca6d73SMauro Carvalho Chehab=============
32895ca6d73SMauro Carvalho Chehab
32995ca6d73SMauro Carvalho Chehab::
33095ca6d73SMauro Carvalho Chehab
33195ca6d73SMauro Carvalho Chehab	#include <linux/hwspinlock.h>
33295ca6d73SMauro Carvalho Chehab	#include <linux/err.h>
33395ca6d73SMauro Carvalho Chehab
33495ca6d73SMauro Carvalho Chehab	int hwspinlock_example1(void)
33595ca6d73SMauro Carvalho Chehab	{
33695ca6d73SMauro Carvalho Chehab		struct hwspinlock *hwlock;
33795ca6d73SMauro Carvalho Chehab		int ret;
33895ca6d73SMauro Carvalho Chehab
33995ca6d73SMauro Carvalho Chehab		/* dynamically assign a hwspinlock */
34095ca6d73SMauro Carvalho Chehab		hwlock = hwspin_lock_request();
34195ca6d73SMauro Carvalho Chehab		if (!hwlock)
34295ca6d73SMauro Carvalho Chehab			...
34395ca6d73SMauro Carvalho Chehab
34495ca6d73SMauro Carvalho Chehab		id = hwspin_lock_get_id(hwlock);
34595ca6d73SMauro Carvalho Chehab		/* probably need to communicate id to a remote processor now */
34695ca6d73SMauro Carvalho Chehab
34795ca6d73SMauro Carvalho Chehab		/* take the lock, spin for 1 sec if it's already taken */
34895ca6d73SMauro Carvalho Chehab		ret = hwspin_lock_timeout(hwlock, 1000);
34995ca6d73SMauro Carvalho Chehab		if (ret)
35095ca6d73SMauro Carvalho Chehab			...
35195ca6d73SMauro Carvalho Chehab
35295ca6d73SMauro Carvalho Chehab		/*
35395ca6d73SMauro Carvalho Chehab		* we took the lock, do our thing now, but do NOT sleep
35495ca6d73SMauro Carvalho Chehab		*/
35595ca6d73SMauro Carvalho Chehab
35695ca6d73SMauro Carvalho Chehab		/* release the lock */
35795ca6d73SMauro Carvalho Chehab		hwspin_unlock(hwlock);
35895ca6d73SMauro Carvalho Chehab
35995ca6d73SMauro Carvalho Chehab		/* free the lock */
36095ca6d73SMauro Carvalho Chehab		ret = hwspin_lock_free(hwlock);
36195ca6d73SMauro Carvalho Chehab		if (ret)
36295ca6d73SMauro Carvalho Chehab			...
36395ca6d73SMauro Carvalho Chehab
36495ca6d73SMauro Carvalho Chehab		return ret;
36595ca6d73SMauro Carvalho Chehab	}
36695ca6d73SMauro Carvalho Chehab
36795ca6d73SMauro Carvalho Chehab	int hwspinlock_example2(void)
36895ca6d73SMauro Carvalho Chehab	{
36995ca6d73SMauro Carvalho Chehab		struct hwspinlock *hwlock;
37095ca6d73SMauro Carvalho Chehab		int ret;
37195ca6d73SMauro Carvalho Chehab
37295ca6d73SMauro Carvalho Chehab		/*
37395ca6d73SMauro Carvalho Chehab		* assign a specific hwspinlock id - this should be called early
37495ca6d73SMauro Carvalho Chehab		* by board init code.
37595ca6d73SMauro Carvalho Chehab		*/
37695ca6d73SMauro Carvalho Chehab		hwlock = hwspin_lock_request_specific(PREDEFINED_LOCK_ID);
37795ca6d73SMauro Carvalho Chehab		if (!hwlock)
37895ca6d73SMauro Carvalho Chehab			...
37995ca6d73SMauro Carvalho Chehab
38095ca6d73SMauro Carvalho Chehab		/* try to take it, but don't spin on it */
38195ca6d73SMauro Carvalho Chehab		ret = hwspin_trylock(hwlock);
38295ca6d73SMauro Carvalho Chehab		if (!ret) {
38395ca6d73SMauro Carvalho Chehab			pr_info("lock is already taken\n");
38495ca6d73SMauro Carvalho Chehab			return -EBUSY;
38595ca6d73SMauro Carvalho Chehab		}
38695ca6d73SMauro Carvalho Chehab
38795ca6d73SMauro Carvalho Chehab		/*
38895ca6d73SMauro Carvalho Chehab		* we took the lock, do our thing now, but do NOT sleep
38995ca6d73SMauro Carvalho Chehab		*/
39095ca6d73SMauro Carvalho Chehab
39195ca6d73SMauro Carvalho Chehab		/* release the lock */
39295ca6d73SMauro Carvalho Chehab		hwspin_unlock(hwlock);
39395ca6d73SMauro Carvalho Chehab
39495ca6d73SMauro Carvalho Chehab		/* free the lock */
39595ca6d73SMauro Carvalho Chehab		ret = hwspin_lock_free(hwlock);
39695ca6d73SMauro Carvalho Chehab		if (ret)
39795ca6d73SMauro Carvalho Chehab			...
39895ca6d73SMauro Carvalho Chehab
39995ca6d73SMauro Carvalho Chehab		return ret;
40095ca6d73SMauro Carvalho Chehab	}
40195ca6d73SMauro Carvalho Chehab
40295ca6d73SMauro Carvalho Chehab
40395ca6d73SMauro Carvalho ChehabAPI for implementors
40495ca6d73SMauro Carvalho Chehab====================
40595ca6d73SMauro Carvalho Chehab
40695ca6d73SMauro Carvalho Chehab::
40795ca6d73SMauro Carvalho Chehab
40895ca6d73SMauro Carvalho Chehab  int hwspin_lock_register(struct hwspinlock_device *bank, struct device *dev,
40995ca6d73SMauro Carvalho Chehab		const struct hwspinlock_ops *ops, int base_id, int num_locks);
41095ca6d73SMauro Carvalho Chehab
41195ca6d73SMauro Carvalho ChehabTo be called from the underlying platform-specific implementation, in
41295ca6d73SMauro Carvalho Chehaborder to register a new hwspinlock device (which is usually a bank of
41395ca6d73SMauro Carvalho Chehabnumerous locks). Should be called from a process context (this function
41495ca6d73SMauro Carvalho Chehabmight sleep).
41595ca6d73SMauro Carvalho Chehab
41695ca6d73SMauro Carvalho ChehabReturns 0 on success, or appropriate error code on failure.
41795ca6d73SMauro Carvalho Chehab
41895ca6d73SMauro Carvalho Chehab::
41995ca6d73SMauro Carvalho Chehab
42095ca6d73SMauro Carvalho Chehab  int hwspin_lock_unregister(struct hwspinlock_device *bank);
42195ca6d73SMauro Carvalho Chehab
42295ca6d73SMauro Carvalho ChehabTo be called from the underlying vendor-specific implementation, in order
42395ca6d73SMauro Carvalho Chehabto unregister an hwspinlock device (which is usually a bank of numerous
42495ca6d73SMauro Carvalho Chehablocks).
42595ca6d73SMauro Carvalho Chehab
42695ca6d73SMauro Carvalho ChehabShould be called from a process context (this function might sleep).
42795ca6d73SMauro Carvalho Chehab
42895ca6d73SMauro Carvalho ChehabReturns the address of hwspinlock on success, or NULL on error (e.g.
42995ca6d73SMauro Carvalho Chehabif the hwspinlock is still in use).
43095ca6d73SMauro Carvalho Chehab
43195ca6d73SMauro Carvalho ChehabImportant structs
43295ca6d73SMauro Carvalho Chehab=================
43395ca6d73SMauro Carvalho Chehab
43495ca6d73SMauro Carvalho Chehabstruct hwspinlock_device is a device which usually contains a bank
43595ca6d73SMauro Carvalho Chehabof hardware locks. It is registered by the underlying hwspinlock
43695ca6d73SMauro Carvalho Chehabimplementation using the hwspin_lock_register() API.
43795ca6d73SMauro Carvalho Chehab
43895ca6d73SMauro Carvalho Chehab::
43995ca6d73SMauro Carvalho Chehab
44095ca6d73SMauro Carvalho Chehab	/**
44195ca6d73SMauro Carvalho Chehab	* struct hwspinlock_device - a device which usually spans numerous hwspinlocks
44295ca6d73SMauro Carvalho Chehab	* @dev: underlying device, will be used to invoke runtime PM api
44395ca6d73SMauro Carvalho Chehab	* @ops: platform-specific hwspinlock handlers
44495ca6d73SMauro Carvalho Chehab	* @base_id: id index of the first lock in this device
44595ca6d73SMauro Carvalho Chehab	* @num_locks: number of locks in this device
44695ca6d73SMauro Carvalho Chehab	* @lock: dynamically allocated array of 'struct hwspinlock'
44795ca6d73SMauro Carvalho Chehab	*/
44895ca6d73SMauro Carvalho Chehab	struct hwspinlock_device {
44995ca6d73SMauro Carvalho Chehab		struct device *dev;
45095ca6d73SMauro Carvalho Chehab		const struct hwspinlock_ops *ops;
45195ca6d73SMauro Carvalho Chehab		int base_id;
45295ca6d73SMauro Carvalho Chehab		int num_locks;
45395ca6d73SMauro Carvalho Chehab		struct hwspinlock lock[0];
45495ca6d73SMauro Carvalho Chehab	};
45595ca6d73SMauro Carvalho Chehab
45695ca6d73SMauro Carvalho Chehabstruct hwspinlock_device contains an array of hwspinlock structs, each
45795ca6d73SMauro Carvalho Chehabof which represents a single hardware lock::
45895ca6d73SMauro Carvalho Chehab
45995ca6d73SMauro Carvalho Chehab	/**
46095ca6d73SMauro Carvalho Chehab	* struct hwspinlock - this struct represents a single hwspinlock instance
46195ca6d73SMauro Carvalho Chehab	* @bank: the hwspinlock_device structure which owns this lock
46295ca6d73SMauro Carvalho Chehab	* @lock: initialized and used by hwspinlock core
46395ca6d73SMauro Carvalho Chehab	* @priv: private data, owned by the underlying platform-specific hwspinlock drv
46495ca6d73SMauro Carvalho Chehab	*/
46595ca6d73SMauro Carvalho Chehab	struct hwspinlock {
46695ca6d73SMauro Carvalho Chehab		struct hwspinlock_device *bank;
46795ca6d73SMauro Carvalho Chehab		spinlock_t lock;
46895ca6d73SMauro Carvalho Chehab		void *priv;
46995ca6d73SMauro Carvalho Chehab	};
47095ca6d73SMauro Carvalho Chehab
47195ca6d73SMauro Carvalho ChehabWhen registering a bank of locks, the hwspinlock driver only needs to
47295ca6d73SMauro Carvalho Chehabset the priv members of the locks. The rest of the members are set and
47395ca6d73SMauro Carvalho Chehabinitialized by the hwspinlock core itself.
47495ca6d73SMauro Carvalho Chehab
47595ca6d73SMauro Carvalho ChehabImplementation callbacks
47695ca6d73SMauro Carvalho Chehab========================
47795ca6d73SMauro Carvalho Chehab
47895ca6d73SMauro Carvalho ChehabThere are three possible callbacks defined in 'struct hwspinlock_ops'::
47995ca6d73SMauro Carvalho Chehab
48095ca6d73SMauro Carvalho Chehab	struct hwspinlock_ops {
48195ca6d73SMauro Carvalho Chehab		int (*trylock)(struct hwspinlock *lock);
48295ca6d73SMauro Carvalho Chehab		void (*unlock)(struct hwspinlock *lock);
48395ca6d73SMauro Carvalho Chehab		void (*relax)(struct hwspinlock *lock);
48495ca6d73SMauro Carvalho Chehab	};
48595ca6d73SMauro Carvalho Chehab
48695ca6d73SMauro Carvalho ChehabThe first two callbacks are mandatory:
48795ca6d73SMauro Carvalho Chehab
48895ca6d73SMauro Carvalho ChehabThe ->trylock() callback should make a single attempt to take the lock, and
48995ca6d73SMauro Carvalho Chehabreturn 0 on failure and 1 on success. This callback may **not** sleep.
49095ca6d73SMauro Carvalho Chehab
49195ca6d73SMauro Carvalho ChehabThe ->unlock() callback releases the lock. It always succeed, and it, too,
49295ca6d73SMauro Carvalho Chehabmay **not** sleep.
49395ca6d73SMauro Carvalho Chehab
49495ca6d73SMauro Carvalho ChehabThe ->relax() callback is optional. It is called by hwspinlock core while
49595ca6d73SMauro Carvalho Chehabspinning on a lock, and can be used by the underlying implementation to force
49695ca6d73SMauro Carvalho Chehaba delay between two successive invocations of ->trylock(). It may **not** sleep.
497