aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/platform/chrome/wilco_ec/Kconfig10
-rw-r--r--drivers/platform/chrome/wilco_ec/Makefile2
-rw-r--r--drivers/platform/chrome/wilco_ec/core.c14
-rw-r--r--drivers/platform/chrome/wilco_ec/debugfs.c238
4 files changed, 264 insertions, 0 deletions
diff --git a/drivers/platform/chrome/wilco_ec/Kconfig b/drivers/platform/chrome/wilco_ec/Kconfig
index c6bc4e8f3062..4a119ced4d0c 100644
--- a/drivers/platform/chrome/wilco_ec/Kconfig
+++ b/drivers/platform/chrome/wilco_ec/Kconfig
@@ -8,3 +8,13 @@ config WILCO_EC
8 8
9 To compile this driver as a module, choose M here: the 9 To compile this driver as a module, choose M here: the
10 module will be called wilco_ec. 10 module will be called wilco_ec.
11
12config WILCO_EC_DEBUGFS
13 tristate "Enable raw access to EC via debugfs"
14 depends on WILCO_EC
15 help
16 If you say Y here, you get support for sending raw commands to
17 the Wilco EC via debugfs. These commands do not do any byte
18 manipulation and allow for testing arbitrary commands. This
19 interface is intended for debug only and will not be present
20 on production devices.
diff --git a/drivers/platform/chrome/wilco_ec/Makefile b/drivers/platform/chrome/wilco_ec/Makefile
index 03b32301dc61..063e7fb4ea17 100644
--- a/drivers/platform/chrome/wilco_ec/Makefile
+++ b/drivers/platform/chrome/wilco_ec/Makefile
@@ -2,3 +2,5 @@
2 2
3wilco_ec-objs := core.o mailbox.o 3wilco_ec-objs := core.o mailbox.o
4obj-$(CONFIG_WILCO_EC) += wilco_ec.o 4obj-$(CONFIG_WILCO_EC) += wilco_ec.o
5wilco_ec_debugfs-objs := debugfs.o
6obj-$(CONFIG_WILCO_EC_DEBUGFS) += wilco_ec_debugfs.o
diff --git a/drivers/platform/chrome/wilco_ec/core.c b/drivers/platform/chrome/wilco_ec/core.c
index 20ecc580d108..af5fd288b63b 100644
--- a/drivers/platform/chrome/wilco_ec/core.c
+++ b/drivers/platform/chrome/wilco_ec/core.c
@@ -69,11 +69,25 @@ static int wilco_ec_probe(struct platform_device *pdev)
69 cros_ec_lpc_mec_init(ec->io_packet->start, 69 cros_ec_lpc_mec_init(ec->io_packet->start,
70 ec->io_packet->start + EC_MAILBOX_DATA_SIZE); 70 ec->io_packet->start + EC_MAILBOX_DATA_SIZE);
71 71
72 /*
73 * Register a child device that will be found by the debugfs driver.
74 * Ignore failure.
75 */
76 ec->debugfs_pdev = platform_device_register_data(dev,
77 "wilco-ec-debugfs",
78 PLATFORM_DEVID_AUTO,
79 NULL, 0);
80
72 return 0; 81 return 0;
73} 82}
74 83
75static int wilco_ec_remove(struct platform_device *pdev) 84static int wilco_ec_remove(struct platform_device *pdev)
76{ 85{
86 struct wilco_ec_device *ec = platform_get_drvdata(pdev);
87
88 if (ec->debugfs_pdev)
89 platform_device_unregister(ec->debugfs_pdev);
90
77 /* Teardown cros_ec interface */ 91 /* Teardown cros_ec interface */
78 cros_ec_lpc_mec_destroy(); 92 cros_ec_lpc_mec_destroy();
79 93
diff --git a/drivers/platform/chrome/wilco_ec/debugfs.c b/drivers/platform/chrome/wilco_ec/debugfs.c
new file mode 100644
index 000000000000..c090db2cd5be
--- /dev/null
+++ b/drivers/platform/chrome/wilco_ec/debugfs.c
@@ -0,0 +1,238 @@
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * debugfs attributes for Wilco EC
4 *
5 * Copyright 2019 Google LLC
6 *
7 * There is only one attribute used for debugging, called raw.
8 * You can write a hexadecimal sentence to raw, and that series of bytes
9 * will be sent to the EC. Then, you can read the bytes of response
10 * by reading from raw.
11 *
12 * For writing:
13 * Bytes 0-1 indicate the message type:
14 * 00 F0 = Execute Legacy Command
15 * 00 F2 = Read/Write NVRAM Property
16 * Byte 2 provides the command code
17 * Bytes 3+ consist of the data passed in the request
18 *
19 * When referencing the EC interface spec, byte 2 corresponds to MBOX[0],
20 * byte 3 corresponds to MBOX[1], etc.
21 *
22 * At least three bytes are required, for the msg type and command,
23 * with additional bytes optional for additional data.
24 *
25 * Example:
26 * // Request EC info type 3 (EC firmware build date)
27 * $ echo 00 f0 38 00 03 00 > raw
28 * // View the result. The decoded ASCII result "12/21/18" is
29 * // included after the raw hex.
30 * $ cat raw
31 * 00 31 32 2f 32 31 2f 31 38 00 38 00 01 00 2f 00 .12/21/18.8...
32 */
33
34#include <linux/ctype.h>
35#include <linux/debugfs.h>
36#include <linux/fs.h>
37#include <linux/module.h>
38#include <linux/platform_data/wilco-ec.h>
39#include <linux/platform_device.h>
40
41#define DRV_NAME "wilco-ec-debugfs"
42
43/* The 256 raw bytes will take up more space when represented as a hex string */
44#define FORMATTED_BUFFER_SIZE (EC_MAILBOX_DATA_SIZE_EXTENDED * 4)
45
46struct wilco_ec_debugfs {
47 struct wilco_ec_device *ec;
48 struct dentry *dir;
49 size_t response_size;
50 u8 raw_data[EC_MAILBOX_DATA_SIZE_EXTENDED];
51 u8 formatted_data[FORMATTED_BUFFER_SIZE];
52};
53static struct wilco_ec_debugfs *debug_info;
54
55/**
56 * parse_hex_sentence() - Convert a ascii hex representation into byte array.
57 * @in: Input buffer of ascii.
58 * @isize: Length of input buffer.
59 * @out: Output buffer.
60 * @osize: Length of output buffer, e.g. max number of bytes to parse.
61 *
62 * An valid input is a series of ascii hexadecimal numbers, separated by spaces.
63 * An example valid input is
64 * " 00 f2 0 000076 6 0 ff"
65 *
66 * If an individual "word" within the hex sentence is longer than MAX_WORD_SIZE,
67 * then the sentence is illegal, and parsing will fail.
68 *
69 * Return: Number of bytes parsed, or negative error code on failure.
70 */
71static int parse_hex_sentence(const char *in, int isize, u8 *out, int osize)
72{
73 int n_parsed = 0;
74 int word_start = 0;
75 int word_end;
76 int word_len;
77 /* Temp buffer for holding a "word" of chars that represents one byte */
78 #define MAX_WORD_SIZE 16
79 char tmp[MAX_WORD_SIZE + 1];
80 u8 byte;
81
82 while (word_start < isize && n_parsed < osize) {
83 /* Find the start of the next word */
84 while (word_start < isize && isspace(in[word_start]))
85 word_start++;
86 /* reached the end of the input before next word? */
87 if (word_start >= isize)
88 break;
89
90 /* Find the end of this word */
91 word_end = word_start;
92 while (word_end < isize && !isspace(in[word_end]))
93 word_end++;
94
95 /* Copy to a tmp NULL terminated string */
96 word_len = word_end - word_start;
97 if (word_len > MAX_WORD_SIZE)
98 return -EINVAL;
99 memcpy(tmp, in + word_start, word_len);
100 tmp[word_len] = '\0';
101
102 /*
103 * Convert from hex string, place in output. If fails to parse,
104 * just return -EINVAL because specific error code is only
105 * relevant for this one word, returning it would be confusing.
106 */
107 if (kstrtou8(tmp, 16, &byte))
108 return -EINVAL;
109 out[n_parsed++] = byte;
110
111 word_start = word_end;
112 }
113 return n_parsed;
114}
115
116/* The message type takes up two bytes*/
117#define TYPE_AND_DATA_SIZE ((EC_MAILBOX_DATA_SIZE) + 2)
118
119static ssize_t raw_write(struct file *file, const char __user *user_buf,
120 size_t count, loff_t *ppos)
121{
122 char *buf = debug_info->formatted_data;
123 struct wilco_ec_message msg;
124 u8 request_data[TYPE_AND_DATA_SIZE];
125 ssize_t kcount;
126 int ret;
127
128 if (count > FORMATTED_BUFFER_SIZE)
129 return -EINVAL;
130
131 kcount = simple_write_to_buffer(buf, FORMATTED_BUFFER_SIZE, ppos,
132 user_buf, count);
133 if (kcount < 0)
134 return kcount;
135
136 ret = parse_hex_sentence(buf, kcount, request_data, TYPE_AND_DATA_SIZE);
137 if (ret < 0)
138 return ret;
139 /* Need at least two bytes for message type and one for command */
140 if (ret < 3)
141 return -EINVAL;
142
143 /* Clear response data buffer */
144 memset(debug_info->raw_data, '\0', EC_MAILBOX_DATA_SIZE_EXTENDED);
145
146 msg.type = request_data[0] << 8 | request_data[1];
147 msg.flags = WILCO_EC_FLAG_RAW;
148 msg.command = request_data[2];
149 msg.request_data = ret > 3 ? request_data + 3 : 0;
150 msg.request_size = ret - 3;
151 msg.response_data = debug_info->raw_data;
152 msg.response_size = EC_MAILBOX_DATA_SIZE;
153
154 /* Telemetry commands use extended response data */
155 if (msg.type == WILCO_EC_MSG_TELEMETRY_LONG) {
156 msg.flags |= WILCO_EC_FLAG_EXTENDED_DATA;
157 msg.response_size = EC_MAILBOX_DATA_SIZE_EXTENDED;
158 }
159
160 ret = wilco_ec_mailbox(debug_info->ec, &msg);
161 if (ret < 0)
162 return ret;
163 debug_info->response_size = ret;
164
165 return count;
166}
167
168static ssize_t raw_read(struct file *file, char __user *user_buf, size_t count,
169 loff_t *ppos)
170{
171 int fmt_len = 0;
172
173 if (debug_info->response_size) {
174 fmt_len = hex_dump_to_buffer(debug_info->raw_data,
175 debug_info->response_size,
176 16, 1, debug_info->formatted_data,
177 FORMATTED_BUFFER_SIZE, true);
178 /* Only return response the first time it is read */
179 debug_info->response_size = 0;
180 }
181
182 return simple_read_from_buffer(user_buf, count, ppos,
183 debug_info->formatted_data, fmt_len);
184}
185
186static const struct file_operations fops_raw = {
187 .owner = THIS_MODULE,
188 .read = raw_read,
189 .write = raw_write,
190 .llseek = no_llseek,
191};
192
193/**
194 * wilco_ec_debugfs_probe() - Create the debugfs node
195 * @pdev: The platform device, probably created in core.c
196 *
197 * Try to create a debugfs node. If it fails, then we don't want to change
198 * behavior at all, this is for debugging after all. Just fail silently.
199 *
200 * Return: 0 always.
201 */
202static int wilco_ec_debugfs_probe(struct platform_device *pdev)
203{
204 struct wilco_ec_device *ec = dev_get_drvdata(pdev->dev.parent);
205
206 debug_info = devm_kzalloc(&pdev->dev, sizeof(*debug_info), GFP_KERNEL);
207 if (!debug_info)
208 return 0;
209 debug_info->ec = ec;
210 debug_info->dir = debugfs_create_dir("wilco_ec", NULL);
211 if (!debug_info->dir)
212 return 0;
213 debugfs_create_file("raw", 0644, debug_info->dir, NULL, &fops_raw);
214
215 return 0;
216}
217
218static int wilco_ec_debugfs_remove(struct platform_device *pdev)
219{
220 debugfs_remove_recursive(debug_info->dir);
221
222 return 0;
223}
224
225static struct platform_driver wilco_ec_debugfs_driver = {
226 .driver = {
227 .name = DRV_NAME,
228 },
229 .probe = wilco_ec_debugfs_probe,
230 .remove = wilco_ec_debugfs_remove,
231};
232
233module_platform_driver(wilco_ec_debugfs_driver);
234
235MODULE_ALIAS("platform:" DRV_NAME);
236MODULE_AUTHOR("Nick Crews <ncrews@chromium.org>");
237MODULE_LICENSE("GPL v2");
238MODULE_DESCRIPTION("Wilco EC debugfs driver");