summaryrefslogtreecommitdiff
path: root/arch/arm/mach-tegra/sysfs-dcc.c
diff options
context:
space:
mode:
authorScott Williams <scwilliams@nvidia.com>2010-12-07 11:19:20 -0800
committerDan Willemsen <dwillemsen@nvidia.com>2011-11-30 21:42:02 -0800
commit816ab05de4f2171e20171db01fa916634ed7bee6 (patch)
tree963ce2c1724c63598deb279d9f6ed54cfc93d1e3 /arch/arm/mach-tegra/sysfs-dcc.c
parent7d8324d8f3dfc27407d7514fcbd605649c9bd210 (diff)
[ARM/tegra] Add Tegra3 support
Bug 764354 Original-Change-Id: I8a390eb4dae87dceacb97461f23d13554868b046 Reviewed-on: http://git-master/r/12228 Reviewed-by: Scott Williams <scwilliams@nvidia.com> Tested-by: Scott Williams <scwilliams@nvidia.com> Original-Change-Id: I8e6b8303898796419fb5a759cd16edff9aeac081 Rebase-Id: R2866240384c6c24f46bd7ef54bc3dc9140d9e96b
Diffstat (limited to 'arch/arm/mach-tegra/sysfs-dcc.c')
-rw-r--r--arch/arm/mach-tegra/sysfs-dcc.c262
1 files changed, 262 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/sysfs-dcc.c b/arch/arm/mach-tegra/sysfs-dcc.c
new file mode 100644
index 000000000000..fb1cf92b1641
--- /dev/null
+++ b/arch/arm/mach-tegra/sysfs-dcc.c
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2010 NVIDIA Corporation.
+ * All rights reserved.
+ *
+ * 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 of the NVIDIA Corporation 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 HOLDER 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.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/sysfs.h>
+#include <linux/workqueue.h>
+#include <linux/kobject.h>
+#include "nvos.h"
+
+#define DCC_TIMEOUT_US 100000 /* Delay time for DCC timeout (in US) */
+#define CP14_DSCR_WDTRFULL 0x20000000 /* Write Data Transfer Register Full */
+#define SYSFS_DCC_DEBUG_PRINTS 0 /* Set non-zero to enable debug prints */
+
+#if SYSFS_DCC_DEBUG_PRINTS
+#define DEBUG_DCC(x) printk x
+#else
+#define DEBUG_DCC(x)
+#endif
+
+static int DebuggerConnected = 0; /* -1=not connected, 0=unknown, 1=connected */
+static struct kobject *nvdcc_kobj;
+static spinlock_t dcc_lock;
+static struct list_head dcc_list;
+
+static ssize_t sysfsdcc_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf);
+
+static ssize_t sysfsdcc_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count);
+
+
+static struct kobj_attribute nvdcc_attr =
+ __ATTR(dcc0, 0222, sysfsdcc_show, sysfsdcc_store);
+
+static int write_to_dcc(u32 c)
+{
+ volatile NvU32 dscr;
+
+ /* Have we already determined that there is no debugger connected? */
+ if (DebuggerConnected < 0)
+ {
+ return -ENXIO;
+ }
+
+ /* Read the DSCR. */
+ asm volatile ("mrc p14, 0, %0, c0, c1, 0" : "=r" (dscr) : : "cc");
+
+ /* If DSCR Bit 29 (wDTRFull) is set there is data in the write
+ * register. If it stays there for than the DCC_TIMEOUT_US
+ * period, ignore this write and disable further DCC accesses. */
+ if (dscr & CP14_DSCR_WDTRFULL)
+ {
+ NvU64 start = NvOsGetTimeUS();
+ NvU64 end = start + DCC_TIMEOUT_US;
+ NvU64 offset = (end > start) ? 0 : 0 - start;
+ NvU64 now;
+
+ for (;;)
+ {
+ /* Re-read the DSCR. */
+ asm volatile ("mrc p14, 0, %0, c0, c1, 0" : "=r" (dscr) : : "cc");
+
+ /* Previous data still there? */
+ if (dscr & CP14_DSCR_WDTRFULL)
+ {
+ if (end > start)
+ {
+ now = NvOsGetTimeUS();
+
+ if ((now >= end) || (now < start))
+ {
+ goto fail;
+ }
+ }
+ else
+ {
+ now = offset + NvOsGetTimeUS();
+
+ if (now >= (end + offset))
+ {
+ goto fail;
+ }
+ }
+ }
+ else
+ {
+ if (DebuggerConnected == 0) {
+ /* Debugger connected */
+ spin_lock(&dcc_lock);
+ DebuggerConnected = 1;
+ spin_unlock(&dcc_lock);
+ }
+ break;
+ }
+ }
+ }
+
+ // Write the data into the DCC output register
+ asm volatile ("mcr p14, 0, %0, c0, c5, 0" : : "r" (c) : "cc");
+ return 0;
+
+fail:
+ /* No debugged connected -- disable DCC */
+ spin_lock(&dcc_lock);
+ DebuggerConnected = -1;
+ spin_unlock(&dcc_lock);
+ return -ENXIO;
+}
+
+
+struct tegra_dcc_req {
+ struct list_head node;
+
+ const char *pBuf;
+ unsigned int size;
+};
+
+struct dcc_action {
+ struct tegra_dcc_req req;
+ struct work_struct work;
+ struct list_head node;
+};
+
+
+static void dcc_writer(struct work_struct *work)
+{
+ struct dcc_action *action = container_of(work, struct dcc_action, work);
+ const char *p;
+
+ DEBUG_DCC(("+dcc_writer\n"));
+
+ spin_lock(&dcc_lock);
+ list_del(&action->req.node);
+ spin_unlock(&dcc_lock);
+
+ p = action->req.pBuf;
+ if (p)
+ while ((p < &(action->req.pBuf[action->req.size])) && (*p))
+ if (write_to_dcc(*p++))
+ break;
+
+ kfree(action->req.pBuf);
+ kfree(action);
+
+ DEBUG_DCC(("-dcc_writer\n"));
+}
+
+static ssize_t sysfsdcc_show(struct kobject *kobj,
+ struct kobj_attribute *attr, char *buf)
+{
+ DEBUG_DCC(("!sysfsdcc_show\n"));
+ return -EACCES;
+}
+
+static ssize_t sysfsdcc_store(struct kobject *kobj,
+ struct kobj_attribute *attr, const char *buf, size_t count)
+{
+ struct dcc_action *action;
+ char *pBuf;
+ ssize_t ret = count;
+
+ DEBUG_DCC(("+sysfsdcc_store: %p, %d\n", buf, count));
+
+ if (!buf || !count) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
+ pBuf = kmalloc(count+1, GFP_KERNEL);
+ if (!pBuf) {
+ pr_debug("%s: insufficient memory\n", __func__);
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ action = kzalloc(sizeof(*action), GFP_KERNEL);
+ if (!action) {
+ kfree(pBuf);
+ pr_debug("%s: insufficient memory\n", __func__);
+ ret = -ENOMEM;
+ goto fail;
+ }
+
+ strncpy(pBuf, buf, count);
+ pBuf[count] = '\0';
+ action->req.pBuf = pBuf;
+ action->req.size = count;
+
+ INIT_WORK(&action->work, dcc_writer);
+
+ spin_lock(&dcc_lock);
+ list_add_tail(&action->req.node, &dcc_list);
+ spin_unlock(&dcc_lock);
+
+ /* DCC writes can only be performed from CPU0 */
+ schedule_work_on(0, &action->work);
+
+fail:
+ DEBUG_DCC(("-sysfsdcc_store: %d\n", count));
+ return ret;
+}
+
+static int __init sysfsdcc_init(void)
+{
+ spin_lock_init(&dcc_lock);
+ INIT_LIST_HEAD(&dcc_list);
+
+ DEBUG_DCC(("+sysfsdcc_init\n"));
+ nvdcc_kobj = kobject_create_and_add("dcc", kernel_kobj);
+
+ if (sysfs_create_file(nvdcc_kobj, &nvdcc_attr.attr))
+ {
+ DEBUG_DCC(("DCC: sysfs_create_file failed!\n"));
+ return -ENXIO;
+ }
+
+ DEBUG_DCC(("-sysfsdcc_init\n"));
+ return 0;
+}
+
+static void __exit sysfsdcc_exit(void)
+{
+ DEBUG_DCC(("+sysfsdcc_exit\n"));
+ sysfs_remove_file(nvdcc_kobj, &nvdcc_attr.attr);
+ kobject_del(nvdcc_kobj);
+ DEBUG_DCC(("-sysfsdcc_exit\n"));
+}
+
+module_init(sysfsdcc_init);
+module_exit(sysfsdcc_exit);
+MODULE_LICENSE("GPL");