blob: 44ee78a10eb8f102d6efdeb5863c4c5ae8408c0f [file] [log] [blame]
Pawel Laszczak8bc19012019-07-02 14:38:01 +01001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Cadence USBSS DRD Controller DebugFS filer.
4 *
5 * Copyright (C) 2018-2019 Cadence.
6 *
7 * Author: Pawel Laszczak <pawell@cadence.com>
8 */
9
10#include <linux/types.h>
11#include <linux/debugfs.h>
12#include <linux/seq_file.h>
13#include <linux/uaccess.h>
14
15#include "core.h"
16#include "gadget.h"
17#include "drd.h"
18
19static const char *const cdns3_mode[] = {
20 [USB_DR_MODE_UNKNOWN] = "unknown",
21 [USB_DR_MODE_OTG] = "otg",
22 [USB_DR_MODE_HOST] = "host",
23 [USB_DR_MODE_PERIPHERAL] = "device",
24};
25
26static int cdns3_mode_show(struct seq_file *s, void *unused)
27{
28 struct cdns3 *cdns = s->private;
29
30 seq_puts(s, cdns3_mode[cdns->current_dr_mode]);
31
32 return 0;
33}
34
35static int cdns3_mode_open(struct inode *inode, struct file *file)
36{
37 return single_open(file, cdns3_mode_show, inode->i_private);
38}
39
40static ssize_t cdns3_mode_write(struct file *file,
41 const char __user *ubuf,
42 size_t count, loff_t *ppos)
43{
44 struct seq_file *s = file->private_data;
45 struct cdns3 *cdns = s->private;
46 char buf[32];
47 int ret;
48
49 if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
50 return -EFAULT;
51
52 if (cdns->debug_disable) {
53 dev_err(cdns->dev,
54 "Mode can't be changed when disable is set\n");
55 return -EPERM;
56 }
57
58 ret = match_string(cdns3_mode, ARRAY_SIZE(cdns3_mode), buf);
59 if (ret < 0 || ret == USB_DR_MODE_UNKNOWN) {
60 dev_err(cdns->dev, "Failed: incorrect mode setting\n");
61 return -EINVAL;
62 }
63
64 if (cdns->current_dr_mode != ret) {
65 cdns->desired_dr_mode = ret;
66 cdns3_role_stop(cdns);
67 ret = cdns3_drd_update_mode(cdns);
68 if (ret)
69 return ret;
70
71 queue_work(system_freezable_wq, &cdns->role_switch_wq);
72 }
73
74 return count;
75}
76
77static const struct file_operations cdns3_mode_fops = {
78 .open = cdns3_mode_open,
79 .write = cdns3_mode_write,
80 .read = seq_read,
81 .llseek = seq_lseek,
82 .release = single_release,
83};
84
85static int cdns3_disable_show(struct seq_file *s, void *unused)
86{
87 struct cdns3 *cdns = s->private;
88
89 if (!cdns->debug_disable)
90 seq_puts(s, "0\n");
91 else
92 seq_puts(s, "1\n");
93
94 return 0;
95}
96
97static ssize_t cdns3_disable_write(struct file *file,
98 const char __user *ubuf,
99 size_t count, loff_t *ppos)
100{
101 struct seq_file *s = file->private_data;
102 struct cdns3 *cdns = s->private;
103 bool disable;
104 char buf[16];
105
106 if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
107 return -EFAULT;
108
109 if (kstrtobool(buf, &disable)) {
110 dev_err(cdns->dev, "wrong setting\n");
111 return -EINVAL;
112 }
113
114 if (disable != cdns->debug_disable) {
115 cdns->debug_disable = disable;
116 queue_work(system_freezable_wq, &cdns->role_switch_wq);
117 }
118
119 return count;
120}
121
122static int cdns3_disable_open(struct inode *inode, struct file *file)
123{
124 return single_open(file, cdns3_disable_show, inode->i_private);
125}
126
127static const struct file_operations cdns3_disable_fops = {
128 .open = cdns3_disable_open,
129 .write = cdns3_disable_write,
130 .read = seq_read,
131 .llseek = seq_lseek,
132 .release = single_release,
133};
134
135void cdns3_debugfs_init(struct cdns3 *cdns)
136{
137 struct dentry *root;
138
139 root = debugfs_create_dir(dev_name(cdns->dev), NULL);
140 cdns->root = root;
141 if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET) &&
142 IS_ENABLED(CONFIG_USB_CDNS3_HOST))
143 debugfs_create_file("mode", 0644, root, cdns,
144 &cdns3_mode_fops);
145
146 debugfs_create_file("disable", 0644, root, cdns,
147 &cdns3_disable_fops);
148}
149
150void cdns3_debugfs_exit(struct cdns3 *cdns)
151{
152 debugfs_remove_recursive(cdns->root);
153}