diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /arch/arm/mach-tegra/sysfs-dcc.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'arch/arm/mach-tegra/sysfs-dcc.c')
-rw-r--r-- | arch/arm/mach-tegra/sysfs-dcc.c | 249 |
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 | |||
52 | static int DebuggerConnected = 0; /* -1=not connected, 0=unknown, 1=connected */ | ||
53 | static struct kobject *nvdcc_kobj; | ||
54 | static spinlock_t dcc_lock; | ||
55 | static struct list_head dcc_list; | ||
56 | |||
57 | static ssize_t sysfsdcc_show(struct kobject *kobj, | ||
58 | struct kobj_attribute *attr, char *buf); | ||
59 | |||
60 | static ssize_t sysfsdcc_store(struct kobject *kobj, | ||
61 | struct kobj_attribute *attr, const char *buf, size_t count); | ||
62 | |||
63 | |||
64 | static struct kobj_attribute nvdcc_attr = | ||
65 | __ATTR(dcc0, 0222, sysfsdcc_show, sysfsdcc_store); | ||
66 | |||
67 | static 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 | |||
120 | fail: | ||
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 | |||
129 | struct tegra_dcc_req { | ||
130 | struct list_head node; | ||
131 | |||
132 | const char *pBuf; | ||
133 | unsigned int size; | ||
134 | }; | ||
135 | |||
136 | struct dcc_action { | ||
137 | struct tegra_dcc_req req; | ||
138 | struct work_struct work; | ||
139 | struct list_head node; | ||
140 | }; | ||
141 | |||
142 | |||
143 | static 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 | |||
166 | static 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 | |||
173 | static 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 | |||
216 | fail: | ||
217 | DEBUG_DCC(("-sysfsdcc_store: %d\n", count)); | ||
218 | return ret; | ||
219 | } | ||
220 | |||
221 | static 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 | |||
239 | static 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 | |||
247 | module_init(sysfsdcc_init); | ||
248 | module_exit(sysfsdcc_exit); | ||
249 | MODULE_LICENSE("GPL"); | ||