diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c index 9d7a74767008..085dc887631c 100644 --- a/drivers/soc/qcom/qcom_aoss.c +++ b/drivers/soc/qcom/qcom_aoss.c @@ -3,6 +3,7 @@ * Copyright (c) 2019, Linaro Ltd */ #include +#include #include #include #include @@ -44,6 +45,8 @@ #define QMP_NUM_COOLING_RESOURCES 2 +#define QMP_DEBUGFS_FILES 4 + static bool qmp_cdev_max_state = 1; struct qmp_cooling_device { @@ -82,6 +85,8 @@ struct qmp { struct clk_hw qdss_clk; struct qmp_cooling_device *cooling_devs; + struct dentry *debugfs_root; + struct dentry *debugfs_files[QMP_DEBUGFS_FILES]; }; static void qmp_kick(struct qmp *qmp) @@ -475,6 +480,91 @@ void qmp_put(struct qmp *qmp) } EXPORT_SYMBOL_GPL(qmp_put); +struct qmp_debugfs_entry { + const char *name; + const char *fmt; + bool is_bool; + const char *true_val; + const char *false_val; +}; + +static const struct qmp_debugfs_entry qmp_debugfs_entries[QMP_DEBUGFS_FILES] = { + { "ddr_frequency_mhz", "{class: ddr, res: fixed, val: %u}", false }, + { "prevent_aoss_sleep", "{class: aoss_slp, res: sleep: %s}", true, "enable", "disable" }, + { "prevent_cx_collapse", "{class: cx_mol, res: cx, val: %s}", true, "mol", "off" }, + { "prevent_ddr_collapse", "{class: ddr_mol, res: ddr, val: %s}", true, "mol", "off" }, +}; + +static ssize_t qmp_debugfs_write(struct file *file, const char __user *user_buf, + size_t count, loff_t *pos) +{ + const struct qmp_debugfs_entry *entry = NULL; + struct qmp *qmp = file->private_data; + char buf[QMP_MSG_LEN]; + unsigned int uint_val; + const char *str_val; + bool bool_val; + int ret; + int i; + + for (i = 0; i < ARRAY_SIZE(qmp->debugfs_files); i++) { + if (qmp->debugfs_files[i] == file->f_path.dentry) { + entry = &qmp_debugfs_entries[i]; + break; + } + } + if (WARN_ON(!entry)) + return -EFAULT; + + if (entry->is_bool) { + ret = kstrtobool_from_user(user_buf, count, &bool_val); + if (ret) + return ret; + + str_val = bool_val ? entry->true_val : entry->false_val; + + ret = snprintf(buf, sizeof(buf), entry->fmt, str_val); + if (ret >= sizeof(buf)) + return -EINVAL; + } else { + ret = kstrtou32_from_user(user_buf, count, 0, &uint_val); + if (ret) + return ret; + + ret = snprintf(buf, sizeof(buf), entry->fmt, uint_val); + if (ret >= sizeof(buf)) + return -EINVAL; + } + + ret = qmp_send(qmp, buf); + if (ret < 0) + return ret; + + return count; +} + +static const struct file_operations qmp_debugfs_fops = { + .open = simple_open, + .write = qmp_debugfs_write, +}; + +static void qmp_debugfs_create(struct qmp *qmp) +{ + const struct qmp_debugfs_entry *entry; + int i; + + qmp->debugfs_root = debugfs_create_dir("qcom_aoss", NULL); + + for (i = 0; i < ARRAY_SIZE(qmp->debugfs_files); i++) { + entry = &qmp_debugfs_entries[i]; + + qmp->debugfs_files[i] = debugfs_create_file(entry->name, 0200, + qmp->debugfs_root, + qmp, + &qmp_debugfs_fops); + } +} + static int qmp_probe(struct platform_device *pdev) { struct qmp *qmp; @@ -523,6 +613,8 @@ static int qmp_probe(struct platform_device *pdev) platform_set_drvdata(pdev, qmp); + qmp_debugfs_create(qmp); + return 0; err_close_qmp: @@ -537,6 +629,8 @@ static void qmp_remove(struct platform_device *pdev) { struct qmp *qmp = platform_get_drvdata(pdev); + debugfs_remove_recursive(qmp->debugfs_root); + qmp_qdss_clk_remove(qmp); qmp_cooling_devices_remove(qmp);