ARM: tegra: pcie: Remove dock detect variable
[linux-3.10.git] / arch / arm / mach-tegra / sysfs-dcc.c
1 /*
2  * arch/arm/mach-tegra/sysfs-dcc.c
3  *
4  * Copyright (c) 2010-2013, NVIDIA CORPORATION, All rights reserved.
5  *
6  * This software is licensed under the terms of the GNU General Public
7  * License version 2, as published by the Free Software Foundation, and
8  * may be copied, distributed, and modified under those terms.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  */
16
17 #include <linux/module.h>
18 #include <linux/kernel.h>
19 #include <linux/spinlock.h>
20 #include <linux/sysfs.h>
21 #include <linux/workqueue.h>
22 #include <linux/kobject.h>
23 #include <linux/hrtimer.h>
24 #include <linux/slab.h>
25
26 #define DCC_TIMEOUT_US      100000      /* Delay time for DCC timeout (in uS) */
27 #define CP14_DSCR_WDTRFULL  0x20000000  /* Write Data Transfer Register Full */
28 #define SYSFS_DCC_DEBUG_PRINTS 0        /* Set non-zero to enable debug prints */
29
30 #if SYSFS_DCC_DEBUG_PRINTS
31 #define DEBUG_DCC(x) printk x
32 #else
33 #define DEBUG_DCC(x)
34 #endif
35
36 static int DebuggerConnected = 0;  /* -1=not connected, 0=unknown, 1=connected */
37 static struct kobject *nvdcc_kobj;
38 static spinlock_t dcc_lock;
39 static struct list_head dcc_list;
40
41 static ssize_t sysfsdcc_show(struct kobject *kobj,
42                 struct kobj_attribute *attr, char *buf);
43
44 static ssize_t sysfsdcc_store(struct kobject *kobj,
45                 struct kobj_attribute *attr, const char *buf, size_t count);
46
47
48 static struct kobj_attribute nvdcc_attr =
49                 __ATTR(dcc0, 0222, sysfsdcc_show, sysfsdcc_store);
50
51 static int write_to_dcc(u32 c)
52 {
53         volatile u32 dscr;
54
55         /* Have we already determined that there is no debugger connected? */
56         if (DebuggerConnected < 0)
57         {
58                 return -ENXIO;
59         }
60
61         /* Read the DSCR. */
62         asm volatile ("mrc p14, 0, %0, c0, c1, 0" : "=r" (dscr) : : "cc");
63
64         /* If DSCR Bit 29 (wDTRFull) is set there is data in the write
65          * register. If it stays there for than the DCC_TIMEOUT_US
66          * period, ignore this write and disable further DCC accesses. */
67         if (dscr & CP14_DSCR_WDTRFULL)
68         {
69                 ktime_t end = ktime_add_ns(ktime_get(), DCC_TIMEOUT_US * 1000);
70                 ktime_t now;
71
72                 for (;;)
73                 {
74                         /* Re-read the DSCR. */
75                         asm volatile ("mrc p14, 0, %0, c0, c1, 0" : "=r" (dscr) : : "cc");
76
77                         /* Previous data still there? */
78                         if (dscr & CP14_DSCR_WDTRFULL)
79                         {
80                                 now = ktime_get();
81
82                                 if (ktime_to_ns(now) >= ktime_to_ns(end))
83                                 {
84                                         goto fail;
85                                 }
86                         }
87                         else
88                         {
89                                 if (DebuggerConnected == 0) {
90                                         /* Debugger connected */
91                                         spin_lock(&dcc_lock);
92                                         DebuggerConnected = 1;
93                                         spin_unlock(&dcc_lock);
94                                 }
95                                 break;
96                         }
97                 }
98         }
99
100         // Write the data into the DCC output register
101         asm volatile ("mcr p14, 0, %0, c0, c5, 0" : : "r" (c) : "cc");
102         return 0;
103
104 fail:
105         /* No debugged connected -- disable DCC */
106         spin_lock(&dcc_lock);
107         DebuggerConnected = -1;
108         spin_unlock(&dcc_lock);
109         return -ENXIO;
110 }
111
112
113 struct tegra_dcc_req {
114         struct list_head node;
115
116         const char *pBuf;
117         unsigned int size;
118 };
119
120 struct dcc_action {
121         struct tegra_dcc_req req;
122         struct work_struct work;
123         struct list_head node;
124 };
125
126
127 static void dcc_writer(struct work_struct *work)
128 {
129         struct dcc_action *action = container_of(work, struct dcc_action, work);
130         const char *p;
131
132         DEBUG_DCC(("+dcc_writer\n"));
133
134         spin_lock(&dcc_lock);
135         list_del(&action->req.node);
136         spin_unlock(&dcc_lock);
137
138         p = action->req.pBuf;
139         if (p)
140                 while ((p < &(action->req.pBuf[action->req.size])) && (*p))
141                         if (write_to_dcc(*p++))
142                                 break;
143
144         kfree(action->req.pBuf);
145         kfree(action);
146
147         DEBUG_DCC(("-dcc_writer\n"));
148 }
149
150 static ssize_t sysfsdcc_show(struct kobject *kobj,
151                 struct kobj_attribute *attr, char *buf)
152 {
153         DEBUG_DCC(("!sysfsdcc_show\n"));
154         return -EACCES;
155 }
156
157 static ssize_t sysfsdcc_store(struct kobject *kobj,
158         struct kobj_attribute *attr, const char *buf, size_t count)
159 {
160         struct dcc_action *action;
161         char *pBuf;
162         ssize_t ret = count;
163
164         DEBUG_DCC(("+sysfsdcc_store: %p, %d\n", buf, count));
165
166         if (!buf || !count) {
167                 ret = -EINVAL;
168                 goto fail;
169         }
170
171         pBuf = kmalloc(count+1, GFP_KERNEL);
172         if (!pBuf) {
173                 pr_debug("%s: insufficient memory\n", __func__);
174                 ret = -ENOMEM;
175                 goto fail;
176         }
177
178         action = kzalloc(sizeof(*action), GFP_KERNEL);
179         if (!action) {
180                 kfree(pBuf);
181                 pr_debug("%s: insufficient memory\n", __func__);
182                 ret = -ENOMEM;
183                 goto fail;
184         }
185
186         strncpy(pBuf, buf, count);
187         pBuf[count] = '\0';
188         action->req.pBuf = pBuf;
189         action->req.size = count;
190
191         INIT_WORK(&action->work, dcc_writer);
192
193         spin_lock(&dcc_lock);
194         list_add_tail(&action->req.node, &dcc_list);
195         spin_unlock(&dcc_lock);
196
197         /* DCC writes can only be performed from CPU0 */
198         schedule_work_on(0, &action->work);
199
200 fail:
201         DEBUG_DCC(("-sysfsdcc_store: %d\n", count));
202         return ret;
203 }
204
205 static int __init sysfsdcc_init(void)
206 {
207         spin_lock_init(&dcc_lock);
208         INIT_LIST_HEAD(&dcc_list);
209
210         DEBUG_DCC(("+sysfsdcc_init\n"));
211         nvdcc_kobj = kobject_create_and_add("dcc", kernel_kobj);
212
213         if (sysfs_create_file(nvdcc_kobj, &nvdcc_attr.attr))
214         {
215                 DEBUG_DCC(("DCC: sysfs_create_file failed!\n"));
216                 return -ENXIO;
217         }
218
219         DEBUG_DCC(("-sysfsdcc_init\n"));
220         return 0;
221 }
222
223 static void __exit sysfsdcc_exit(void)
224 {
225         DEBUG_DCC(("+sysfsdcc_exit\n"));
226         sysfs_remove_file(nvdcc_kobj, &nvdcc_attr.attr);
227         kobject_del(nvdcc_kobj);
228         DEBUG_DCC(("-sysfsdcc_exit\n"));
229 }
230
231 module_init(sysfsdcc_init);
232 module_exit(sysfsdcc_exit);
233 MODULE_LICENSE("GPL");