aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorShow Liu <show.liu@linaro.org>2013-12-25 14:46:34 +0800
committerShow Liu <show.liu@linaro.org>2014-02-20 15:59:08 +0800
commit3bf3cb238e6fec7ff976d3e641ad18750c6700f2 (patch)
tree7f044395382a3dd0bcb57533153d873549f4fe19
parent16df9ccdb2c47cdf56316f6da6b35917b0b64496 (diff)
downloadlinaro-lsk-3bf3cb238e6fec7ff976d3e641ad18750c6700f2.tar.gz
Added ARM mali KDS driver support
-rw-r--r--drivers/base/Kconfig12
-rw-r--r--drivers/base/Makefile1
-rwxr-xr-xdrivers/base/kds/Kbuild17
-rwxr-xr-xdrivers/base/kds/Kconfig19
-rwxr-xr-xdrivers/base/kds/Makefile36
-rw-r--r--drivers/base/kds/Module.symvers8
-rwxr-xr-xdrivers/base/kds/kds.c532
-rwxr-xr-xdrivers/base/kds/sconscript72
-rwxr-xr-xinclude/linux/kds.h171
9 files changed, 868 insertions, 0 deletions
diff --git a/drivers/base/Kconfig b/drivers/base/Kconfig
index 07abd9d76f7..c09a1ddba29 100644
--- a/drivers/base/Kconfig
+++ b/drivers/base/Kconfig
@@ -292,4 +292,16 @@ config CMA_AREAS
endif
+config DMA_SHARED_BUFFER_USES_KDS
+ bool "Add KDS resource within every dma_buf allocation"
+ depends on DMA_SHARED_BUFFER && KDS
+ default n
+ help
+ This option adds a KDS resource within every dma_buf allocation.
+
+config KDS
+ bool "Kernel dependency system"
+ help
+ This option enables the generic kernel dependency system
+
endmenu
diff --git a/drivers/base/Makefile b/drivers/base/Makefile
index 4e22ce3ed73..435077321aa 100644
--- a/drivers/base/Makefile
+++ b/drivers/base/Makefile
@@ -11,6 +11,7 @@ obj-y += power/
obj-$(CONFIG_HAS_DMA) += dma-mapping.o
obj-$(CONFIG_HAVE_GENERIC_DMA_COHERENT) += dma-coherent.o
obj-$(CONFIG_DMA_SHARED_BUFFER) += dma-buf.o
+obj-$(CONFIG_KDS) += kds/
obj-$(CONFIG_ISA) += isa.o
obj-$(CONFIG_FW_LOADER) += firmware_class.o
obj-$(CONFIG_NUMA) += node.o
diff --git a/drivers/base/kds/Kbuild b/drivers/base/kds/Kbuild
new file mode 100755
index 00000000000..74db4816472
--- /dev/null
+++ b/drivers/base/kds/Kbuild
@@ -0,0 +1,17 @@
+#
+# (C) COPYRIGHT 2012 ARM Limited. All rights reserved.
+#
+# This program is free software and is provided to you under the terms of the
+# GNU General Public License version 2 as published by the Free Software
+# Foundation, and any use by you of this program is subject to the terms
+# of such GNU licence.
+#
+# A copy of the licence is included with the program, and can also be obtained
+# from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+#
+#
+
+
+obj-$(CONFIG_KDS) += kds.o
+obj-$(CONFIG_KDS_TEST) += kds_test.o
diff --git a/drivers/base/kds/Kconfig b/drivers/base/kds/Kconfig
new file mode 100755
index 00000000000..3efdb5f464f
--- /dev/null
+++ b/drivers/base/kds/Kconfig
@@ -0,0 +1,19 @@
+#
+# (C) COPYRIGHT 2012 ARM Limited. All rights reserved.
+#
+# This program is free software and is provided to you under the terms of the
+# GNU General Public License version 2 as published by the Free Software
+# Foundation, and any use by you of this program is subject to the terms
+# of such GNU licence.
+#
+# A copy of the licence is included with the program, and can also be obtained
+# from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+#
+#
+
+
+config KDS
+ tristate "Kernel dependency system"
+ help
+ This option enables the generic kernel dependency system
diff --git a/drivers/base/kds/Makefile b/drivers/base/kds/Makefile
new file mode 100755
index 00000000000..03d2626df23
--- /dev/null
+++ b/drivers/base/kds/Makefile
@@ -0,0 +1,36 @@
+#
+# (C) COPYRIGHT 2011-2012 ARM Limited. All rights reserved.
+#
+# This program is free software and is provided to you under the terms of the
+# GNU General Public License version 2 as published by the Free Software
+# Foundation, and any use by you of this program is subject to the terms
+# of such GNU licence.
+#
+# A copy of the licence is included with the program, and can also be obtained
+# from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+#
+#
+
+
+# linux build system bootstrap for out-of-tree module
+
+# default to building for the host
+ARCH ?= $(shell uname -m)
+CONFIG_KDS_TEST ?= n
+
+ifeq ($(KDIR),)
+$(error Must specify KDIR to point to the kernel to target))
+endif
+
+all: kds
+
+kds:
+ $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) EXTRA_CFLAGS="-I$(CURDIR)/../../../include" CONFIG_KDS=m
+
+kds_test:
+ $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) EXTRA_CFLAGS="-I$(CURDIR)/../../../include" CONFIG_KDS_TEST=m
+
+clean:
+ $(MAKE) ARCH=$(ARCH) -C $(KDIR) M=$(CURDIR) clean
+
diff --git a/drivers/base/kds/Module.symvers b/drivers/base/kds/Module.symvers
new file mode 100644
index 00000000000..7eba467f4fa
--- /dev/null
+++ b/drivers/base/kds/Module.symvers
@@ -0,0 +1,8 @@
+0x00000000 kds_waitall /home/show/workspace/arndale/mali/TX011-SW-99002-r3p0-02rel0/driver/product/kernel/drivers/base/kds/kds EXPORT_SYMBOL
+0x00000000 kds_resource_set_release_sync /home/show/workspace/arndale/mali/TX011-SW-99002-r3p0-02rel0/driver/product/kernel/drivers/base/kds/kds EXPORT_SYMBOL
+0x00000000 kds_resource_term /home/show/workspace/arndale/mali/TX011-SW-99002-r3p0-02rel0/driver/product/kernel/drivers/base/kds/kds EXPORT_SYMBOL
+0x00000000 kds_async_waitall /home/show/workspace/arndale/mali/TX011-SW-99002-r3p0-02rel0/driver/product/kernel/drivers/base/kds/kds EXPORT_SYMBOL
+0x00000000 kds_callback_init /home/show/workspace/arndale/mali/TX011-SW-99002-r3p0-02rel0/driver/product/kernel/drivers/base/kds/kds EXPORT_SYMBOL
+0x00000000 kds_callback_term /home/show/workspace/arndale/mali/TX011-SW-99002-r3p0-02rel0/driver/product/kernel/drivers/base/kds/kds EXPORT_SYMBOL
+0x00000000 kds_resource_init /home/show/workspace/arndale/mali/TX011-SW-99002-r3p0-02rel0/driver/product/kernel/drivers/base/kds/kds EXPORT_SYMBOL
+0x00000000 kds_resource_set_release /home/show/workspace/arndale/mali/TX011-SW-99002-r3p0-02rel0/driver/product/kernel/drivers/base/kds/kds EXPORT_SYMBOL
diff --git a/drivers/base/kds/kds.c b/drivers/base/kds/kds.c
new file mode 100755
index 00000000000..aada2da646a
--- /dev/null
+++ b/drivers/base/kds/kds.c
@@ -0,0 +1,532 @@
+/*
+ *
+ * (C) COPYRIGHT 2012-2013 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * A copy of the licence is included with the program, and can also be obtained
+ * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/workqueue.h>
+#include <linux/kds.h>
+
+
+#define KDS_LINK_TRIGGERED (1u << 0)
+#define KDS_LINK_EXCLUSIVE (1u << 1)
+
+#define KDS_INVALID (void *)-2
+#define KDS_RESOURCE (void *)-1
+
+struct kds_resource_set
+{
+ unsigned long num_resources;
+ unsigned long pending;
+ struct kds_callback *cb;
+ void *callback_parameter;
+ void *callback_extra_parameter;
+ struct list_head callback_link;
+ struct work_struct callback_work;
+
+ /* This is only initted when kds_waitall() is called. */
+ wait_queue_head_t wake;
+
+ struct kds_link resources[0];
+};
+
+static DEFINE_SPINLOCK(kds_lock);
+
+int kds_callback_init(struct kds_callback *cb, int direct, kds_callback_fn user_cb)
+{
+ int ret = 0;
+
+ cb->direct = direct;
+ cb->user_cb = user_cb;
+
+ if (!direct)
+ {
+ cb->wq = alloc_workqueue("kds", WQ_UNBOUND | WQ_HIGHPRI, WQ_UNBOUND_MAX_ACTIVE);
+ if (!cb->wq)
+ ret = -ENOMEM;
+ }
+ else
+ {
+ cb->wq = NULL;
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(kds_callback_init);
+
+void kds_callback_term(struct kds_callback *cb)
+{
+ if (!cb->direct)
+ {
+ BUG_ON(!cb->wq);
+ destroy_workqueue(cb->wq);
+ }
+ else
+ {
+ BUG_ON(cb->wq);
+ }
+}
+
+EXPORT_SYMBOL(kds_callback_term);
+
+static void kds_do_user_callback(struct kds_resource_set *rset)
+{
+ rset->cb->user_cb(rset->callback_parameter, rset->callback_extra_parameter);
+}
+
+static void kds_queued_callback(struct work_struct *work)
+{
+ struct kds_resource_set *rset;
+ rset = container_of(work, struct kds_resource_set, callback_work);
+
+ kds_do_user_callback(rset);
+}
+
+static void kds_callback_perform(struct kds_resource_set *rset)
+{
+ if (rset->cb->direct)
+ kds_do_user_callback(rset);
+ else
+ {
+ int result;
+ result = queue_work(rset->cb->wq, &rset->callback_work);
+ /* if we got a 0 return it means we've triggered the same rset twice! */
+ WARN_ON(!result);
+ }
+}
+
+void kds_resource_init(struct kds_resource * const res)
+{
+ BUG_ON(!res);
+ INIT_LIST_HEAD(&res->waiters.link);
+ res->waiters.parent = KDS_RESOURCE;
+}
+EXPORT_SYMBOL(kds_resource_init);
+
+int kds_resource_term(struct kds_resource *res)
+{
+ unsigned long lflags;
+ BUG_ON(!res);
+ spin_lock_irqsave(&kds_lock, lflags);
+ if (!list_empty(&res->waiters.link))
+ {
+ spin_unlock_irqrestore(&kds_lock, lflags);
+ printk(KERN_ERR "ERROR: KDS resource is still in use\n");
+ return -EBUSY;
+ }
+ res->waiters.parent = KDS_INVALID;
+ spin_unlock_irqrestore(&kds_lock, lflags);
+ return 0;
+}
+EXPORT_SYMBOL(kds_resource_term);
+
+int kds_async_waitall(
+ struct kds_resource_set ** const pprset,
+ struct kds_callback *cb,
+ void *callback_parameter,
+ void *callback_extra_parameter,
+ int number_resources,
+ unsigned long *exclusive_access_bitmap,
+ struct kds_resource **resource_list)
+{
+ struct kds_resource_set *rset = NULL;
+ unsigned long lflags;
+ int i;
+ int triggered;
+ int err = -EFAULT;
+
+ BUG_ON(!pprset);
+ BUG_ON(!resource_list);
+ BUG_ON(!cb);
+
+ WARN_ONCE(number_resources > 10, "Waiting on a high numbers of resources may increase latency, see documentation.");
+
+ rset = kmalloc(sizeof(*rset) + number_resources * sizeof(struct kds_link), GFP_KERNEL);
+ if (!rset)
+ {
+ return -ENOMEM;
+ }
+
+ rset->num_resources = number_resources;
+ rset->pending = number_resources;
+ rset->cb = cb;
+ rset->callback_parameter = callback_parameter;
+ rset->callback_extra_parameter = callback_extra_parameter;
+ INIT_LIST_HEAD(&rset->callback_link);
+ INIT_WORK(&rset->callback_work, kds_queued_callback);
+
+ for (i = 0; i < number_resources; i++)
+ {
+ INIT_LIST_HEAD(&rset->resources[i].link);
+ rset->resources[i].parent = rset;
+ }
+
+ spin_lock_irqsave(&kds_lock, lflags);
+
+ for (i = 0; i < number_resources; i++)
+ {
+ unsigned long link_state = 0;
+
+ if (test_bit(i, exclusive_access_bitmap))
+ {
+ link_state |= KDS_LINK_EXCLUSIVE;
+ }
+
+ /* no-one else waiting? */
+ if (list_empty(&resource_list[i]->waiters.link))
+ {
+ link_state |= KDS_LINK_TRIGGERED;
+ rset->pending--;
+ }
+ /* Adding a non-exclusive and the current tail is a triggered non-exclusive? */
+ else if (((link_state & KDS_LINK_EXCLUSIVE) == 0) &&
+ (((list_entry(resource_list[i]->waiters.link.prev, struct kds_link, link)->state & (KDS_LINK_EXCLUSIVE | KDS_LINK_TRIGGERED)) == KDS_LINK_TRIGGERED)))
+ {
+ link_state |= KDS_LINK_TRIGGERED;
+ rset->pending--;
+ }
+ rset->resources[i].state = link_state;
+
+ /* avoid double wait (hang) */
+ if (!list_empty(&resource_list[i]->waiters.link))
+ {
+ /* adding same rset again? */
+ if (list_entry(resource_list[i]->waiters.link.prev, struct kds_link, link)->parent == rset)
+ {
+ goto roll_back;
+ }
+ }
+ list_add_tail(&rset->resources[i].link, &resource_list[i]->waiters.link);
+ }
+
+ triggered = (rset->pending == 0);
+
+ /* set the pointer before the callback is called so it sees it */
+ *pprset = rset;
+
+ spin_unlock_irqrestore(&kds_lock, lflags);
+
+ if (triggered)
+ {
+ /* all resources obtained, trigger callback */
+ kds_callback_perform(rset);
+ }
+
+ return 0;
+
+roll_back:
+ /* roll back */
+ while (i-- > 0)
+ {
+ list_del(&rset->resources[i].link);
+ }
+ err = -EINVAL;
+
+ spin_unlock_irqrestore(&kds_lock, lflags);
+ kfree(rset);
+ return err;
+}
+EXPORT_SYMBOL(kds_async_waitall);
+
+static void wake_up_sync_call(void *callback_parameter, void *callback_extra_parameter)
+{
+ wait_queue_head_t *wait = (wait_queue_head_t *)callback_parameter;
+ wake_up(wait);
+}
+
+static struct kds_callback sync_cb =
+{
+ wake_up_sync_call,
+ 1,
+ NULL,
+};
+
+struct kds_resource_set *kds_waitall(
+ int number_resources,
+ unsigned long *exclusive_access_bitmap,
+ struct kds_resource **resource_list,
+ unsigned long jiffies_timeout)
+{
+ struct kds_resource_set *rset;
+ unsigned long lflags;
+ int i;
+ int triggered = 0;
+ DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wake);
+
+ rset = kmalloc(sizeof(*rset) + number_resources * sizeof(struct kds_link), GFP_KERNEL);
+ if (!rset)
+ return rset;
+
+ rset->num_resources = number_resources;
+ rset->pending = number_resources;
+ init_waitqueue_head(&rset->wake);
+ INIT_LIST_HEAD(&rset->callback_link);
+ INIT_WORK(&rset->callback_work, kds_queued_callback);
+
+ spin_lock_irqsave(&kds_lock, lflags);
+
+ for (i = 0; i < number_resources; i++)
+ {
+ unsigned long link_state = 0;
+
+ if (test_bit(i, exclusive_access_bitmap))
+ {
+ link_state |= KDS_LINK_EXCLUSIVE;
+ }
+
+ if (list_empty(&resource_list[i]->waiters.link))
+ {
+ link_state |= KDS_LINK_TRIGGERED;
+ rset->pending--;
+ }
+ /* Adding a non-exclusive and the current tail is a triggered non-exclusive? */
+ else if (((link_state & KDS_LINK_EXCLUSIVE) == 0) &&
+ (((list_entry(resource_list[i]->waiters.link.prev, struct kds_link, link)->state & (KDS_LINK_EXCLUSIVE | KDS_LINK_TRIGGERED)) == KDS_LINK_TRIGGERED)))
+ {
+ link_state |= KDS_LINK_TRIGGERED;
+ rset->pending--;
+ }
+
+ INIT_LIST_HEAD(&rset->resources[i].link);
+ rset->resources[i].parent = rset;
+ rset->resources[i].state = link_state;
+
+ /* avoid double wait (hang) */
+ if (!list_empty(&resource_list[i]->waiters.link))
+ {
+ /* adding same rset again? */
+ if (list_entry(resource_list[i]->waiters.link.prev, struct kds_link, link)->parent == rset)
+ {
+ goto roll_back;
+ }
+ }
+
+ list_add_tail(&rset->resources[i].link, &resource_list[i]->waiters.link);
+ }
+
+ if (rset->pending == 0)
+ triggered = 1;
+ else
+ {
+ rset->cb = &sync_cb;
+ rset->callback_parameter = &rset->wake;
+ rset->callback_extra_parameter = NULL;
+ }
+
+ spin_unlock_irqrestore(&kds_lock, lflags);
+
+ if (!triggered)
+ {
+ long wait_res = 0;
+ long timeout = (jiffies_timeout == KDS_WAIT_BLOCKING) ?
+ MAX_SCHEDULE_TIMEOUT : jiffies_timeout;
+
+ if (timeout)
+ {
+ wait_res = wait_event_interruptible_timeout(rset->wake,
+ rset->pending == 0, timeout);
+ }
+
+ if ((wait_res == -ERESTARTSYS) || (wait_res == 0))
+ {
+ /* use \a kds_resource_set_release to roll back */
+ kds_resource_set_release(&rset);
+ return ERR_PTR(wait_res);
+ }
+ }
+ return rset;
+
+roll_back:
+ /* roll back */
+ while (i-- > 0)
+ {
+ list_del(&rset->resources[i].link);
+ }
+
+ spin_unlock_irqrestore(&kds_lock, lflags);
+ kfree(rset);
+ return ERR_PTR(-EINVAL);
+}
+EXPORT_SYMBOL(kds_waitall);
+
+static void __kds_resource_set_release_common(struct kds_resource_set *rset)
+{
+ struct list_head triggered = LIST_HEAD_INIT(triggered);
+ struct kds_resource_set *it;
+ unsigned long lflags;
+ int i;
+
+ spin_lock_irqsave(&kds_lock, lflags);
+
+ for (i = 0; i < rset->num_resources; i++)
+ {
+ struct kds_resource *resource;
+ struct kds_link *it = NULL;
+
+ /* fetch the previous entry on the linked list */
+ it = list_entry(rset->resources[i].link.prev, struct kds_link, link);
+ /* unlink ourself */
+ list_del(&rset->resources[i].link);
+
+ /* any waiters? */
+ if (list_empty(&it->link))
+ continue;
+
+ /* were we the head of the list? (head if prev is a resource) */
+ if (it->parent != KDS_RESOURCE)
+ {
+ if ((it->state & KDS_LINK_TRIGGERED) && !(it->state & KDS_LINK_EXCLUSIVE) )
+ {
+ /*
+ * previous was triggered and not exclusive, so we
+ * trigger non-exclusive until end-of-list or first
+ * exclusive
+ */
+
+ struct kds_link *it_waiting = it;
+
+ list_for_each_entry(it, &it_waiting->link, link)
+ {
+ /* exclusive found, stop triggering */
+ if (it->state & KDS_LINK_EXCLUSIVE)
+ break;
+
+ it->state |= KDS_LINK_TRIGGERED;
+ /* a parent to update? */
+ if ( it->parent != KDS_RESOURCE )
+ {
+ if (0 == --it->parent->pending)
+ {
+ /* new owner now triggered, track for callback later */
+ list_add(&it->parent->callback_link, &triggered);
+ }
+ }
+ }
+ }
+ continue;
+ }
+
+ /* we were the head, find the kds_resource */
+ resource = container_of(it, struct kds_resource, waiters);
+
+ /* we know there is someone waiting from the any-waiters test above */
+
+ /* find the head of the waiting list */
+ it = list_first_entry(&resource->waiters.link, struct kds_link, link);
+
+ /* new exclusive owner? */
+ if (it->state & KDS_LINK_EXCLUSIVE)
+ {
+ /* link now triggered */
+ it->state |= KDS_LINK_TRIGGERED;
+ /* a parent to update? */
+ if (0 == --it->parent->pending)
+ {
+ /* new owner now triggered, track for callback later */
+ list_add(&it->parent->callback_link, &triggered);
+ }
+ }
+ /* exclusive releasing ? */
+ else if (rset->resources[i].state & KDS_LINK_EXCLUSIVE)
+ {
+ /* trigger non-exclusive until end-of-list or first exclusive */
+ list_for_each_entry(it, &resource->waiters.link, link)
+ {
+ /* exclusive found, stop triggering */
+ if (it->state & KDS_LINK_EXCLUSIVE)
+ break;
+
+ it->state |= KDS_LINK_TRIGGERED;
+ /* a parent to update? */
+ if (0 == --it->parent->pending)
+ {
+ /* new owner now triggered, track for callback later */
+ list_add(&it->parent->callback_link, &triggered);
+ }
+ }
+ }
+ }
+
+ spin_unlock_irqrestore(&kds_lock, lflags);
+
+ while (!list_empty(&triggered))
+ {
+ it = list_first_entry(&triggered, struct kds_resource_set, callback_link);
+ list_del(&it->callback_link);
+ kds_callback_perform(it);
+ }
+}
+
+void kds_resource_set_release(struct kds_resource_set **pprset)
+{
+ struct kds_resource_set *rset;
+
+ rset = cmpxchg(pprset,*pprset,NULL);
+
+ if (!rset)
+ {
+ /* caught a race between a cancelation
+ * and a completion, nothing to do */
+ return;
+ }
+
+ __kds_resource_set_release_common(rset);
+
+ /*
+ * Caller is responsible for guaranteeing that callback work is not
+ * pending (i.e. its running or completed) prior to calling release.
+ */
+ BUG_ON(work_pending(&rset->callback_work));
+
+ /* free the resource set */
+ kfree(rset);
+}
+EXPORT_SYMBOL(kds_resource_set_release);
+
+void kds_resource_set_release_sync(struct kds_resource_set **pprset)
+{
+ struct kds_resource_set *rset;
+
+ rset = cmpxchg(pprset,*pprset,NULL);
+ if (!rset)
+ {
+ /* caught a race between a cancelation
+ * and a completion, nothing to do */
+ return;
+ }
+
+ __kds_resource_set_release_common(rset);
+
+ /*
+ * In the case of a kds async wait cancellation ensure the deferred
+ * call back does not get scheduled if a trigger fired at the same time
+ * to release the wait.
+ */
+ cancel_work_sync(&rset->callback_work);
+
+ /* free the resource set */
+ kfree(rset);
+}
+EXPORT_SYMBOL(kds_resource_set_release_sync);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("ARM Ltd.");
+MODULE_VERSION("1.0");
diff --git a/drivers/base/kds/sconscript b/drivers/base/kds/sconscript
new file mode 100755
index 00000000000..23f8eca4cdc
--- /dev/null
+++ b/drivers/base/kds/sconscript
@@ -0,0 +1,72 @@
+#
+# (C) COPYRIGHT 2010-2012 ARM Limited. All rights reserved.
+#
+# This program is free software and is provided to you under the terms of the
+# GNU General Public License version 2 as published by the Free Software
+# Foundation, and any use by you of this program is subject to the terms
+# of such GNU licence.
+#
+# A copy of the licence is included with the program, and can also be obtained
+# from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+# Boston, MA 02110-1301, USA.
+#
+#
+
+
+import os
+import re
+Import('env')
+
+# If KDS is built into the kernel already we skip building the module ourselves
+linux_config_file = os.path.normpath(os.environ['KDIR']) + '/.config'
+search_term = '^[\ ]*CONFIG_KDS[\ ]*=[\ ]*y'
+build_kds = 1
+for line in open(linux_config_file, 'r'):
+ if re.search(search_term, line):
+ # KDS in kernel. Do not build KDS kernel module but
+ # still allow for building kds_test module.
+ build_kds = 0
+
+if env['v'] != '1':
+ env['MAKECOMSTR'] = '[MAKE] ${SOURCE.dir}'
+
+src = [Glob('#kernel/drivers/base/kds/*.c'), Glob('#kernel/include/linux/*.h'), Glob('#kernel/drivers/base/kds/K*')]
+
+env.Append( CPPPATH = '#kernel/include' )
+
+arch = env['arch']
+# translate into something Linux understands
+if arch == 'x86_32':
+ arch = 'x86'
+elif 'arm' in arch:
+ arch = 'arm'
+
+if Glob('tests/sconscript'):
+ SConscript( 'tests/sconscript' )
+
+if env.GetOption('clean') :
+ # Clean KDS module
+ if build_kds:
+ env.Execute(Action("make ARCH=%s clean" % arch, '[CLEAN] kds'))
+ cmd = env.Command('$STATIC_LIB_PATH/kds.ko', src, [])
+ env.ProgTarget('kds', cmd)
+
+ # Clean KDS test module
+ if (int(env['unit']) == 1):
+ cmdTest = env.Command('$STATIC_LIB_PATH/kds_test.ko', src, [])
+ env.ProgTarget('kds', cmdTest)
+else:
+ # Build KDS module
+ if build_kds:
+ makeAction=Action("cd ${SOURCE.dir} && make kds ARCH=%s && cp kds.ko $STATIC_LIB_PATH/" % arch, '$MAKECOMSTR')
+ cmd = env.Command('$STATIC_LIB_PATH/kds.ko', src, [makeAction])
+ env.ProgTarget('kds', cmd)
+
+ # Build KDS test module
+ if int(env['unit']) == 1:
+ makeActionTest=Action("cd ${SOURCE.dir} && make kds_test ARCH=%s && cp kds_test.ko $STATIC_LIB_PATH/" %arch, '$MAKECOMSTR')
+ cmdTest = env.Command('$STATIC_LIB_PATH/kds_test.ko', src, [makeActionTest])
+ env.ProgTarget('kds', cmdTest)
+ if build_kds:
+ Depends(cmdTest, cmd)
+
diff --git a/include/linux/kds.h b/include/linux/kds.h
new file mode 100755
index 00000000000..73168657b3c
--- /dev/null
+++ b/include/linux/kds.h
@@ -0,0 +1,171 @@
+/*
+ *
+ * (C) COPYRIGHT 2012 ARM Limited. All rights reserved.
+ *
+ * This program is free software and is provided to you under the terms of the
+ * GNU General Public License version 2 as published by the Free Software
+ * Foundation, and any use by you of this program is subject to the terms
+ * of such GNU licence.
+ *
+ * A copy of the licence is included with the program, and can also be obtained
+ * from Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ *
+ */
+
+
+
+#ifndef _KDS_H_
+#define _KDS_H_
+
+#include <linux/list.h>
+#include <linux/workqueue.h>
+
+#define KDS_WAIT_BLOCKING (ULONG_MAX)
+
+struct kds_resource_set;
+
+typedef void (*kds_callback_fn) (void *callback_parameter, void *callback_extra_parameter);
+
+struct kds_callback
+{
+ kds_callback_fn user_cb; /* real cb */
+ int direct; /* do direct or queued call? */
+ struct workqueue_struct *wq;
+};
+
+struct kds_link
+{
+ struct kds_resource_set *parent;
+ struct list_head link;
+ unsigned long state;
+};
+
+struct kds_resource
+{
+ struct kds_link waiters;
+};
+
+/* callback API */
+
+/* Initialize a callback object.
+ *
+ * Typically created per context or per hw resource.
+ *
+ * Callbacks can be performed directly if no nested locking can
+ * happen in the client.
+ *
+ * Nested locking can occur when a lock is held during the kds_async_waitall or
+ * kds_resource_set_release call. If the callback needs to take the same lock
+ * nested locking will happen.
+ *
+ * If nested locking could happen non-direct callbacks can be requested.
+ * Callbacks will then be called asynchronous to the triggering call.
+ */
+int kds_callback_init(struct kds_callback *cb, int direct, kds_callback_fn user_cb);
+
+/* Terminate the use of a callback object.
+ *
+ * If the callback object was set up as non-direct
+ * any pending callbacks will be flushed first.
+ * Note that to avoid a deadlock the lock callbacks needs
+ * can't be held when a callback object is terminated.
+ */
+void kds_callback_term(struct kds_callback *cb);
+
+
+/* resource object API */
+
+/* initialize a resource handle for a shared resource */
+void kds_resource_init(struct kds_resource * const resource);
+
+/*
+ * Will return 0 on success.
+ * If the resource is being used or waited -EBUSY is returned.
+ * The caller should NOT try to terminate a resource that could still have clients.
+ * After the function returns the resource is no longer known by kds.
+ */
+int kds_resource_term(struct kds_resource *resource);
+
+/* Asynchronous wait for a set of resources.
+ * Callback will be called when all resources are available.
+ * If all the resources was available the callback will be called before kds_async_waitall returns.
+ * So one must not hold any locks the callback code-flow can take when calling kds_async_waitall.
+ * Caller considered to own/use the resources until \a kds_rset_release is called.
+ * exclusive_access_bitmap is a bitmap where a high bit means exclusive access while a low bit means shared access.
+ * Use the Linux __set_bit API, where the index of the buffer to control is used as the bit index.
+ *
+ * Standard Linux error return value.
+ */
+int kds_async_waitall(
+ struct kds_resource_set ** const pprset,
+ struct kds_callback *cb,
+ void *callback_parameter,
+ void *callback_extra_parameter,
+ int number_resources,
+ unsigned long *exclusive_access_bitmap,
+ struct kds_resource **resource_list);
+
+/* Synchronous wait for a set of resources.
+ * Function will return when one of these have happened:
+ * - all resources have been obtained
+ * - timeout lapsed while waiting
+ * - a signal was received while waiting
+ *
+ * To wait without a timeout, specify KDS_WAIT_BLOCKING for \a jifies_timeout, otherwise
+ * the timeout in jiffies. A zero timeout attempts to obtain all resources and returns
+ * immediately with a timeout if all resources could not be obtained.
+ *
+ * Caller considered to own/use the resources when the function returns.
+ * Caller must release the resources using \a kds_rset_release.
+ *
+ * Calling this function while holding already locked resources or other locking primitives is dangerous.
+ * One must if this is needed decide on a lock order of the resources and/or the other locking primitives
+ * and always take the resources/locking primitives in the specific order.
+ *
+ * Use the ERR_PTR framework to decode the return value.
+ * NULL = time out
+ * If IS_ERR then PTR_ERR gives:
+ * ERESTARTSYS = signal received, retry call after signal
+ * all other values = internal error, lock failed
+ * Other values = successful wait, now the owner, must call kds_resource_set_release
+ */
+struct kds_resource_set *kds_waitall(
+ int number_resources,
+ unsigned long *exclusive_access_bitmap,
+ struct kds_resource **resource_list,
+ unsigned long jifies_timeout);
+
+/* Release resources after use.
+ * Caller must handle that other async callbacks will trigger,
+ * so must avoid holding any locks a callback will take.
+ *
+ * The function takes a pointer to your poiner to handle a race
+ * between a cancelation and a completion.
+ *
+ * If the caller can't guarantee that a race can't occur then
+ * the passed in pointer must be the same in both call paths
+ * to allow kds to manage the potential race.
+ */
+void kds_resource_set_release(struct kds_resource_set **pprset);
+
+/* Release resources after use and wait callbacks to complete.
+ * Caller must handle that other async callbacks will trigger,
+ * so must avoid holding any locks a callback will take.
+ *
+ * The function takes a pointer to your poiner to handle a race
+ * between a cancelation and a completion.
+ *
+ * If the caller can't guarantee that a race can't occur then
+ * the passed in pointer must be the same in both call paths
+ * to allow kds to manage the potential race.
+ *
+ * This should be used to cancel waits which are pending on a kds
+ * resource.
+ *
+ * It is a bug to call this from atomic contexts and from within
+ * a kds callback that now owns the kds_rseource.
+ */
+
+void kds_resource_set_release_sync(struct kds_resource_set **pprset);
+#endif /* _KDS_H_ */