summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAshutosh Patel <ashutoshp@nvidia.com>2013-01-30 12:08:13 +0530
committerDan Willemsen <dwillemsen@nvidia.com>2013-09-14 12:51:36 -0700
commit81054cdbcde969876f60ce6add1d68dcf9fa9c1e (patch)
tree9c1ffe827a79dd68e7b5677b672c44d1641f0167
parentc36bc7d5995c98087fa37c8893ee77f8b23f2424 (diff)
chardev: Access control framework for Tegra gmi bus.
-provide exclusive access to gmi bus. -provide priority based access. -support Asynchronous & synchronous requests. bug 1047323 Change-Id: I0a630a9120e5a2abd3b0ff6b1e3250005b1136e1 Signed-off-by: Nitin Sehgal <nsehgal@nvidia.com> Signed-off-by: Ashutosh Patel <ashutoshp@nvidia.com> Reviewed-on: http://git-master/r/189918 Reviewed-by: Automatic_Commit_Validation_User Reviewed-by: Laxman Dewangan <ldewangan@nvidia.com>
-rw-r--r--drivers/char/Kconfig8
-rw-r--r--drivers/char/Makefile1
-rw-r--r--drivers/char/tegra_gmi_access.c274
-rw-r--r--drivers/mtd/maps/tegra_nor_gmi.c2
-rw-r--r--include/linux/tegra_gmi_access.h27
5 files changed, 311 insertions, 1 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 896601e27869..4392d9143b5c 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -629,6 +629,14 @@ config TEGRA_EFS
help
Enable tegra efs helper driver for Nvidia's board.
+config TEGRA_GMI_ACCESS_CONTROL
+ bool "Provides access control functionality for multiple devices connected on tegra GMI bus."
+ depends on ARCH_TEGRA
+ ---help---
+ This driver provides access control and priority support for
+ tegra GMI bus. Nvidia Embedded entertainment boards support multiple
+ devices connected on the same bus selectable using CS.
+
config TEGRA_GMI_CHAR
bool "Character-device interface for tegra GMI bus"
depends on ARCH_TEGRA
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 31b7e9df83d8..8b0415dd3870 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -65,3 +65,4 @@ js-rtc-y = rtc.o
obj-$(CONFIG_TILE_SROM) += tile-srom.o
obj-$(CONFIG_TEGRA_EFS) += tegra-efshlp.o
obj-$(CONFIG_TEGRA_GMI_CHAR) += tegra_gmi_char.o
+obj-$(CONFIG_TEGRA_GMI_ACCESS_CONTROL) += tegra_gmi_access.o
diff --git a/drivers/char/tegra_gmi_access.c b/drivers/char/tegra_gmi_access.c
new file mode 100644
index 000000000000..084b75df12e5
--- /dev/null
+++ b/drivers/char/tegra_gmi_access.c
@@ -0,0 +1,274 @@
+/*
+ * drivers/char/tegra_gmi_access.c
+ *
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <linux/wait.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/semaphore.h>
+#include <linux/rwlock_types.h>
+#include <linux/io.h>
+#include <linux/tegra_gmi_access.h>
+#include <linux/proc_fs.h>
+#include <linux/module.h>
+#include <mach/iomap.h>
+#include <linux/fs.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+/*Data Structures*/
+#define GMI_MAX_PRIO 4
+#define GMI_MAX_DEVICES 6
+#define GMI_HANDLE_SIGNATURE 0xABABABAB
+
+static int gmi_init(void);
+
+struct gmi_device {
+ u32 signature;
+ char *dev_name;
+ u32 priority;
+ u32 dev_index;
+};
+
+/* Async Request Queue Access API's */
+struct gmi_async_req_queue_head_t {
+ spinlock_t lock;
+ struct list_head async_req_list;
+};
+
+struct gmi_async_req {
+ struct list_head async_req_list;
+ void *priv_data;
+ gasync_callbackp cb;
+};
+
+struct gmi_driver {
+ struct gmi_device *gdev_list[GMI_MAX_DEVICES];
+ wait_queue_head_t waitq[GMI_MAX_PRIO];
+ /* single queue and is the highest prio*/
+ struct gmi_async_req_queue_head_t asyncq;
+ /* Thread to service async pending requests */
+ struct task_struct *athread;
+ struct semaphore athread_lock;
+ u32 cur_index;
+ struct semaphore gbus_lock;
+ struct semaphore qprot;
+};
+
+struct gmi_driver *gdrv;
+
+static void enqueue_request(struct gmi_async_req_queue_head_t *q,
+ struct gmi_async_req *new)
+{
+ unsigned long flags;
+ spin_lock_irqsave(&q->lock, flags);
+ list_add_tail(&new->async_req_list, &q->async_req_list);
+ spin_unlock_irqrestore(&q->lock, flags);
+}
+
+static struct gmi_async_req *dequeue_request(
+ struct gmi_async_req_queue_head_t *q)
+{
+ struct gmi_async_req *gasync_req = NULL;
+ unsigned long flags;
+ spin_lock_irqsave(&q->lock, flags);
+ if (!list_empty(&q->async_req_list)) {
+ gasync_req = list_first_entry(&q->async_req_list,
+ struct gmi_async_req, async_req_list);
+ list_del(&gasync_req->async_req_list);
+ }
+ spin_unlock_irqrestore(&q->lock, flags);
+
+ return gasync_req;
+}
+
+static int request_pending(struct gmi_async_req_queue_head_t *q)
+{
+
+ unsigned long flags;
+ int ret;
+ spin_lock_irqsave(&q->lock, flags);
+ ret = list_empty(&q->async_req_list);
+ spin_unlock_irqrestore(&q->lock, flags);
+ return !ret;
+}
+
+
+static int async_handler_thread(void *data)
+{
+ while (1) {
+ struct gmi_async_req *areq;
+
+ /* Blocked on lock, sleep */
+ if (down_interruptible(&gdrv->athread_lock))
+ continue;
+ /* When we reach here bus lock is already taken */
+ BUG_ON(!down_trylock(&gdrv->gbus_lock));
+ areq = dequeue_request(&gdrv->asyncq);
+ if (!areq) {
+ release_gmi_access();
+ continue;
+ }
+ areq->cb(areq->priv_data);
+ kfree(areq);
+ release_gmi_access();
+ }
+ /* unreachable */
+ return 0;
+}
+
+int enqueue_gmi_async_request(u32 gdev_handle, gasync_callbackp cb, void *pdata)
+{
+ struct gmi_async_req *areq;
+
+ if ((*(u32 *)gdev_handle) != GMI_HANDLE_SIGNATURE) {
+ printk(KERN_ERR"\n Invalid Handle ");
+ return -1;
+ }
+
+ if (cb == NULL)
+ return -1;
+ if (!gdrv)
+ BUG();
+
+ areq = kmalloc(sizeof(struct gmi_async_req), GFP_ATOMIC);
+ areq->cb = cb;
+ areq->priv_data = pdata;
+ INIT_LIST_HEAD(&areq->async_req_list);
+ enqueue_request(&gdrv->asyncq, areq);
+
+ /* Unblock the thread bus lock granted */
+ if (!down_trylock(&gdrv->gbus_lock))
+ up(&gdrv->athread_lock);
+
+ return 0;
+}
+
+int request_gmi_access(u32 gdev_handle)
+{
+ struct gmi_device *gdev = (struct gmi_device *)gdev_handle;
+
+ if ((*(u32 *)gdev_handle) != GMI_HANDLE_SIGNATURE) {
+ printk("\n Invalid Handle ");
+ return -1;
+ }
+ if (!gdrv || (gdev->priority >= GMI_MAX_PRIO) ||
+ (gdev->dev_index >= GMI_MAX_DEVICES))
+ BUG();
+
+ down(&gdrv->qprot);
+
+ if (down_trylock(&gdrv->gbus_lock)) {
+ /* Bus already in use so block this request on the waitQueue */
+ DEFINE_WAIT(wait_entry);
+ prepare_to_wait_exclusive(&gdrv->waitq[gdev->priority],
+ &wait_entry, TASK_UNINTERRUPTIBLE);
+ up(&gdrv->qprot);
+
+ schedule();
+
+ finish_wait(&gdrv->waitq[gdev->priority], &wait_entry);
+
+ /* When we reach here we would have the bus lock acquired */
+ BUG_ON(!down_trylock(&gdrv->gbus_lock));
+ return 0;
+ } else{
+ up(&gdrv->qprot);
+ return 0; /* Got hold of the bus*/
+ }
+}
+EXPORT_SYMBOL(request_gmi_access);
+
+void release_gmi_access(void)
+{
+ int i;
+ /* Check if any async request is pending serve it
+ and unblock the thread*/
+ if (request_pending(&gdrv->asyncq)) {
+ up(&gdrv->athread_lock);
+ return;
+ }
+ down(&gdrv->qprot);
+ /* Wakeup one task the highest priority available */
+ for (i = 0; i < GMI_MAX_PRIO; i++) {
+ if (waitqueue_active(&gdrv->waitq[i])) {
+ up(&gdrv->qprot);
+ wake_up(&gdrv->waitq[i]);
+ return;
+ }
+ }
+
+ /*No task Waiting release the bus_lock */
+ up(&gdrv->gbus_lock);
+ up(&gdrv->qprot);
+}
+EXPORT_SYMBOL(release_gmi_access);
+
+/* More than one device can register at same priority */
+u32 register_gmi_device(const char *devName, u32 priority)
+{
+ struct gmi_device *gdev = NULL;
+
+ if (!gdrv)
+ gmi_init();
+
+ down(&gdrv->qprot);
+
+ if (!gdrv || (priority >= GMI_MAX_PRIO) ||
+ (gdrv->cur_index >= GMI_MAX_DEVICES)) {
+ up(&gdrv->qprot);
+ return (u32)NULL;
+ }
+
+ gdev = kzalloc(sizeof(struct gmi_device), GFP_KERNEL);
+
+ gdev->signature = GMI_HANDLE_SIGNATURE;
+ gdev->priority = priority;
+ /* Init devName :pending*/
+ gdev->dev_index = gdrv->cur_index;
+ gdrv->gdev_list[gdrv->cur_index++] = gdev;
+
+ up(&gdrv->qprot);
+ return (u32)gdev;
+}
+EXPORT_SYMBOL(register_gmi_device);
+
+static int gmi_init(void)
+{
+ int i;
+ gdrv = kzalloc(sizeof(*gdrv), GFP_KERNEL);
+ sema_init(&gdrv->gbus_lock, 1);
+ sema_init(&gdrv->athread_lock, 1);
+ down(&gdrv->athread_lock);
+ sema_init(&gdrv->qprot, 1);
+ spin_lock_init(&(gdrv->asyncq.lock));
+
+ INIT_LIST_HEAD(&gdrv->asyncq.async_req_list);
+ for (i = 0; i < GMI_MAX_PRIO; i++)
+ init_waitqueue_head(&gdrv->waitq[i]);
+
+ gdrv->athread = kthread_create(async_handler_thread, 0 , "Athread");
+ wake_up_process(gdrv->athread);
+
+ return 0;
+}
+
+MODULE_AUTHOR("Nitin Sehgal <nsehgal@nvidia.com>");
+MODULE_DESCRIPTION("Tegra GMI bus access control api's");
+MODULE_LICENSE("GPL");
diff --git a/drivers/mtd/maps/tegra_nor_gmi.c b/drivers/mtd/maps/tegra_nor_gmi.c
index 474bc948c84b..1e818086572f 100644
--- a/drivers/mtd/maps/tegra_nor_gmi.c
+++ b/drivers/mtd/maps/tegra_nor_gmi.c
@@ -39,7 +39,7 @@
#include <linux/tegra_snor.h>
#ifdef CONFIG_TEGRA_GMI_ACCESS_CONTROL
-#include <linux/tegra-gmi-access.h>
+#include <linux/tegra_gmi_access.h>
#endif
struct map_info_list {
diff --git a/include/linux/tegra_gmi_access.h b/include/linux/tegra_gmi_access.h
new file mode 100644
index 000000000000..f28ae2bcba6b
--- /dev/null
+++ b/include/linux/tegra_gmi_access.h
@@ -0,0 +1,27 @@
+/*
+ * include/linux/tegra_gmi_access.h
+ *
+ * Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+int request_gmi_access(u32 gdevHandle);
+void release_gmi_access(void);
+u32 register_gmi_device(const char *dev_name, u32 priority);
+
+typedef void(*gasync_callbackp)(void *priv_data);
+int enqueue_gmi_async_request(u32 gdev_handle,
+ gasync_callbackp cb, void *pdata);
+