blob: 5b890c1e69e02a1129d5f5dd326f01c8c5445fe7 [file] [log] [blame]
Alexander Shishkine443b332012-05-11 17:25:46 +03001#include <linux/kernel.h>
Alexander Shishkin3c37bb62013-03-30 02:46:17 +02002#include <linux/device.h>
3#include <linux/types.h>
4#include <linux/spinlock.h>
Alexander Shishkin2d651282013-03-30 12:53:51 +02005#include <linux/debugfs.h>
6#include <linux/seq_file.h>
7#include <linux/uaccess.h>
Alexander Shishkine443b332012-05-11 17:25:46 +03008#include <linux/usb/ch9.h>
9#include <linux/usb/gadget.h>
Alexander Shishkine443b332012-05-11 17:25:46 +030010
11#include "ci.h"
12#include "udc.h"
13#include "bits.h"
14#include "debug.h"
Li Junc4a8b632014-04-23 15:56:40 +080015#include "otg.h"
Alexander Shishkine443b332012-05-11 17:25:46 +030016
Alexander Shishkine443b332012-05-11 17:25:46 +030017/**
Alexander Shishkin2d651282013-03-30 12:53:51 +020018 * ci_device_show: prints information about device capabilities and status
Alexander Shishkine443b332012-05-11 17:25:46 +030019 */
Alexander Shishkin2d651282013-03-30 12:53:51 +020020static int ci_device_show(struct seq_file *s, void *data)
Alexander Shishkine443b332012-05-11 17:25:46 +030021{
Alexander Shishkin8e229782013-06-24 14:46:36 +030022 struct ci_hdrc *ci = s->private;
Richard Zhao26c696c2012-07-07 22:56:40 +080023 struct usb_gadget *gadget = &ci->gadget;
Alexander Shishkine443b332012-05-11 17:25:46 +030024
Alexander Shishkin2d651282013-03-30 12:53:51 +020025 seq_printf(s, "speed = %d\n", gadget->speed);
26 seq_printf(s, "max_speed = %d\n", gadget->max_speed);
27 seq_printf(s, "is_otg = %d\n", gadget->is_otg);
28 seq_printf(s, "is_a_peripheral = %d\n", gadget->is_a_peripheral);
29 seq_printf(s, "b_hnp_enable = %d\n", gadget->b_hnp_enable);
30 seq_printf(s, "a_hnp_support = %d\n", gadget->a_hnp_support);
31 seq_printf(s, "a_alt_hnp_support = %d\n", gadget->a_alt_hnp_support);
32 seq_printf(s, "name = %s\n",
33 (gadget->name ? gadget->name : ""));
34
35 if (!ci->driver)
Alexander Shishkine443b332012-05-11 17:25:46 +030036 return 0;
Alexander Shishkine443b332012-05-11 17:25:46 +030037
Alexander Shishkin2d651282013-03-30 12:53:51 +020038 seq_printf(s, "gadget function = %s\n",
39 (ci->driver->function ? ci->driver->function : ""));
40 seq_printf(s, "gadget max speed = %d\n", ci->driver->max_speed);
Alexander Shishkine443b332012-05-11 17:25:46 +030041
Alexander Shishkin2d651282013-03-30 12:53:51 +020042 return 0;
Alexander Shishkine443b332012-05-11 17:25:46 +030043}
Alexander Shishkin2d651282013-03-30 12:53:51 +020044
45static int ci_device_open(struct inode *inode, struct file *file)
46{
47 return single_open(file, ci_device_show, inode->i_private);
48}
49
50static const struct file_operations ci_device_fops = {
51 .open = ci_device_open,
52 .read = seq_read,
53 .llseek = seq_lseek,
54 .release = single_release,
55};
Alexander Shishkine443b332012-05-11 17:25:46 +030056
57/**
Alexander Shishkin2d651282013-03-30 12:53:51 +020058 * ci_port_test_show: reads port test mode
Alexander Shishkine443b332012-05-11 17:25:46 +030059 */
Alexander Shishkin2d651282013-03-30 12:53:51 +020060static int ci_port_test_show(struct seq_file *s, void *data)
Alexander Shishkine443b332012-05-11 17:25:46 +030061{
Alexander Shishkin8e229782013-06-24 14:46:36 +030062 struct ci_hdrc *ci = s->private;
Alexander Shishkine443b332012-05-11 17:25:46 +030063 unsigned long flags;
64 unsigned mode;
65
Richard Zhao26c696c2012-07-07 22:56:40 +080066 spin_lock_irqsave(&ci->lock, flags);
67 mode = hw_port_test_get(ci);
68 spin_unlock_irqrestore(&ci->lock, flags);
Alexander Shishkine443b332012-05-11 17:25:46 +030069
Alexander Shishkin2d651282013-03-30 12:53:51 +020070 seq_printf(s, "mode = %u\n", mode);
71
72 return 0;
Alexander Shishkine443b332012-05-11 17:25:46 +030073}
74
75/**
Alexander Shishkin2d651282013-03-30 12:53:51 +020076 * ci_port_test_write: writes port test mode
Alexander Shishkine443b332012-05-11 17:25:46 +030077 */
Alexander Shishkin2d651282013-03-30 12:53:51 +020078static ssize_t ci_port_test_write(struct file *file, const char __user *ubuf,
79 size_t count, loff_t *ppos)
Alexander Shishkine443b332012-05-11 17:25:46 +030080{
Alexander Shishkin2d651282013-03-30 12:53:51 +020081 struct seq_file *s = file->private_data;
Alexander Shishkin8e229782013-06-24 14:46:36 +030082 struct ci_hdrc *ci = s->private;
Alexander Shishkine443b332012-05-11 17:25:46 +030083 unsigned long flags;
84 unsigned mode;
Alexander Shishkin2d651282013-03-30 12:53:51 +020085 char buf[32];
86 int ret;
Alexander Shishkine443b332012-05-11 17:25:46 +030087
Alexander Shishkin2d651282013-03-30 12:53:51 +020088 if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
89 return -EFAULT;
Alexander Shishkine443b332012-05-11 17:25:46 +030090
Alexander Shishkin2d651282013-03-30 12:53:51 +020091 if (sscanf(buf, "%u", &mode) != 1)
92 return -EINVAL;
Alexander Shishkine443b332012-05-11 17:25:46 +030093
Richard Zhao26c696c2012-07-07 22:56:40 +080094 spin_lock_irqsave(&ci->lock, flags);
Alexander Shishkin2d651282013-03-30 12:53:51 +020095 ret = hw_port_test_set(ci, mode);
Richard Zhao26c696c2012-07-07 22:56:40 +080096 spin_unlock_irqrestore(&ci->lock, flags);
Alexander Shishkine443b332012-05-11 17:25:46 +030097
Alexander Shishkin2d651282013-03-30 12:53:51 +020098 return ret ? ret : count;
Alexander Shishkine443b332012-05-11 17:25:46 +030099}
Alexander Shishkin2d651282013-03-30 12:53:51 +0200100
101static int ci_port_test_open(struct inode *inode, struct file *file)
102{
103 return single_open(file, ci_port_test_show, inode->i_private);
104}
105
106static const struct file_operations ci_port_test_fops = {
107 .open = ci_port_test_open,
108 .write = ci_port_test_write,
109 .read = seq_read,
110 .llseek = seq_lseek,
111 .release = single_release,
112};
Alexander Shishkine443b332012-05-11 17:25:46 +0300113
114/**
Alexander Shishkin2d651282013-03-30 12:53:51 +0200115 * ci_qheads_show: DMA contents of all queue heads
Alexander Shishkine443b332012-05-11 17:25:46 +0300116 */
Alexander Shishkin2d651282013-03-30 12:53:51 +0200117static int ci_qheads_show(struct seq_file *s, void *data)
Alexander Shishkine443b332012-05-11 17:25:46 +0300118{
Alexander Shishkin8e229782013-06-24 14:46:36 +0300119 struct ci_hdrc *ci = s->private;
Alexander Shishkine443b332012-05-11 17:25:46 +0300120 unsigned long flags;
Alexander Shishkin2d651282013-03-30 12:53:51 +0200121 unsigned i, j;
Alexander Shishkine443b332012-05-11 17:25:46 +0300122
Alexander Shishkin2d651282013-03-30 12:53:51 +0200123 if (ci->role != CI_ROLE_GADGET) {
124 seq_printf(s, "not in gadget mode\n");
Alexander Shishkine443b332012-05-11 17:25:46 +0300125 return 0;
126 }
127
Richard Zhao26c696c2012-07-07 22:56:40 +0800128 spin_lock_irqsave(&ci->lock, flags);
129 for (i = 0; i < ci->hw_ep_max/2; i++) {
Alexander Shishkin8e229782013-06-24 14:46:36 +0300130 struct ci_hw_ep *hweprx = &ci->ci_hw_ep[i];
131 struct ci_hw_ep *hweptx =
132 &ci->ci_hw_ep[i + ci->hw_ep_max/2];
Alexander Shishkin2d651282013-03-30 12:53:51 +0200133 seq_printf(s, "EP=%02i: RX=%08X TX=%08X\n",
Alexander Shishkin2dbc5c42013-06-13 18:00:03 +0300134 i, (u32)hweprx->qh.dma, (u32)hweptx->qh.dma);
Alexander Shishkin8e229782013-06-24 14:46:36 +0300135 for (j = 0; j < (sizeof(struct ci_hw_qh)/sizeof(u32)); j++)
Alexander Shishkin2d651282013-03-30 12:53:51 +0200136 seq_printf(s, " %04X: %08X %08X\n", j,
Alexander Shishkin2dbc5c42013-06-13 18:00:03 +0300137 *((u32 *)hweprx->qh.ptr + j),
138 *((u32 *)hweptx->qh.ptr + j));
Alexander Shishkine443b332012-05-11 17:25:46 +0300139 }
Richard Zhao26c696c2012-07-07 22:56:40 +0800140 spin_unlock_irqrestore(&ci->lock, flags);
Alexander Shishkine443b332012-05-11 17:25:46 +0300141
Alexander Shishkin2d651282013-03-30 12:53:51 +0200142 return 0;
Alexander Shishkine443b332012-05-11 17:25:46 +0300143}
144
Alexander Shishkin2d651282013-03-30 12:53:51 +0200145static int ci_qheads_open(struct inode *inode, struct file *file)
Alexander Shishkine443b332012-05-11 17:25:46 +0300146{
Alexander Shishkin2d651282013-03-30 12:53:51 +0200147 return single_open(file, ci_qheads_show, inode->i_private);
Alexander Shishkine443b332012-05-11 17:25:46 +0300148}
Alexander Shishkin2d651282013-03-30 12:53:51 +0200149
150static const struct file_operations ci_qheads_fops = {
151 .open = ci_qheads_open,
152 .read = seq_read,
153 .llseek = seq_lseek,
154 .release = single_release,
155};
Alexander Shishkine443b332012-05-11 17:25:46 +0300156
157/**
Alexander Shishkin2d651282013-03-30 12:53:51 +0200158 * ci_requests_show: DMA contents of all requests currently queued (all endpts)
Alexander Shishkine443b332012-05-11 17:25:46 +0300159 */
Alexander Shishkin2d651282013-03-30 12:53:51 +0200160static int ci_requests_show(struct seq_file *s, void *data)
Alexander Shishkine443b332012-05-11 17:25:46 +0300161{
Alexander Shishkin8e229782013-06-24 14:46:36 +0300162 struct ci_hdrc *ci = s->private;
Alexander Shishkine443b332012-05-11 17:25:46 +0300163 unsigned long flags;
164 struct list_head *ptr = NULL;
Alexander Shishkin8e229782013-06-24 14:46:36 +0300165 struct ci_hw_req *req = NULL;
Michael Grzeschikcc9e6c42013-06-13 17:59:53 +0300166 struct td_node *node, *tmpnode;
Alexander Shishkin8e229782013-06-24 14:46:36 +0300167 unsigned i, j, qsize = sizeof(struct ci_hw_td)/sizeof(u32);
Alexander Shishkine443b332012-05-11 17:25:46 +0300168
Alexander Shishkin2d651282013-03-30 12:53:51 +0200169 if (ci->role != CI_ROLE_GADGET) {
170 seq_printf(s, "not in gadget mode\n");
Alexander Shishkine443b332012-05-11 17:25:46 +0300171 return 0;
172 }
173
Richard Zhao26c696c2012-07-07 22:56:40 +0800174 spin_lock_irqsave(&ci->lock, flags);
175 for (i = 0; i < ci->hw_ep_max; i++)
Alexander Shishkin8e229782013-06-24 14:46:36 +0300176 list_for_each(ptr, &ci->ci_hw_ep[i].qh.queue) {
177 req = list_entry(ptr, struct ci_hw_req, queue);
Alexander Shishkine443b332012-05-11 17:25:46 +0300178
Michael Grzeschikcc9e6c42013-06-13 17:59:53 +0300179 list_for_each_entry_safe(node, tmpnode, &req->tds, td) {
180 seq_printf(s, "EP=%02i: TD=%08X %s\n",
181 i % (ci->hw_ep_max / 2),
182 (u32)node->dma,
183 ((i < ci->hw_ep_max/2) ?
184 "RX" : "TX"));
Alexander Shishkine443b332012-05-11 17:25:46 +0300185
Michael Grzeschikcc9e6c42013-06-13 17:59:53 +0300186 for (j = 0; j < qsize; j++)
187 seq_printf(s, " %04X: %08X\n", j,
188 *((u32 *)node->ptr + j));
189 }
Alexander Shishkine443b332012-05-11 17:25:46 +0300190 }
Richard Zhao26c696c2012-07-07 22:56:40 +0800191 spin_unlock_irqrestore(&ci->lock, flags);
Alexander Shishkine443b332012-05-11 17:25:46 +0300192
Alexander Shishkin2d651282013-03-30 12:53:51 +0200193 return 0;
Alexander Shishkine443b332012-05-11 17:25:46 +0300194}
Alexander Shishkin2d651282013-03-30 12:53:51 +0200195
196static int ci_requests_open(struct inode *inode, struct file *file)
197{
198 return single_open(file, ci_requests_show, inode->i_private);
199}
200
201static const struct file_operations ci_requests_fops = {
202 .open = ci_requests_open,
203 .read = seq_read,
204 .llseek = seq_lseek,
205 .release = single_release,
206};
Alexander Shishkine443b332012-05-11 17:25:46 +0300207
Alexander Shishkinc8e333a2013-03-30 12:53:52 +0200208static int ci_role_show(struct seq_file *s, void *data)
209{
Alexander Shishkin8e229782013-06-24 14:46:36 +0300210 struct ci_hdrc *ci = s->private;
Alexander Shishkinc8e333a2013-03-30 12:53:52 +0200211
212 seq_printf(s, "%s\n", ci_role(ci)->name);
213
214 return 0;
215}
216
217static ssize_t ci_role_write(struct file *file, const char __user *ubuf,
218 size_t count, loff_t *ppos)
219{
220 struct seq_file *s = file->private_data;
Alexander Shishkin8e229782013-06-24 14:46:36 +0300221 struct ci_hdrc *ci = s->private;
Alexander Shishkinc8e333a2013-03-30 12:53:52 +0200222 enum ci_role role;
223 char buf[8];
224 int ret;
225
226 if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
227 return -EFAULT;
228
229 for (role = CI_ROLE_HOST; role < CI_ROLE_END; role++)
230 if (ci->roles[role] &&
231 !strncmp(buf, ci->roles[role]->name,
232 strlen(ci->roles[role]->name)))
233 break;
234
235 if (role == CI_ROLE_END || role == ci->role)
236 return -EINVAL;
237
238 ci_role_stop(ci);
239 ret = ci_role_start(ci, role);
240
241 return ret ? ret : count;
242}
243
244static int ci_role_open(struct inode *inode, struct file *file)
245{
246 return single_open(file, ci_role_show, inode->i_private);
247}
248
249static const struct file_operations ci_role_fops = {
250 .open = ci_role_open,
251 .write = ci_role_write,
252 .read = seq_read,
253 .llseek = seq_lseek,
254 .release = single_release,
255};
256
Li Junc4a8b632014-04-23 15:56:40 +0800257int ci_registers_show(struct seq_file *s, void *unused)
258{
259 struct ci_hdrc *ci = s->private;
260 u32 tmp_reg;
261
262 if (!ci)
263 return 0;
264
265 /* ------ Registers ----- */
266 tmp_reg = hw_read_intr_enable(ci);
267 seq_printf(s, "USBINTR reg: %08x\n", tmp_reg);
268
269 tmp_reg = hw_read_intr_status(ci);
270 seq_printf(s, "USBSTS reg: %08x\n", tmp_reg);
271
272 tmp_reg = hw_read(ci, OP_USBMODE, ~0);
273 seq_printf(s, "USBMODE reg: %08x\n", tmp_reg);
274
275 tmp_reg = hw_read(ci, OP_USBCMD, ~0);
276 seq_printf(s, "USBCMD reg: %08x\n", tmp_reg);
277
278 tmp_reg = hw_read(ci, OP_PORTSC, ~0);
279 seq_printf(s, "PORTSC reg: %08x\n", tmp_reg);
280
281 if (ci->is_otg) {
282 tmp_reg = hw_read_otgsc(ci, ~0);
283 seq_printf(s, "OTGSC reg: %08x\n", tmp_reg);
284 }
285
286 return 0;
287}
288
289static int ci_registers_open(struct inode *inode, struct file *file)
290{
291 return single_open(file, ci_registers_show, inode->i_private);
292}
293
294static const struct file_operations ci_registers_fops = {
295 .open = ci_registers_open,
296 .read = seq_read,
297 .llseek = seq_lseek,
298 .release = single_release,
299};
300
Alexander Shishkine443b332012-05-11 17:25:46 +0300301/**
302 * dbg_create_files: initializes the attribute interface
Alexander Shishkin2d651282013-03-30 12:53:51 +0200303 * @ci: device
Alexander Shishkine443b332012-05-11 17:25:46 +0300304 *
305 * This function returns an error code
306 */
Alexander Shishkin8e229782013-06-24 14:46:36 +0300307int dbg_create_files(struct ci_hdrc *ci)
Alexander Shishkine443b332012-05-11 17:25:46 +0300308{
Alexander Shishkin2d651282013-03-30 12:53:51 +0200309 struct dentry *dent;
Alexander Shishkine443b332012-05-11 17:25:46 +0300310
Alexander Shishkin2d651282013-03-30 12:53:51 +0200311 ci->debugfs = debugfs_create_dir(dev_name(ci->dev), NULL);
312 if (!ci->debugfs)
313 return -ENOMEM;
Alexander Shishkine443b332012-05-11 17:25:46 +0300314
Alexander Shishkin2d651282013-03-30 12:53:51 +0200315 dent = debugfs_create_file("device", S_IRUGO, ci->debugfs, ci,
316 &ci_device_fops);
317 if (!dent)
318 goto err;
319
320 dent = debugfs_create_file("port_test", S_IRUGO | S_IWUSR, ci->debugfs,
321 ci, &ci_port_test_fops);
322 if (!dent)
323 goto err;
324
325 dent = debugfs_create_file("qheads", S_IRUGO, ci->debugfs, ci,
326 &ci_qheads_fops);
327 if (!dent)
328 goto err;
329
330 dent = debugfs_create_file("requests", S_IRUGO, ci->debugfs, ci,
331 &ci_requests_fops);
Alexander Shishkinc8e333a2013-03-30 12:53:52 +0200332 if (!dent)
333 goto err;
334
335 dent = debugfs_create_file("role", S_IRUGO | S_IWUSR, ci->debugfs, ci,
336 &ci_role_fops);
Li Junc4a8b632014-04-23 15:56:40 +0800337 if (!dent)
338 goto err;
339
340 dent = debugfs_create_file("registers", S_IRUGO, ci->debugfs, ci,
341 &ci_registers_fops);
342
Alexander Shishkin2d651282013-03-30 12:53:51 +0200343 if (dent)
344 return 0;
345err:
346 debugfs_remove_recursive(ci->debugfs);
347 return -ENOMEM;
Alexander Shishkine443b332012-05-11 17:25:46 +0300348}
349
350/**
351 * dbg_remove_files: destroys the attribute interface
Alexander Shishkin2d651282013-03-30 12:53:51 +0200352 * @ci: device
Alexander Shishkine443b332012-05-11 17:25:46 +0300353 */
Alexander Shishkin8e229782013-06-24 14:46:36 +0300354void dbg_remove_files(struct ci_hdrc *ci)
Alexander Shishkine443b332012-05-11 17:25:46 +0300355{
Alexander Shishkin2d651282013-03-30 12:53:51 +0200356 debugfs_remove_recursive(ci->debugfs);
Alexander Shishkine443b332012-05-11 17:25:46 +0300357}