diff options
author | Scott Williams <scwilliams@nvidia.com> | 2010-12-07 11:19:20 -0800 |
---|---|---|
committer | Dan Willemsen <dwillemsen@nvidia.com> | 2011-11-30 21:42:02 -0800 |
commit | 816ab05de4f2171e20171db01fa916634ed7bee6 (patch) | |
tree | 963ce2c1724c63598deb279d9f6ed54cfc93d1e3 /arch/arm/mach-tegra/sysfs-dcc.c | |
parent | 7d8324d8f3dfc27407d7514fcbd605649c9bd210 (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.c | 262 |
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"); |