rtc: tps80031: register as mfd sub device
[linux-2.6.git] / arch / arm / mach-tegra / sysfs-dcc.c
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");