aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-tegra/sysfs-dcc.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-tegra/sysfs-dcc.c')
-rw-r--r--arch/arm/mach-tegra/sysfs-dcc.c249
1 files changed, 249 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 00000000000..a4dc9a72135
--- /dev/null
+++ b/arch/arm/mach-tegra/sysfs-dcc.c
@@ -0,0 +1,249 @@
1/*
2 * Copyright (c) 2010-2011 NVIDIA Corporation.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 *
8 * Redistributions of source code must retain the above copyright notice,
9 * this list of conditions and the following disclaimer.
10 *
11 * Redistributions in binary form must reproduce the above copyright notice,
12 * this list of conditions and the following disclaimer in the documentation
13 * and/or other materials provided with the distribution.
14 *
15 * Neither the name of the NVIDIA Corporation nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 *
31 */
32
33#include <linux/module.h>
34#include <linux/kernel.h>
35#include <linux/spinlock.h>
36#include <linux/sysfs.h>
37#include <linux/workqueue.h>
38#include <linux/kobject.h>
39#include <linux/hrtimer.h>
40#include <linux/slab.h>
41
42#define DCC_TIMEOUT_US 100000 /* Delay time for DCC timeout (in uS) */
43#define CP14_DSCR_WDTRFULL 0x20000000 /* Write Data Transfer Register Full */
44#define SYSFS_DCC_DEBUG_PRINTS 0 /* Set non-zero to enable debug prints */
45
46#if SYSFS_DCC_DEBUG_PRINTS
47#define DEBUG_DCC(x) printk x
48#else
49#define DEBUG_DCC(x)
50#endif
51
52static int DebuggerConnected = 0; /* -1=not connected, 0=unknown, 1=connected */
53static struct kobject *nvdcc_kobj;
54static spinlock_t dcc_lock;
55static struct list_head dcc_list;
56
57static ssize_t sysfsdcc_show(struct kobject *kobj,
58 struct kobj_attribute *attr, char *buf);
59
60static ssize_t sysfsdcc_store(struct kobject *kobj,
61 struct kobj_attribute *attr, const char *buf, size_t count);
62
63
64static struct kobj_attribute nvdcc_attr =
65 __ATTR(dcc0, 0222, sysfsdcc_show, sysfsdcc_store);
66
67static int write_to_dcc(u32 c)
68{
69 volatile u32 dscr;
70
71 /* Have we already determined that there is no debugger connected? */
72 if (DebuggerConnected < 0)
73 {
74 return -ENXIO;
75 }
76
77 /* Read the DSCR. */
78 asm volatile ("mrc p14, 0, %0, c0, c1, 0" : "=r" (dscr) : : "cc");
79
80 /* If DSCR Bit 29 (wDTRFull) is set there is data in the write
81 * register. If it stays there for than the DCC_TIMEOUT_US
82 * period, ignore this write and disable further DCC accesses. */
83 if (dscr & CP14_DSCR_WDTRFULL)
84 {
85 ktime_t end = ktime_add_ns(ktime_get(), DCC_TIMEOUT_US * 1000);
86 ktime_t now;
87
88 for (;;)
89 {
90 /* Re-read the DSCR. */
91 asm volatile ("mrc p14, 0, %0, c0, c1, 0" : "=r" (dscr) : : "cc");
92
93 /* Previous data still there? */
94 if (dscr & CP14_DSCR_WDTRFULL)
95 {
96 now = ktime_get();
97
98 if (ktime_to_ns(now) >= ktime_to_ns(end))
99 {
100 goto fail;
101 }
102 }
103 else
104 {
105 if (DebuggerConnected == 0) {
106 /* Debugger connected */
107 spin_lock(&dcc_lock);
108 DebuggerConnected = 1;
109 spin_unlock(&dcc_lock);
110 }
111 break;
112 }
113 }
114 }
115
116 // Write the data into the DCC output register
117 asm volatile ("mcr p14, 0, %0, c0, c5, 0" : : "r" (c) : "cc");
118 return 0;
119
120fail:
121 /* No debugged connected -- disable DCC */
122 spin_lock(&dcc_lock);
123 DebuggerConnected = -1;
124 spin_unlock(&dcc_lock);
125 return -ENXIO;
126}
127
128
129struct tegra_dcc_req {
130 struct list_head node;
131
132 const char *pBuf;
133 unsigned int size;
134};
135
136struct dcc_action {
137 struct tegra_dcc_req req;
138 struct work_struct work;
139 struct list_head node;
140};
141
142
143static void dcc_writer(struct work_struct *work)
144{
145 struct dcc_action *action = container_of(work, struct dcc_action, work);
146 const char *p;
147
148 DEBUG_DCC(("+dcc_writer\n"));
149
150 spin_lock(&dcc_lock);
151 list_del(&action->req.node);
152 spin_unlock(&dcc_lock);
153
154 p = action->req.pBuf;
155 if (p)
156 while ((p < &(action->req.pBuf[action->req.size])) && (*p))
157 if (write_to_dcc(*p++))
158 break;
159
160 kfree(action->req.pBuf);
161 kfree(action);
162
163 DEBUG_DCC(("-dcc_writer\n"));
164}
165
166static ssize_t sysfsdcc_show(struct kobject *kobj,
167 struct kobj_attribute *attr, char *buf)
168{
169 DEBUG_DCC(("!sysfsdcc_show\n"));
170 return -EACCES;
171}
172
173static ssize_t sysfsdcc_store(struct kobject *kobj,
174 struct kobj_attribute *attr, const char *buf, size_t count)
175{
176 struct dcc_action *action;
177 char *pBuf;
178 ssize_t ret = count;
179
180 DEBUG_DCC(("+sysfsdcc_store: %p, %d\n", buf, count));
181
182 if (!buf || !count) {
183 ret = -EINVAL;
184 goto fail;
185 }
186
187 pBuf = kmalloc(count+1, GFP_KERNEL);
188 if (!pBuf) {
189 pr_debug("%s: insufficient memory\n", __func__);
190 ret = -ENOMEM;
191 goto fail;
192 }
193
194 action = kzalloc(sizeof(*action), GFP_KERNEL);
195 if (!action) {
196 kfree(pBuf);
197 pr_debug("%s: insufficient memory\n", __func__);
198 ret = -ENOMEM;
199 goto fail;
200 }
201
202 strncpy(pBuf, buf, count);
203 pBuf[count] = '\0';
204 action->req.pBuf = pBuf;
205 action->req.size = count;
206
207 INIT_WORK(&action->work, dcc_writer);
208
209 spin_lock(&dcc_lock);
210 list_add_tail(&action->req.node, &dcc_list);
211 spin_unlock(&dcc_lock);
212
213 /* DCC writes can only be performed from CPU0 */
214 schedule_work_on(0, &action->work);
215
216fail:
217 DEBUG_DCC(("-sysfsdcc_store: %d\n", count));
218 return ret;
219}
220
221static int __init sysfsdcc_init(void)
222{
223 spin_lock_init(&dcc_lock);
224 INIT_LIST_HEAD(&dcc_list);
225
226 DEBUG_DCC(("+sysfsdcc_init\n"));
227 nvdcc_kobj = kobject_create_and_add("dcc", kernel_kobj);
228
229 if (sysfs_create_file(nvdcc_kobj, &nvdcc_attr.attr))
230 {
231 DEBUG_DCC(("DCC: sysfs_create_file failed!\n"));
232 return -ENXIO;
233 }
234
235 DEBUG_DCC(("-sysfsdcc_init\n"));
236 return 0;
237}
238
239static void __exit sysfsdcc_exit(void)
240{
241 DEBUG_DCC(("+sysfsdcc_exit\n"));
242 sysfs_remove_file(nvdcc_kobj, &nvdcc_attr.attr);
243 kobject_del(nvdcc_kobj);
244 DEBUG_DCC(("-sysfsdcc_exit\n"));
245}
246
247module_init(sysfsdcc_init);
248module_exit(sysfsdcc_exit);
249MODULE_LICENSE("GPL");