Linux: Add support for the hwspinlock_user driver
authorMisael Lopez Cruz <misael.lopez@ti.com>
Fri, 18 Dec 2015 21:50:52 +0000 (15:50 -0600)
committerAngela Stegmaier <angelabaker@ti.com>
Mon, 21 Dec 2015 22:05:04 +0000 (16:05 -0600)
The hwspinlock_user is a character driver that exposes ioctls to
lock and unlock a given hwspinlock instance.  The hwspinlock_user
creates the /dev/hwspinlock device file.

The hwspinlock_user driver is used as the first option when available,
otherwise the previous /dev/mem approach is used.

The hwspinlock_user.h uapi header file is kept locally as it may not
be present in all supported kernels.

Signed-off-by: Misael Lopez Cruz <misael.lopez@ti.com>
linux/include/linux/hwspinlock_user.h [new file with mode: 0644]
linux/src/api/gates/GateHWSpinlock.c
linux/src/daemon/GateHWSpinlock.c

diff --git a/linux/include/linux/hwspinlock_user.h b/linux/include/linux/hwspinlock_user.h
new file mode 100644 (file)
index 0000000..68f3dff
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Userspace interface to hardware spinlocks
+ *
+ * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in
+ *   the documentation and/or other materials provided with the
+ *   distribution.
+ * * Neither the name Texas Instruments nor the names of its
+ *   contributors may be used to endorse or promote products derived
+ *   from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _UAPI_HWSPINLOCK_USER_H
+#define _UAPI_HWSPINLOCK_USER_H
+
+#include <linux/ioctl.h>
+
+/**
+ * struct hwspinlock_user_lock - Sent to lock a specific hwspinlock
+ * @id:                hardware spinlock id
+ * @timeout:   timeout value in msecs
+ */
+struct hwspinlock_user_lock {
+       int id;
+       unsigned int timeout;
+};
+
+/**
+ * struct hwspinlock_user_unlock - Sent to unlock a specific hwspinlock
+ * @id:                hardware spinlock id
+ */
+struct hwspinlock_user_unlock {
+       int id;
+};
+
+#define HWSPINLOCK_IOC_MAGIC           'h'
+
+#define HWSPINLOCK_USER_LOCK           _IOW(HWSPINLOCK_IOC_MAGIC, 0,   \
+                                            struct hwspinlock_user_lock)
+#define HWSPINLOCK_USER_UNLOCK         _IOW(HWSPINLOCK_IOC_MAGIC, 1,   \
+                                            struct hwspinlock_user_unlock)
+
+#endif  /* _UAPI_HWSPINLOCK_USER_H */
index 142f3e1823094f2c76818032a62c0f62eb64fed2..64fa93a40d2f665df5fc128d5cc9092a7a42be1b 100644 (file)
@@ -64,6 +64,9 @@ typedef UInt32            Error_Block;
 #include <sys/mman.h>
 #include <sys/stat.h>
 
+#include <linux/ioctl.h>
+#include <linux/hwspinlock_user.h>
+
 /* =============================================================================
  * Structures & Enums
  * =============================================================================
@@ -73,6 +76,7 @@ typedef struct {
     Int32                           fd;         /* spinlock device handle */
     UInt32 *                        baseAddr;   /* base addr lock registers */
     GateMutex_Handle                gmHandle;   /* handle to gate mutex */
+    Bool                            useHwlockDrv; /* use the hwspinlock driver */
 } GateHWSpinlock_Module_State;
 
 /* GateHWSpinlock instance object */
@@ -95,7 +99,8 @@ static GateHWSpinlock_Module_State GateHWSpinlock_state =
 {
     .fd = -1,
     .baseAddr = NULL,
-    .gmHandle = NULL
+    .gmHandle = NULL,
+    .useHwlockDrv = false,
 };
 
 static GateHWSpinlock_Module_State *Mod = &GateHWSpinlock_state;
@@ -174,34 +179,44 @@ Int32 GateHWSpinlock_start(Void)
     UInt32              dst;
     int                 flags;
 
