diff options
Diffstat (limited to 'drivers/platform/chrome/cros_ec_debugfs.c')
| -rw-r--r-- | drivers/platform/chrome/cros_ec_debugfs.c | 144 |
1 files changed, 95 insertions, 49 deletions
diff --git a/drivers/platform/chrome/cros_ec_debugfs.c b/drivers/platform/chrome/cros_ec_debugfs.c index 21d973fc6be2..d10f9561990c 100644 --- a/drivers/platform/chrome/cros_ec_debugfs.c +++ b/drivers/platform/chrome/cros_ec_debugfs.c @@ -7,6 +7,7 @@ #include <linux/debugfs.h> #include <linux/delay.h> #include <linux/fs.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/platform_data/cros_ec_commands.h> @@ -25,6 +26,10 @@ #define CIRC_ADD(idx, size, value) (((idx) + (value)) & ((size) - 1)) +static unsigned int log_poll_period_ms = LOG_POLL_SEC * MSEC_PER_SEC; +module_param(log_poll_period_ms, uint, 0644); +MODULE_PARM_DESC(log_poll_period_ms, "EC log polling period(ms)"); + /* waitqueue for log readers */ static DECLARE_WAIT_QUEUE_HEAD(cros_ec_debugfs_log_wq); @@ -38,6 +43,8 @@ static DECLARE_WAIT_QUEUE_HEAD(cros_ec_debugfs_log_wq); * @log_mutex: mutex to protect circular buffer * @log_poll_work: recurring task to poll EC for new console log data * @panicinfo_blob: panicinfo debugfs blob + * @notifier_panic: notifier_block to let kernel to flush buffered log + * when EC panic */ struct cros_ec_debugfs { struct cros_ec_dev *ec; @@ -49,11 +56,12 @@ struct cros_ec_debugfs { struct delayed_work log_poll_work; /* EC panicinfo */ struct debugfs_blob_wrapper panicinfo_blob; + struct notifier_block notifier_panic; }; /* * We need to make sure that the EC log buffer on the UART is large enough, - * so that it is unlikely enough to overlow within LOG_POLL_SEC. + * so that it is unlikely enough to overlow within log_poll_period_ms. */ static void cros_ec_console_log_work(struct work_struct *__work) { @@ -115,7 +123,7 @@ static void cros_ec_console_log_work(struct work_struct *__work) resched: schedule_delayed_work(&debug_info->log_poll_work, - msecs_to_jiffies(LOG_POLL_SEC * 1000)); + msecs_to_jiffies(log_poll_period_ms)); } static int cros_ec_console_log_open(struct inode *inode, struct file *file) @@ -199,22 +207,15 @@ static ssize_t cros_ec_pdinfo_read(struct file *file, char read_buf[EC_USB_PD_MAX_PORTS * 40], *p = read_buf; struct cros_ec_debugfs *debug_info = file->private_data; struct cros_ec_device *ec_dev = debug_info->ec->ec_dev; - struct { - struct cros_ec_command msg; - union { - struct ec_response_usb_pd_control_v1 resp; - struct ec_params_usb_pd_control params; - }; - } __packed ec_buf; - struct cros_ec_command *msg; - struct ec_response_usb_pd_control_v1 *resp; - struct ec_params_usb_pd_control *params; + DEFINE_RAW_FLEX(struct cros_ec_command, msg, data, + MAX(sizeof(struct ec_response_usb_pd_control_v1), + sizeof(struct ec_params_usb_pd_control))); + struct ec_response_usb_pd_control_v1 *resp = + (struct ec_response_usb_pd_control_v1 *)msg->data; + struct ec_params_usb_pd_control *params = + (struct ec_params_usb_pd_control *)msg->data; int i; - msg = &ec_buf.msg; - params = (struct ec_params_usb_pd_control *)msg->data; - resp = (struct ec_response_usb_pd_control_v1 *)msg->data; - msg->command = EC_CMD_USB_PD_CONTROL; msg->version = 1; msg->insize = sizeof(*resp); @@ -245,17 +246,15 @@ static ssize_t cros_ec_pdinfo_read(struct file *file, static bool cros_ec_uptime_is_supported(struct cros_ec_device *ec_dev) { - struct { - struct cros_ec_command cmd; - struct ec_response_uptime_info resp; - } __packed msg = {}; + DEFINE_RAW_FLEX(struct cros_ec_command, msg, data, + sizeof(struct ec_response_uptime_info)); int ret; - msg.cmd.command = EC_CMD_GET_UPTIME_INFO; - msg.cmd.insize = sizeof(msg.resp); + msg->command = EC_CMD_GET_UPTIME_INFO; + msg->insize = sizeof(struct ec_response_uptime_info); - ret = cros_ec_cmd_xfer_status(ec_dev, &msg.cmd); - if (ret == -EPROTO && msg.cmd.result == EC_RES_INVALID_COMMAND) + ret = cros_ec_cmd_xfer_status(ec_dev, msg); + if (ret == -EPROTO && msg->result == EC_RES_INVALID_COMMAND) return false; /* Other errors maybe a transient error, do not rule about support. */ @@ -267,20 +266,17 @@ static ssize_t cros_ec_uptime_read(struct file *file, char __user *user_buf, { struct cros_ec_debugfs *debug_info = file->private_data; struct cros_ec_device *ec_dev = debug_info->ec->ec_dev; - struct { - struct cros_ec_command cmd; - struct ec_response_uptime_info resp; - } __packed msg = {}; - struct ec_response_uptime_info *resp; + DEFINE_RAW_FLEX(struct cros_ec_command, msg, data, + sizeof(struct ec_response_uptime_info)); + struct ec_response_uptime_info *resp = + (struct ec_response_uptime_info *)msg->data; char read_buf[32]; int ret; - resp = (struct ec_response_uptime_info *)&msg.resp; - - msg.cmd.command = EC_CMD_GET_UPTIME_INFO; - msg.cmd.insize = sizeof(*resp); + msg->command = EC_CMD_GET_UPTIME_INFO; + msg->insize = sizeof(*resp); - ret = cros_ec_cmd_xfer_status(ec_dev, &msg.cmd); + ret = cros_ec_cmd_xfer_status(ec_dev, msg); if (ret < 0) return ret; @@ -294,7 +290,6 @@ static const struct file_operations cros_ec_console_log_fops = { .owner = THIS_MODULE, .open = cros_ec_console_log_open, .read = cros_ec_console_log_read, - .llseek = no_llseek, .poll = cros_ec_console_log_poll, .release = cros_ec_console_log_release, }; @@ -326,6 +321,7 @@ static int ec_read_version_supported(struct cros_ec_dev *ec) if (!msg) return 0; + msg->version = 1; msg->command = EC_CMD_GET_CMD_VERSIONS + ec->cmd_offset; msg->outsize = sizeof(*params); msg->insize = sizeof(*response); @@ -397,24 +393,48 @@ static void cros_ec_cleanup_console_log(struct cros_ec_debugfs *debug_info) } } -static int cros_ec_create_panicinfo(struct cros_ec_debugfs *debug_info) +/* + * Returns the size of the panicinfo data fetched from the EC + */ +static int cros_ec_get_panicinfo(struct cros_ec_device *ec_dev, uint8_t *data, + int data_size) { - struct cros_ec_device *ec_dev = debug_info->ec->ec_dev; int ret; struct cros_ec_command *msg; - int insize; - insize = ec_dev->max_response; + if (!data || data_size <= 0 || data_size > ec_dev->max_response) + return -EINVAL; - msg = devm_kzalloc(debug_info->ec->dev, - sizeof(*msg) + insize, GFP_KERNEL); + msg = kzalloc(sizeof(*msg) + data_size, GFP_KERNEL); if (!msg) return -ENOMEM; msg->command = EC_CMD_GET_PANIC_INFO; - msg->insize = insize; + msg->insize = data_size; ret = cros_ec_cmd_xfer_status(ec_dev, msg); + if (ret < 0) + goto free; + + memcpy(data, msg->data, data_size); + +free: + kfree(msg); + return ret; +} + +static int cros_ec_create_panicinfo(struct cros_ec_debugfs *debug_info) +{ + struct cros_ec_device *ec_dev = debug_info->ec->ec_dev; + int ret; + void *data; + + data = devm_kzalloc(debug_info->ec->dev, ec_dev->max_response, + GFP_KERNEL); + if (!data) + return -ENOMEM; + + ret = cros_ec_get_panicinfo(ec_dev, data, ec_dev->max_response); if (ret < 0) { ret = 0; goto free; @@ -424,19 +444,35 @@ static int cros_ec_create_panicinfo(struct cros_ec_debugfs *debug_info) if (ret == 0) goto free; - debug_info->panicinfo_blob.data = msg->data; + debug_info->panicinfo_blob.data = data; debug_info->panicinfo_blob.size = ret; - debugfs_create_blob("panicinfo", S_IFREG | 0444, debug_info->dir, + debugfs_create_blob("panicinfo", 0444, debug_info->dir, &debug_info->panicinfo_blob); return 0; free: - devm_kfree(debug_info->ec->dev, msg); + devm_kfree(debug_info->ec->dev, data); return ret; } +static int cros_ec_debugfs_panic_event(struct notifier_block *nb, + unsigned long queued_during_suspend, void *_notify) +{ + struct cros_ec_debugfs *debug_info = + container_of(nb, struct cros_ec_debugfs, notifier_panic); + + if (debug_info->log_buffer.buf) { + /* Force log poll work to run immediately */ + mod_delayed_work(debug_info->log_poll_work.wq, &debug_info->log_poll_work, 0); + /* Block until log poll work finishes */ + flush_delayed_work(&debug_info->log_poll_work); + } + + return NOTIFY_DONE; +} + static int cros_ec_debugfs_probe(struct platform_device *pd) { struct cros_ec_dev *ec = dev_get_drvdata(pd->dev.parent); @@ -473,6 +509,12 @@ static int cros_ec_debugfs_probe(struct platform_device *pd) debugfs_create_u16("suspend_timeout_ms", 0664, debug_info->dir, &ec->ec_dev->suspend_timeout_ms); + debug_info->notifier_panic.notifier_call = cros_ec_debugfs_panic_event; + ret = blocking_notifier_chain_register(&ec->ec_dev->panic_notifier, + &debug_info->notifier_panic); + if (ret) + goto remove_debugfs; + ec->debug_info = debug_info; dev_set_drvdata(&pd->dev, ec); @@ -484,14 +526,12 @@ remove_debugfs: return ret; } -static int cros_ec_debugfs_remove(struct platform_device *pd) +static void cros_ec_debugfs_remove(struct platform_device *pd) { struct cros_ec_dev *ec = dev_get_drvdata(pd->dev.parent); debugfs_remove_recursive(ec->debug_info->dir); cros_ec_cleanup_console_log(ec->debug_info); - - return 0; } static int __maybe_unused cros_ec_debugfs_suspend(struct device *dev) @@ -517,6 +557,12 @@ static int __maybe_unused cros_ec_debugfs_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(cros_ec_debugfs_pm_ops, cros_ec_debugfs_suspend, cros_ec_debugfs_resume); +static const struct platform_device_id cros_ec_debugfs_id[] = { + { DRV_NAME, 0 }, + {} +}; +MODULE_DEVICE_TABLE(platform, cros_ec_debugfs_id); + static struct platform_driver cros_ec_debugfs_driver = { .driver = { .name = DRV_NAME, @@ -525,10 +571,10 @@ static struct platform_driver cros_ec_debugfs_driver = { }, .probe = cros_ec_debugfs_probe, .remove = cros_ec_debugfs_remove, + .id_table = cros_ec_debugfs_id, }; module_platform_driver(cros_ec_debugfs_driver); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("Debug logs for ChromeOS EC"); -MODULE_ALIAS("platform:" DRV_NAME); |
