Linux: Add support for the hwspinlock_user driver
[ipc/ipcdev.git] / linux / src / api / gates / GateHWSpinlock.c
1 /*
2  * Copyright (c) 2013-2014, Texas Instruments Incorporated
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *
9  * *  Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  *
12  * *  Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * *  Neither the name of Texas Instruments Incorporated nor the names of
17  *    its contributors may be used to endorse or promote products derived
18  *    from this software without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
22  * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
23  * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
24  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
27  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
28  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
29  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
30  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31  */
33 /*
34  *  ======== GateHWSpinlock.c ========
35  */
37 /* Standard headers */
38 #include <ti/ipc/Std.h>
40 /* Utilities & OSAL headers */
41 #include <ti/ipc/MultiProc.h>
42 #include <ti/ipc/GateMP.h>
44 #include <GateHWSpinlock.h>
46 #include <IGateProvider.h>
48 /*
49  * TODO: does this belong in ti/ipc/Std.h? We should consider getting rid of
50  *       error blocks from the GateMutex.h interface.
51  */
52 typedef UInt32            Error_Block;
53 #include <GateMutex.h>
55 /* Socket Utils */
56 #include <_lad.h>
57 #include <ladclient.h>
59 /* Linux headers */
60 #include <assert.h>
61 #include <fcntl.h>
62 #include <string.h>
63 #include <stdlib.h>
64 #include <sys/mman.h>
65 #include <sys/stat.h>
67 #include <linux/ioctl.h>
68 #include <linux/hwspinlock_user.h>
70 /* =============================================================================
71  * Structures & Enums
72  * =============================================================================
73  */
74 /* GateHWSpinlock Module Local State */
75 typedef struct {
76     Int32                           fd;         /* spinlock device handle */
77     UInt32 *                        baseAddr;   /* base addr lock registers */
78     GateMutex_Handle                gmHandle;   /* handle to gate mutex */
79     Bool                            useHwlockDrv; /* use the hwspinlock driver */
80 } GateHWSpinlock_Module_State;
82 /* GateHWSpinlock instance object */
83 struct GateHWSpinlock_Object {
84     IGateProvider_SuperObject; /* For inheritance from IGateProvider */
85     UInt                        lockNum;
86     UInt                        nested;
87     IGateProvider_Handle        localGate;
88     int                         token;  /* HWSpinlock token */
89 };
92 /* =============================================================================
93  * Globals
94  * =============================================================================
95  */
96 GateHWSpinlock_Config _GateHWSpinlock_cfgParams;
98 static GateHWSpinlock_Module_State GateHWSpinlock_state =
99 {
100     .fd = -1,
101     .baseAddr = NULL,
102     .gmHandle = NULL,
103     .useHwlockDrv = false,
104 };
106 static GateHWSpinlock_Module_State *Mod = &GateHWSpinlock_state;
108 static GateHWSpinlock_Params GateHWSpinlock_defInstParams =
110     .resourceId = 0,
111     .openFlag   = FALSE,
112     .regionId   = 0,
113     .sharedAddr = NULL
114 };
116 /* traces in this file are controlled via _GateHWSpinlock_verbose */
117 Bool _GateHWSpinlock_verbose = FALSE;
118 #define verbose _GateHWSpinlock_verbose
120 /* =============================================================================
121  * APIS
122  * =============================================================================
123  */
124 /* Function to get configuration address & sizes for the GateHWSpinlock module.
125  *
126  */
127 Void GateHWSpinlock_getConfig (GateHWSpinlock_Config * cfgParams)
129     Int status;
130     LAD_ClientHandle handle;
131     struct LAD_CommandObj cmd;
132     union LAD_ResponseObj rsp;
134     assert (cfgParams != NULL);
136     handle = LAD_findHandle();
137     if (handle == LAD_MAXNUMCLIENTS) {
138         PRINTVERBOSE0(
139           "GateHWSpinlock_getConfig: can't find connection to daemon for pid")
140         PRINTVERBOSE1("%d\n", getpid())
142         return;
143     }
145     cmd.cmd = LAD_GATEHWSPINLOCK_GETCONFIG;
146     cmd.clientId = handle;
148     if ((status = LAD_putCommand(&cmd)) != LAD_SUCCESS) {
149         PRINTVERBOSE1(
150           "GateHWSpinlock_getConfig: sending LAD command failed, status=%d\n",
151           status)
152         return;
153     }
155     if ((status = LAD_getResponse(handle, &rsp)) != LAD_SUCCESS) {
156         PRINTVERBOSE1("GateHWSpinlock_getConfig: no LAD response, status=%d\n",
157         status)
158         return;
159     }
160     status = rsp.gateHWSpinlockGetConfig.status;
162     PRINTVERBOSE2(
163       "GateHWSpinlock_getConfig: got LAD response for client %d, status=%d\n",
164       handle, status)
166     memcpy(cfgParams, &rsp.gateHWSpinlockGetConfig.cfgParams,
167         sizeof(*cfgParams));
169     return;
173 /*
174  *  Function to start the GateHWSpinlock module.
175  */
176 Int32 GateHWSpinlock_start(Void)
178     Int32               status = GateHWSpinlock_S_SUCCESS;
179     UInt32              dst;
180     int                 flags;
182     /* Fall back to /dev/mem if hwspinlock_user driver is not supported */
183     Mod->fd = open("/dev/hwspinlock", O_RDWR);
184     if (Mod->fd < 0) {
185         Mod->fd = open ("/dev/mem", O_RDWR | O_SYNC);
186     }
187     else {
188         Mod->useHwlockDrv = true;
189     }
191     if (Mod->fd < 0){
192         PRINTVERBOSE0("GateHWSpinlock_start: failed to open the spinlock device");
193         status = GateHWSpinlock_E_OSFAILURE;
194     }
196     if (!Mod->useHwlockDrv) {
197         /* make sure /dev/mem fd doesn't exist for 'fork() -> exec*()'ed child */
198         flags = fcntl(Mod->fd, F_GETFD);
199         if (flags != -1) {
200             fcntl(Mod->fd, F_SETFD, flags | FD_CLOEXEC);
201         }
203         /* map the hardware lock registers into the local address space */
204         if (status == GateHWSpinlock_S_SUCCESS) {
205             dst = (UInt32)mmap(NULL, _GateHWSpinlock_cfgParams.size,
206                                (PROT_READ | PROT_WRITE),
207                                (MAP_SHARED), Mod->fd,
208                                (off_t)_GateHWSpinlock_cfgParams.baseAddr);
210             if (dst == (UInt32)MAP_FAILED) {
211                 PRINTVERBOSE0("GateHWSpinlock_start: Memory map failed")
212                     status = GateHWSpinlock_E_OSFAILURE;
213                 close(Mod->fd);
214                 Mod->fd = -1;
215             }
216             else {
217                 Mod->baseAddr = (UInt32 *)(dst + _GateHWSpinlock_cfgParams.offset);
218                 status = GateHWSpinlock_S_SUCCESS;
219             }
220         }
221     }
223     /* create GateMutex for local protection*/
224     if (status == GateHWSpinlock_S_SUCCESS) {
225         Mod->gmHandle = GateMutex_create(NULL, NULL);
227         if (Mod->gmHandle == NULL) {
228             PRINTVERBOSE0("GateHWSpinlock_start: GateMutex create failed")
229             status = GateHWSpinlock_E_FAIL;
230             GateHWSpinlock_stop();
231         }
232     }
234     return (status);
237 /*
238  *  Function to stop the GateHWSpinlock module.
239  */
240 Int GateHWSpinlock_stop(Void)
242     Int32               status = GateHWSpinlock_S_SUCCESS;
244     /* delete GateMutex */
245     if (Mod->gmHandle != NULL) {
246         status = GateMutex_delete(&Mod->gmHandle);
247     }
249     /* release lock register mapping */
250     if (!Mod->useHwlockDrv && (Mod->baseAddr != NULL)) {
251         munmap((void *)_GateHWSpinlock_cfgParams.baseAddr,
252            _GateHWSpinlock_cfgParams.size);
253     }
255     /* close the spinlock device file */
256     if (Mod->fd >= 0) {
257         close(Mod->fd);
258         Mod->fd = -1;
259     }
261     return(status);
264 /*
265  *  Initialize parameter structure
266  */
267 Void GateHWSpinlock_Params_init(GateHWSpinlock_Params *params)
269     assert(params != NULL);
271     memcpy(params, &GateHWSpinlock_defInstParams,
272         sizeof(GateHWSpinlock_Params));
275 /*
276  * Create a GateHWSpinlock instance
277  */
278 /* TODO: change the function to accept a local gate. Do this on all platforms */
279 GateHWSpinlock_Handle GateHWSpinlock_create(GateHWSpinlock_LocalProtect
280     localProtect, const GateHWSpinlock_Params * params)
282     GateHWSpinlock_Object * obj = (GateHWSpinlock_Object *)calloc(1,
283         sizeof (GateHWSpinlock_Object));
285     if (!obj) {
286         PRINTVERBOSE0("GateHWSpinlock_create: memory allocation failure")
287         return NULL;
288     }
290     IGateProvider_ObjectInitializer(obj, GateHWSpinlock);
291     /* TODO: handle more local protection types */
292     obj->localGate = (IGateProvider_Handle)Mod->gmHandle;
293     obj->lockNum = params->resourceId;
294     obj->nested = 0;
296     return (GateHWSpinlock_Handle)obj;
299 /*
300  * Delete a GateHWSpinlock instance
301  */
302 Int GateHWSpinlock_delete (GateHWSpinlock_Handle * handle)
304     GateHWSpinlock_Object * obj;
305     Int  status = GateHWSpinlock_S_SUCCESS;
307     if (handle == NULL) {
308         return GateHWSpinlock_E_INVALIDARG;
309     }
310     if (*handle == NULL) {
311         return GateHWSpinlock_E_INVALIDARG;
312     }
314     obj = (GateHWSpinlock_Object *)(*handle);
316     free(obj);
317     *handle = NULL;
319     return (status);
322 /*
323  *  Enter a GateHWSpinlock instance
324  */
325 IArg GateHWSpinlock_enter(GateHWSpinlock_Object *obj)
327     volatile UInt32 *baseAddr = Mod->baseAddr;
328     struct hwspinlock_user_lock data = {
329         .id = obj->lockNum,
330         .timeout = 10,
331     };
332     IArg key;
333     Bool locked;
335     key = IGateProvider_enter(obj->localGate);
337     /* if gate already entered, just return with current key */
338     obj->nested++;
339     if (obj->nested > 1) {
340         return(key);
341     }
343     /* enter the spinlock */
344     while (1) {
345         if (Mod->useHwlockDrv) {
346             locked = !ioctl(Mod->fd, HWSPINLOCK_USER_LOCK, &data);
347         }
348         else {
349             /* read the spinlock, returns non-zero when we get it */
350             locked = (baseAddr[obj->lockNum] == 0);
351         }
353         if (locked) {
354             break;
355         }
357         obj->nested--;
358         IGateProvider_leave(obj->localGate, key);
359         key = IGateProvider_enter(obj->localGate);
360         obj->nested++; /* re-nest the gate */
361     }
363     return (key);
366 /*
367  *  Leave a GateHWSpinlock instance
368  */
369 Int GateHWSpinlock_leave(GateHWSpinlock_Object *obj, IArg key)
371     volatile UInt32 *baseAddr = Mod->baseAddr;
372     struct hwspinlock_user_unlock data = {
373         .id = obj->lockNum,
374     };
376     obj->nested--;
378     /* release the spinlock if not nested */
379     if (obj->nested == 0) {
380         if (Mod->useHwlockDrv) {
381             ioctl(Mod->fd, HWSPINLOCK_USER_UNLOCK, &data);
382         }
383         else {
384             baseAddr[obj->lockNum] = 0;
385         }
386     }
388     IGateProvider_leave(obj->localGate, key);
390     return GateHWSpinlock_S_SUCCESS;