0001
0002
0003
0004
0005
0006 #include <linux/circ_buf.h>
0007 #include <linux/debugfs.h>
0008 #include <linux/delay.h>
0009 #include <linux/fs.h>
0010 #include <linux/module.h>
0011 #include <linux/mutex.h>
0012 #include <linux/platform_data/cros_ec_commands.h>
0013 #include <linux/platform_data/cros_ec_proto.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/poll.h>
0016 #include <linux/sched.h>
0017 #include <linux/slab.h>
0018 #include <linux/wait.h>
0019
0020 #define DRV_NAME "cros-ec-debugfs"
0021
0022 #define LOG_SHIFT 14
0023 #define LOG_SIZE (1 << LOG_SHIFT)
0024 #define LOG_POLL_SEC 10
0025
0026 #define CIRC_ADD(idx, size, value) (((idx) + (value)) & ((size) - 1))
0027
0028
0029 static DECLARE_WAIT_QUEUE_HEAD(cros_ec_debugfs_log_wq);
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042 struct cros_ec_debugfs {
0043 struct cros_ec_dev *ec;
0044 struct dentry *dir;
0045
0046 struct circ_buf log_buffer;
0047 struct cros_ec_command *read_msg;
0048 struct mutex log_mutex;
0049 struct delayed_work log_poll_work;
0050
0051 struct debugfs_blob_wrapper panicinfo_blob;
0052 };
0053
0054
0055
0056
0057
0058 static void cros_ec_console_log_work(struct work_struct *__work)
0059 {
0060 struct cros_ec_debugfs *debug_info =
0061 container_of(to_delayed_work(__work),
0062 struct cros_ec_debugfs,
0063 log_poll_work);
0064 struct cros_ec_dev *ec = debug_info->ec;
0065 struct circ_buf *cb = &debug_info->log_buffer;
0066 struct cros_ec_command snapshot_msg = {
0067 .command = EC_CMD_CONSOLE_SNAPSHOT + ec->cmd_offset,
0068 };
0069
0070 struct ec_params_console_read_v1 *read_params =
0071 (struct ec_params_console_read_v1 *)debug_info->read_msg->data;
0072 uint8_t *ec_buffer = (uint8_t *)debug_info->read_msg->data;
0073 int idx;
0074 int buf_space;
0075 int ret;
0076
0077 ret = cros_ec_cmd_xfer_status(ec->ec_dev, &snapshot_msg);
0078 if (ret < 0)
0079 goto resched;
0080
0081
0082 mutex_lock(&debug_info->log_mutex);
0083 buf_space = CIRC_SPACE(cb->head, cb->tail, LOG_SIZE);
0084
0085 while (1) {
0086 if (!buf_space) {
0087 dev_info_once(ec->dev,
0088 "Some logs may have been dropped...\n");
0089 break;
0090 }
0091
0092 memset(read_params, '\0', sizeof(*read_params));
0093 read_params->subcmd = CONSOLE_READ_RECENT;
0094 ret = cros_ec_cmd_xfer_status(ec->ec_dev,
0095 debug_info->read_msg);
0096 if (ret < 0)
0097 break;
0098
0099
0100 if (ret == 0 || ec_buffer[0] == '\0')
0101 break;
0102
0103 idx = 0;
0104 while (idx < ret && ec_buffer[idx] != '\0' && buf_space > 0) {
0105 cb->buf[cb->head] = ec_buffer[idx];
0106 cb->head = CIRC_ADD(cb->head, LOG_SIZE, 1);
0107 idx++;
0108 buf_space--;
0109 }
0110
0111 wake_up(&cros_ec_debugfs_log_wq);
0112 }
0113
0114 mutex_unlock(&debug_info->log_mutex);
0115
0116 resched:
0117 schedule_delayed_work(&debug_info->log_poll_work,
0118 msecs_to_jiffies(LOG_POLL_SEC * 1000));
0119 }
0120
0121 static int cros_ec_console_log_open(struct inode *inode, struct file *file)
0122 {
0123 file->private_data = inode->i_private;
0124
0125 return stream_open(inode, file);
0126 }
0127
0128 static ssize_t cros_ec_console_log_read(struct file *file, char __user *buf,
0129 size_t count, loff_t *ppos)
0130 {
0131 struct cros_ec_debugfs *debug_info = file->private_data;
0132 struct circ_buf *cb = &debug_info->log_buffer;
0133 ssize_t ret;
0134
0135 mutex_lock(&debug_info->log_mutex);
0136
0137 while (!CIRC_CNT(cb->head, cb->tail, LOG_SIZE)) {
0138 if (file->f_flags & O_NONBLOCK) {
0139 ret = -EAGAIN;
0140 goto error;
0141 }
0142
0143 mutex_unlock(&debug_info->log_mutex);
0144
0145 ret = wait_event_interruptible(cros_ec_debugfs_log_wq,
0146 CIRC_CNT(cb->head, cb->tail, LOG_SIZE));
0147 if (ret < 0)
0148 return ret;
0149
0150 mutex_lock(&debug_info->log_mutex);
0151 }
0152
0153
0154
0155
0156 ret = min_t(size_t, CIRC_CNT_TO_END(cb->head, cb->tail, LOG_SIZE),
0157 count);
0158
0159 if (copy_to_user(buf, cb->buf + cb->tail, ret)) {
0160 ret = -EFAULT;
0161 goto error;
0162 }
0163
0164 cb->tail = CIRC_ADD(cb->tail, LOG_SIZE, ret);
0165
0166 error:
0167 mutex_unlock(&debug_info->log_mutex);
0168 return ret;
0169 }
0170
0171 static __poll_t cros_ec_console_log_poll(struct file *file,
0172 poll_table *wait)
0173 {
0174 struct cros_ec_debugfs *debug_info = file->private_data;
0175 __poll_t mask = 0;
0176
0177 poll_wait(file, &cros_ec_debugfs_log_wq, wait);
0178
0179 mutex_lock(&debug_info->log_mutex);
0180 if (CIRC_CNT(debug_info->log_buffer.head,
0181 debug_info->log_buffer.tail,
0182 LOG_SIZE))
0183 mask |= EPOLLIN | EPOLLRDNORM;
0184 mutex_unlock(&debug_info->log_mutex);
0185
0186 return mask;
0187 }
0188
0189 static int cros_ec_console_log_release(struct inode *inode, struct file *file)
0190 {
0191 return 0;
0192 }
0193
0194 static ssize_t cros_ec_pdinfo_read(struct file *file,
0195 char __user *user_buf,
0196 size_t count,
0197 loff_t *ppos)
0198 {
0199 char read_buf[EC_USB_PD_MAX_PORTS * 40], *p = read_buf;
0200 struct cros_ec_debugfs *debug_info = file->private_data;
0201 struct cros_ec_device *ec_dev = debug_info->ec->ec_dev;
0202 struct {
0203 struct cros_ec_command msg;
0204 union {
0205 struct ec_response_usb_pd_control_v1 resp;
0206 struct ec_params_usb_pd_control params;
0207 };
0208 } __packed ec_buf;
0209 struct cros_ec_command *msg;
0210 struct ec_response_usb_pd_control_v1 *resp;
0211 struct ec_params_usb_pd_control *params;
0212 int i;
0213
0214 msg = &ec_buf.msg;
0215 params = (struct ec_params_usb_pd_control *)msg->data;
0216 resp = (struct ec_response_usb_pd_control_v1 *)msg->data;
0217
0218 msg->command = EC_CMD_USB_PD_CONTROL;
0219 msg->version = 1;
0220 msg->insize = sizeof(*resp);
0221 msg->outsize = sizeof(*params);
0222
0223
0224
0225
0226
0227 for (i = 0; i < EC_USB_PD_MAX_PORTS; ++i) {
0228 params->port = i;
0229 params->role = 0;
0230 params->mux = 0;
0231 params->swap = 0;
0232
0233 if (cros_ec_cmd_xfer_status(ec_dev, msg) < 0)
0234 break;
0235
0236 p += scnprintf(p, sizeof(read_buf) + read_buf - p,
0237 "p%d: %s en:%.2x role:%.2x pol:%.2x\n", i,
0238 resp->state, resp->enabled, resp->role,
0239 resp->polarity);
0240 }
0241
0242 return simple_read_from_buffer(user_buf, count, ppos,
0243 read_buf, p - read_buf);
0244 }
0245
0246 static bool cros_ec_uptime_is_supported(struct cros_ec_device *ec_dev)
0247 {
0248 struct {
0249 struct cros_ec_command cmd;
0250 struct ec_response_uptime_info resp;
0251 } __packed msg = {};
0252 int ret;
0253
0254 msg.cmd.command = EC_CMD_GET_UPTIME_INFO;
0255 msg.cmd.insize = sizeof(msg.resp);
0256
0257 ret = cros_ec_cmd_xfer_status(ec_dev, &msg.cmd);
0258 if (ret == -EPROTO && msg.cmd.result == EC_RES_INVALID_COMMAND)
0259 return false;
0260
0261
0262 return true;
0263 }
0264
0265 static ssize_t cros_ec_uptime_read(struct file *file, char __user *user_buf,
0266 size_t count, loff_t *ppos)
0267 {
0268 struct cros_ec_debugfs *debug_info = file->private_data;
0269 struct cros_ec_device *ec_dev = debug_info->ec->ec_dev;
0270 struct {
0271 struct cros_ec_command cmd;
0272 struct ec_response_uptime_info resp;
0273 } __packed msg = {};
0274 struct ec_response_uptime_info *resp;
0275 char read_buf[32];
0276 int ret;
0277
0278 resp = (struct ec_response_uptime_info *)&msg.resp;
0279
0280 msg.cmd.command = EC_CMD_GET_UPTIME_INFO;
0281 msg.cmd.insize = sizeof(*resp);
0282
0283 ret = cros_ec_cmd_xfer_status(ec_dev, &msg.cmd);
0284 if (ret < 0)
0285 return ret;
0286
0287 ret = scnprintf(read_buf, sizeof(read_buf), "%u\n",
0288 resp->time_since_ec_boot_ms);
0289
0290 return simple_read_from_buffer(user_buf, count, ppos, read_buf, ret);
0291 }
0292
0293 static const struct file_operations cros_ec_console_log_fops = {
0294 .owner = THIS_MODULE,
0295 .open = cros_ec_console_log_open,
0296 .read = cros_ec_console_log_read,
0297 .llseek = no_llseek,
0298 .poll = cros_ec_console_log_poll,
0299 .release = cros_ec_console_log_release,
0300 };
0301
0302 static const struct file_operations cros_ec_pdinfo_fops = {
0303 .owner = THIS_MODULE,
0304 .open = simple_open,
0305 .read = cros_ec_pdinfo_read,
0306 .llseek = default_llseek,
0307 };
0308
0309 static const struct file_operations cros_ec_uptime_fops = {
0310 .owner = THIS_MODULE,
0311 .open = simple_open,
0312 .read = cros_ec_uptime_read,
0313 .llseek = default_llseek,
0314 };
0315
0316 static int ec_read_version_supported(struct cros_ec_dev *ec)
0317 {
0318 struct ec_params_get_cmd_versions_v1 *params;
0319 struct ec_response_get_cmd_versions *response;
0320 int ret;
0321
0322 struct cros_ec_command *msg;
0323
0324 msg = kzalloc(sizeof(*msg) + max(sizeof(*params), sizeof(*response)),
0325 GFP_KERNEL);
0326 if (!msg)
0327 return 0;
0328
0329 msg->command = EC_CMD_GET_CMD_VERSIONS + ec->cmd_offset;
0330 msg->outsize = sizeof(*params);
0331 msg->insize = sizeof(*response);
0332
0333 params = (struct ec_params_get_cmd_versions_v1 *)msg->data;
0334 params->cmd = EC_CMD_CONSOLE_READ;
0335 response = (struct ec_response_get_cmd_versions *)msg->data;
0336
0337 ret = cros_ec_cmd_xfer_status(ec->ec_dev, msg) >= 0 &&
0338 response->version_mask & EC_VER_MASK(1);
0339
0340 kfree(msg);
0341
0342 return ret;
0343 }
0344
0345 static int cros_ec_create_console_log(struct cros_ec_debugfs *debug_info)
0346 {
0347 struct cros_ec_dev *ec = debug_info->ec;
0348 char *buf;
0349 int read_params_size;
0350 int read_response_size;
0351
0352
0353
0354
0355
0356 if (!ec_read_version_supported(ec))
0357 return 0;
0358
0359 buf = devm_kzalloc(ec->dev, LOG_SIZE, GFP_KERNEL);
0360 if (!buf)
0361 return -ENOMEM;
0362
0363 read_params_size = sizeof(struct ec_params_console_read_v1);
0364 read_response_size = ec->ec_dev->max_response;
0365 debug_info->read_msg = devm_kzalloc(ec->dev,
0366 sizeof(*debug_info->read_msg) +
0367 max(read_params_size, read_response_size), GFP_KERNEL);
0368 if (!debug_info->read_msg)
0369 return -ENOMEM;
0370
0371 debug_info->read_msg->version = 1;
0372 debug_info->read_msg->command = EC_CMD_CONSOLE_READ + ec->cmd_offset;
0373 debug_info->read_msg->outsize = read_params_size;
0374 debug_info->read_msg->insize = read_response_size;
0375
0376 debug_info->log_buffer.buf = buf;
0377 debug_info->log_buffer.head = 0;
0378 debug_info->log_buffer.tail = 0;
0379
0380 mutex_init(&debug_info->log_mutex);
0381
0382 debugfs_create_file("console_log", S_IFREG | 0444, debug_info->dir,
0383 debug_info, &cros_ec_console_log_fops);
0384
0385 INIT_DELAYED_WORK(&debug_info->log_poll_work,
0386 cros_ec_console_log_work);
0387 schedule_delayed_work(&debug_info->log_poll_work, 0);
0388
0389 return 0;
0390 }
0391
0392 static void cros_ec_cleanup_console_log(struct cros_ec_debugfs *debug_info)
0393 {
0394 if (debug_info->log_buffer.buf) {
0395 cancel_delayed_work_sync(&debug_info->log_poll_work);
0396 mutex_destroy(&debug_info->log_mutex);
0397 }
0398 }
0399
0400 static int cros_ec_create_panicinfo(struct cros_ec_debugfs *debug_info)
0401 {
0402 struct cros_ec_device *ec_dev = debug_info->ec->ec_dev;
0403 int ret;
0404 struct cros_ec_command *msg;
0405 int insize;
0406
0407 insize = ec_dev->max_response;
0408
0409 msg = devm_kzalloc(debug_info->ec->dev,
0410 sizeof(*msg) + insize, GFP_KERNEL);
0411 if (!msg)
0412 return -ENOMEM;
0413
0414 msg->command = EC_CMD_GET_PANIC_INFO;
0415 msg->insize = insize;
0416
0417 ret = cros_ec_cmd_xfer_status(ec_dev, msg);
0418 if (ret < 0) {
0419 ret = 0;
0420 goto free;
0421 }
0422
0423
0424 if (ret == 0)
0425 goto free;
0426
0427 debug_info->panicinfo_blob.data = msg->data;
0428 debug_info->panicinfo_blob.size = ret;
0429
0430 debugfs_create_blob("panicinfo", S_IFREG | 0444, debug_info->dir,
0431 &debug_info->panicinfo_blob);
0432
0433 return 0;
0434
0435 free:
0436 devm_kfree(debug_info->ec->dev, msg);
0437 return ret;
0438 }
0439
0440 static int cros_ec_debugfs_probe(struct platform_device *pd)
0441 {
0442 struct cros_ec_dev *ec = dev_get_drvdata(pd->dev.parent);
0443 struct cros_ec_platform *ec_platform = dev_get_platdata(ec->dev);
0444 const char *name = ec_platform->ec_name;
0445 struct cros_ec_debugfs *debug_info;
0446 int ret;
0447
0448 debug_info = devm_kzalloc(ec->dev, sizeof(*debug_info), GFP_KERNEL);
0449 if (!debug_info)
0450 return -ENOMEM;
0451
0452 debug_info->ec = ec;
0453 debug_info->dir = debugfs_create_dir(name, NULL);
0454
0455 ret = cros_ec_create_panicinfo(debug_info);
0456 if (ret)
0457 goto remove_debugfs;
0458
0459 ret = cros_ec_create_console_log(debug_info);
0460 if (ret)
0461 goto remove_debugfs;
0462
0463 debugfs_create_file("pdinfo", 0444, debug_info->dir, debug_info,
0464 &cros_ec_pdinfo_fops);
0465
0466 if (cros_ec_uptime_is_supported(ec->ec_dev))
0467 debugfs_create_file("uptime", 0444, debug_info->dir, debug_info,
0468 &cros_ec_uptime_fops);
0469
0470 debugfs_create_x32("last_resume_result", 0444, debug_info->dir,
0471 &ec->ec_dev->last_resume_result);
0472
0473 ec->debug_info = debug_info;
0474
0475 dev_set_drvdata(&pd->dev, ec);
0476
0477 return 0;
0478
0479 remove_debugfs:
0480 debugfs_remove_recursive(debug_info->dir);
0481 return ret;
0482 }
0483
0484 static int cros_ec_debugfs_remove(struct platform_device *pd)
0485 {
0486 struct cros_ec_dev *ec = dev_get_drvdata(pd->dev.parent);
0487
0488 debugfs_remove_recursive(ec->debug_info->dir);
0489 cros_ec_cleanup_console_log(ec->debug_info);
0490
0491 return 0;
0492 }
0493
0494 static int __maybe_unused cros_ec_debugfs_suspend(struct device *dev)
0495 {
0496 struct cros_ec_dev *ec = dev_get_drvdata(dev);
0497
0498 if (ec->debug_info->log_buffer.buf)
0499 cancel_delayed_work_sync(&ec->debug_info->log_poll_work);
0500
0501 return 0;
0502 }
0503
0504 static int __maybe_unused cros_ec_debugfs_resume(struct device *dev)
0505 {
0506 struct cros_ec_dev *ec = dev_get_drvdata(dev);
0507
0508 if (ec->debug_info->log_buffer.buf)
0509 schedule_delayed_work(&ec->debug_info->log_poll_work, 0);
0510
0511 return 0;
0512 }
0513
0514 static SIMPLE_DEV_PM_OPS(cros_ec_debugfs_pm_ops,
0515 cros_ec_debugfs_suspend, cros_ec_debugfs_resume);
0516
0517 static struct platform_driver cros_ec_debugfs_driver = {
0518 .driver = {
0519 .name = DRV_NAME,
0520 .pm = &cros_ec_debugfs_pm_ops,
0521 },
0522 .probe = cros_ec_debugfs_probe,
0523 .remove = cros_ec_debugfs_remove,
0524 };
0525
0526 module_platform_driver(cros_ec_debugfs_driver);
0527
0528 MODULE_LICENSE("GPL");
0529 MODULE_DESCRIPTION("Debug logs for ChromeOS EC");
0530 MODULE_ALIAS("platform:" DRV_NAME);