-    Mod->fd = open ("/dev/mem", O_RDWR | O_SYNC);
-    if (Mod->fd < 0){
-        PRINTVERBOSE0("GateHWSpinlock_start: failed to open the /dev/mem");
-        status = GateHWSpinlock_E_OSFAILURE;
+    /* Fall back to /dev/mem if hwspinlock_user driver is not supported */
+    Mod->fd = open("/dev/hwspinlock", O_RDWR);
+    if (Mod->fd < 0) {
+        Mod->fd = open ("/dev/mem", O_RDWR | O_SYNC);
+    }
+    else {
+        Mod->useHwlockDrv = true;
     }
 
-    /* make sure /dev/mem fd doesn't exist for 'fork() -> exec*()'ed child */
-    flags = fcntl(Mod->fd, F_GETFD);
-    if (flags != -1) {
-        fcntl(Mod->fd, F_SETFD, flags | FD_CLOEXEC);
+    if (Mod->fd < 0){
+        PRINTVERBOSE0("GateHWSpinlock_start: failed to open the spinlock device");
+        status = GateHWSpinlock_E_OSFAILURE;
     }
 
-    /* map the hardware lock registers into the local address space */
-    if (status == GateHWSpinlock_S_SUCCESS) {
-        dst = (UInt32)mmap(NULL, _GateHWSpinlock_cfgParams.size,
-                            (PROT_READ | PROT_WRITE),
-                            (MAP_SHARED), Mod->fd,
-                            (off_t)_GateHWSpinlock_cfgParams.baseAddr);
-
-        if (dst == (UInt32)MAP_FAILED) {
-            PRINTVERBOSE0("GateHWSpinlock_start: Memory map failed")
-            status = GateHWSpinlock_E_OSFAILURE;
-            close(Mod->fd);
-            Mod->fd = -1;
+    if (!Mod->useHwlockDrv) {
+        /* make sure /dev/mem fd doesn't exist for 'fork() -> exec*()'ed child */
+        flags = fcntl(Mod->fd, F_GETFD);
+        if (flags != -1) {
+            fcntl(Mod->fd, F_SETFD, flags | FD_CLOEXEC);
         }
-        else {
-            Mod->baseAddr = (UInt32 *)(dst + _GateHWSpinlock_cfgParams.offset);
-            status = GateHWSpinlock_S_SUCCESS;
+
+        /* map the hardware lock registers into the local address space */
+        if (status == GateHWSpinlock_S_SUCCESS) {
+            dst = (UInt32)mmap(NULL, _GateHWSpinlock_cfgParams.size,
+                               (PROT_READ | PROT_WRITE),
+                               (MAP_SHARED), Mod->fd,
+                               (off_t)_GateHWSpinlock_cfgParams.baseAddr);
+
+            if (dst == (UInt32)MAP_FAILED) {
+                PRINTVERBOSE0("GateHWSpinlock_start: Memory map failed")
+                    status = GateHWSpinlock_E_OSFAILURE;
+                close(Mod->fd);
+                Mod->fd = -1;
+            }
+            else {
+                Mod->baseAddr = (UInt32 *)(dst + _GateHWSpinlock_cfgParams.offset);
+                status = GateHWSpinlock_S_SUCCESS;
+            }
         }
     }
 
@@ -232,7 +247,7 @@ Int GateHWSpinlock_stop(Void)
     }
 
     /* release lock register mapping */
-    if (Mod->baseAddr != NULL) {
+    if (!Mod->useHwlockDrv && (Mod->baseAddr != NULL)) {
         munmap((void *)_GateHWSpinlock_cfgParams.baseAddr,
            _GateHWSpinlock_cfgParams.size);
     }
@@ -310,7 +325,12 @@ Int GateHWSpinlock_delete (GateHWSpinlock_Handle * handle)
 IArg GateHWSpinlock_enter(GateHWSpinlock_Object *obj)
 {
     volatile UInt32 *baseAddr = Mod->baseAddr;
+    struct hwspinlock_user_lock data = {
+        .id = obj->lockNum,
+        .timeout = 10,
+    };
     IArg key;
+    Bool locked;
 
     key = IGateProvider_enter(obj->localGate);
 
@@ -322,10 +342,18 @@ IArg GateHWSpinlock_enter(GateHWSpinlock_Object *obj)
 
     /* enter the spinlock */
     while (1) {
-        /* read the spinlock, returns non-zero when we get it */
-        if (baseAddr[obj->lockNum] == 0) {
+        if (Mod->useHwlockDrv) {
+            locked = !ioctl(Mod->fd, HWSPINLOCK_USER_LOCK, &data);
+        }
+        else {
+            /* read the spinlock, returns non-zero when we get it */
+            locked = (baseAddr[obj->lockNum] == 0);
+        }
+
+        if (locked) {
             break;
         }
+
         obj->nested--;
         IGateProvider_leave(obj->localGate, key);
         key = IGateProvider_enter(obj->localGate);
@@ -341,12 +369,20 @@ IArg GateHWSpinlock_enter(GateHWSpinlock_Object *obj)
 Int GateHWSpinlock_leave(GateHWSpinlock_Object *obj, IArg key)
 {
     volatile UInt32 *baseAddr = Mod->baseAddr;
+    struct hwspinlock_user_unlock data = {
+        .id = obj->lockNum,
+    };
 
     obj->nested--;
 
     /* release the spinlock if not nested */
     if (obj->nested == 0) {
-        baseAddr[obj->lockNum] = 0;
+        if (Mod->useHwlockDrv) {
+            ioctl(Mod->fd, HWSPINLOCK_USER_UNLOCK, &data);
+        }
+        else {
+            baseAddr[obj->lockNum] = 0;
+        }
     }
 
     IGateProvider_leave(obj->localGate, key);
index 9ea4d09877fb6648ee8ae558952b8eb449f09945..391b6f611a9e9034a6b82d16f5ce1be423ac884b 100644 (file)
@@ -63,6 +63,9 @@ typedef UInt32            Error_Block;
 #include <sys/mman.h>
 #include <sys/stat.h>
 
+#include <linux/ioctl.h>
+#include <linux/hwspinlock_user.h>
+
 /* =============================================================================
  * Structures & Enums
  * =============================================================================
@@ -72,6 +75,7 @@ typedef struct {
     Int32                           fd;         /* spinlock device handle */
     UInt32 *                        baseAddr;   /* base addr lock registers */
     GateMutex_Handle                gmHandle;   /* handle to gate mutex */
+    Bool                            useHwlockDrv; /* use the hwspinlock driver */
 } GateHWSpinlock_Module_State;
 
 /* GateHWSpinlock instance object */
@@ -94,7 +98,8 @@ static GateHWSpinlock_Module_State GateHWSpinlock_state =
 {
     .fd = -1,
     .baseAddr = NULL,
-    .gmHandle = NULL
+    .gmHandle = NULL,
+    .useHwlockDrv = false,
 };
 
 static GateHWSpinlock_Module_State *Mod = &GateHWSpinlock_state;
@@ -119,14 +124,22 @@ Int32 GateHWSpinlock_start(Void)
     Int32               status = GateHWSpinlock_S_SUCCESS;
     UInt32              dst;
 
-    Mod->fd = open ("/dev/mem", O_RDWR | O_SYNC);
+    /* Fall back to /dev/mem if hwspinlock_user driver is not supported */
+    Mod->fd = open("/dev/hwspinlock", O_RDWR);
+    if (Mod->fd < 0) {
+        Mod->fd = open ("/dev/mem", O_RDWR | O_SYNC);
+    }
+    else {
+        Mod->useHwlockDrv = true;
+    }
+
     if (Mod->fd < 0){
-        LOG0("GateHWSpinlock_start: failed to open the /dev/mem");
+        LOG0("GateHWSpinlock_start: failed to open the spinlock device");
         status = GateHWSpinlock_E_OSFAILURE;
     }
 
     /* map the hardware lock registers into the local address space */
-    if (status == GateHWSpinlock_S_SUCCESS) {
+    if (!Mod->useHwlockDrv && status == GateHWSpinlock_S_SUCCESS) {
         dst = (UInt32)mmap(NULL, _GateHWSpinlock_cfgParams.size,
                             (PROT_READ | PROT_WRITE),
                             (MAP_SHARED), Mod->fd,
@@ -171,7 +184,7 @@ Int GateHWSpinlock_stop(Void)
     }
 
     /* release lock register mapping */
-    if (Mod->baseAddr != NULL) {
+    if (!Mod->useHwlockDrv && (Mod->baseAddr != NULL)) {
         munmap((void *)_GateHWSpinlock_cfgParams.baseAddr,
            _GateHWSpinlock_cfgParams.size);
     }
@@ -249,7 +262,12 @@ Int GateHWSpinlock_delete (GateHWSpinlock_Handle * handle)
 IArg GateHWSpinlock_enter(GateHWSpinlock_Object *obj)
 {
     volatile UInt32 *baseAddr = Mod->baseAddr;
+    struct hwspinlock_user_lock data = {
+        .id = obj->lockNum,
+        .timeout = 10,
+    };
     IArg key;
+    Bool locked;
 
     key = IGateProvider_enter(obj->localGate);
 
@@ -261,10 +279,18 @@ IArg GateHWSpinlock_enter(GateHWSpinlock_Object *obj)
 
     /* enter the spinlock */
     while (1) {
-        /* read the spinlock, returns non-zero when we get it */
-        if (baseAddr[obj->lockNum] == 0) {
+        if (Mod->useHwlockDrv) {
+            locked = !ioctl(Mod->fd, HWSPINLOCK_USER_LOCK, &data);
+        }
+        else {
+            /* read the spinlock, returns non-zero when we get it */
+            locked = (baseAddr[obj->lockNum] == 0);
+        }
+
+        if (locked) {
             break;
         }
+
         obj->nested--;
         IGateProvider_leave(obj->localGate, key);
         key = IGateProvider_enter(obj->localGate);
@@ -280,12 +306,20 @@ IArg GateHWSpinlock_enter(GateHWSpinlock_Object *obj)
 Int GateHWSpinlock_leave(GateHWSpinlock_Object *obj, IArg key)
 {
     volatile UInt32 *baseAddr = Mod->baseAddr;
+    struct hwspinlock_user_unlock data = {
+        .id = obj->lockNum,
+    };
 
     obj->nested--;
 
     /* release the spinlock if not nested */
     if (obj->nested == 0) {
-        baseAddr[obj->lockNum] = 0;
+        if (Mod->useHwlockDrv) {
+            ioctl(Mod->fd, HWSPINLOCK_USER_UNLOCK, &data);
+        }
+        else {
+            baseAddr[obj->lockNum] = 0;
+        }
     }
 
     IGateProvider_leave(obj->localGate, key);