Loading arch/arm64/configs/vendor/holi_GKI.config +1 −0 Original line number Diff line number Diff line Loading @@ -228,3 +228,4 @@ CONFIG_SPS_SUPPORT_NDP_BAM=y CONFIG_QCOM_MEMORY_DUMP_V2=m # CONFIG_SERIAL_MSM_GENI_CONSOLE is not set CONFIG_MSM_PERFORMANCE=m CONFIG_QCOM_SUBSYSTEM_SLEEP_STATS=m drivers/soc/qcom/Kconfig +8 −0 Original line number Diff line number Diff line Loading @@ -938,6 +938,14 @@ config QTI_RPM_STATS_LOG the low power modes that RPM enters. The drivers outputs the message via a sysfs node. config QCOM_SUBSYSTEM_SLEEP_STATS tristate "Qualcomm Technologies, Inc. Subsystem sleep stats driver" depends on QCOM_SMEM help This driver is IOCTL implementation to get the subsystem stats data from SMEM. Stats information such as sleep count, last entered at, last exited at and accumulated duration can be read from userspace with ioctl. config QTI_DDR_STATS_LOG tristate "Qualcomm Technologies Inc (QTI) DDR Stats Driver" Loading drivers/soc/qcom/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o obj-$(CONFIG_QCOM_SMEM) += smem.o obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o obj-$(CONFIG_QCOM_SMP2P) += smp2p.o obj-$(CONFIG_QCOM_SUBSYSTEM_SLEEP_STATS) += subsystem_sleep_stats.o obj-$(CONFIG_QSEE_IPC_IRQ_BRIDGE) += qsee_ipc_irq_bridge.o obj-$(CONFIG_QCOM_SMSM) += smsm.o obj-$(CONFIG_MSM_CORE_HANG_DETECT) += core_hang_detect.o Loading drivers/soc/qcom/subsystem_sleep_stats.c 0 → 100644 +263 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2021, The Linux Foundation. All rights reserved. */ #include <linux/cdev.h> #include <linux/fs.h> #include <linux/ioctl.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/soc/qcom/smem.h> #include <linux/uaccess.h> #define STATS_BASEMINOR 0 #define STATS_MAX_MINOR 1 #define STATS_DEVICE_NAME "stats" #define SUBSYSTEM_STATS_MAGIC_NUM (0x9d) #define APSS_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 0, \ struct subsystem_stats *) #define MODEM_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 1, \ struct subsystem_stats *) #define WPSS_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 2, \ struct subsystem_stats *) #define ADSP_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 3, \ struct subsystem_stats *) #define ADSP_ISLAND_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 4, \ struct subsystem_stats *) #define CDSP_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 5, \ struct subsystem_stats *) #define SLPI_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 6, \ struct subsystem_stats *) #define GPU_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 7, \ struct subsystem_stats *) #define DISPLAY_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 8, \ struct subsystem_stats *) #define SLPI_ISLAND_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 9, \ struct subsystem_stats *) struct subsystem_stats { u32 version; u32 count; u64 last_entered_at; u64 last_exited_at; u64 accumulated; }; enum subsystem_smem_id { MPSS = 605, ADSP, CDSP, SLPI, GPU, DISPLAY, SLPI_ISLAND = 613, APSS = 631, }; enum subsystem_pid { PID_APSS = 0, PID_MPSS = 1, PID_ADSP = 2, PID_SLPI = 3, PID_CDSP = 5, PID_WPSS = 13, PID_GPU = PID_APSS, PID_DISPLAY = PID_APSS, }; struct sleep_stats_data { dev_t dev_no; struct class *stats_class; struct device *stats_device; struct cdev stats_cdev; }; static DEFINE_MUTEX(sleep_stats_mutex); static long stats_data_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct subsystem_stats *temp, *subsystem_stats_data; int ret = -ENOMEM; unsigned int pid, smem_item; mutex_lock(&sleep_stats_mutex); temp = kzalloc(sizeof(struct subsystem_stats), GFP_KERNEL); if (!temp) goto out_unlock; switch (cmd) { case APSS_IOCTL: pid = QCOM_SMEM_HOST_ANY; smem_item = APSS; break; case MODEM_IOCTL: pid = PID_MPSS; smem_item = MPSS; break; case WPSS_IOCTL: pid = PID_MPSS; smem_item = MPSS; break; case ADSP_IOCTL: pid = PID_ADSP; smem_item = ADSP; break; case ADSP_ISLAND_IOCTL: pid = PID_ADSP; smem_item = SLPI_ISLAND; break; case CDSP_IOCTL: pid = PID_CDSP; smem_item = CDSP; break; case SLPI_IOCTL: pid = PID_SLPI; smem_item = SLPI; break; case GPU_IOCTL: pid = PID_GPU; smem_item = GPU; break; case DISPLAY_IOCTL: pid = PID_DISPLAY; smem_item = DISPLAY; break; case SLPI_ISLAND_IOCTL: pid = PID_SLPI; smem_item = SLPI_ISLAND; break; default: pr_err("Incorrect command error\n"); ret = -EINVAL; goto out_free; } subsystem_stats_data = qcom_smem_get(pid, smem_item, NULL); if (IS_ERR(subsystem_stats_data)) { ret = -ENODEV; goto out_free; } temp->version = subsystem_stats_data->version; temp->count = subsystem_stats_data->count; temp->last_entered_at = subsystem_stats_data->last_entered_at; temp->last_exited_at = subsystem_stats_data->last_exited_at; temp->accumulated = subsystem_stats_data->accumulated; /* * If a subsystem is in sleep when reading the sleep stats from SMEM * adjust the accumulated sleep duration to show actual sleep time. * This ensures that the displayed stats are real when used for * the purpose of computing battery utilization. */ if (temp->last_entered_at > temp->last_exited_at) { temp->accumulated += (__arch_counter_get_cntvct() - temp->last_entered_at); } ret = copy_to_user((void __user *)arg, temp, sizeof(struct subsystem_stats)); kfree(temp); mutex_unlock(&sleep_stats_mutex); return ret; out_free: kfree(temp); out_unlock: mutex_unlock(&sleep_stats_mutex); return ret; } static const struct file_operations stats_data_fops = { .owner = THIS_MODULE, .open = simple_open, .unlocked_ioctl = stats_data_ioctl, }; static int subsystem_stats_probe(struct platform_device *pdev) { struct sleep_stats_data *stats_data; int ret = -ENOMEM; stats_data = devm_kzalloc(&pdev->dev, sizeof(struct sleep_stats_data), GFP_KERNEL); if (!stats_data) return ret; ret = alloc_chrdev_region(&stats_data->dev_no, STATS_BASEMINOR, STATS_MAX_MINOR, STATS_DEVICE_NAME); if (ret) goto fail_alloc_chrdev; cdev_init(&stats_data->stats_cdev, &stats_data_fops); ret = cdev_add(&stats_data->stats_cdev, stats_data->dev_no, 1); if (ret) goto fail_cdev_add; stats_data->stats_class = class_create(THIS_MODULE, STATS_DEVICE_NAME); if (IS_ERR_OR_NULL(stats_data->stats_class)) { ret = -EINVAL; goto fail_class_create; } stats_data->stats_device = device_create(stats_data->stats_class, NULL, stats_data->dev_no, NULL, STATS_DEVICE_NAME); if (IS_ERR_OR_NULL(stats_data->stats_device)) { ret = -EINVAL; goto fail_device_create; } platform_set_drvdata(pdev, stats_data); return 0; fail_device_create: class_destroy(stats_data->stats_class); fail_class_create: cdev_del(&stats_data->stats_cdev); fail_cdev_add: unregister_chrdev_region(stats_data->dev_no, 1); fail_alloc_chrdev: return ret; } static int subsystem_stats_remove(struct platform_device *pdev) { struct sleep_stats_data *stats_data; stats_data = platform_get_drvdata(pdev); if (!stats_data) return 0; class_destroy(stats_data->stats_class); cdev_del(&stats_data->stats_cdev); unregister_chrdev_region(stats_data->dev_no, 1); return 0; } static const struct of_device_id subsystem_stats_table[] = { {.compatible = "qcom,subsystem-sleep-stats"}, {}, }; static struct platform_driver subsystem_sleep_stats_driver = { .probe = subsystem_stats_probe, .remove = subsystem_stats_remove, .driver = { .name = "subsystem_sleep_stats", .of_match_table = subsystem_stats_table, }, }; module_platform_driver(subsystem_sleep_stats_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Qualcomm Technologies, Inc. subsystem sleep stats driver"); Loading
arch/arm64/configs/vendor/holi_GKI.config +1 −0 Original line number Diff line number Diff line Loading @@ -228,3 +228,4 @@ CONFIG_SPS_SUPPORT_NDP_BAM=y CONFIG_QCOM_MEMORY_DUMP_V2=m # CONFIG_SERIAL_MSM_GENI_CONSOLE is not set CONFIG_MSM_PERFORMANCE=m CONFIG_QCOM_SUBSYSTEM_SLEEP_STATS=m
drivers/soc/qcom/Kconfig +8 −0 Original line number Diff line number Diff line Loading @@ -938,6 +938,14 @@ config QTI_RPM_STATS_LOG the low power modes that RPM enters. The drivers outputs the message via a sysfs node. config QCOM_SUBSYSTEM_SLEEP_STATS tristate "Qualcomm Technologies, Inc. Subsystem sleep stats driver" depends on QCOM_SMEM help This driver is IOCTL implementation to get the subsystem stats data from SMEM. Stats information such as sleep count, last entered at, last exited at and accumulated duration can be read from userspace with ioctl. config QTI_DDR_STATS_LOG tristate "Qualcomm Technologies Inc (QTI) DDR Stats Driver" Loading
drivers/soc/qcom/Makefile +1 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ obj-$(CONFIG_QCOM_SMD_RPM) += smd-rpm.o obj-$(CONFIG_QCOM_SMEM) += smem.o obj-$(CONFIG_QCOM_SMEM_STATE) += smem_state.o obj-$(CONFIG_QCOM_SMP2P) += smp2p.o obj-$(CONFIG_QCOM_SUBSYSTEM_SLEEP_STATS) += subsystem_sleep_stats.o obj-$(CONFIG_QSEE_IPC_IRQ_BRIDGE) += qsee_ipc_irq_bridge.o obj-$(CONFIG_QCOM_SMSM) += smsm.o obj-$(CONFIG_MSM_CORE_HANG_DETECT) += core_hang_detect.o Loading
drivers/soc/qcom/subsystem_sleep_stats.c 0 → 100644 +263 −0 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2021, The Linux Foundation. All rights reserved. */ #include <linux/cdev.h> #include <linux/fs.h> #include <linux/ioctl.h> #include <linux/module.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/slab.h> #include <linux/soc/qcom/smem.h> #include <linux/uaccess.h> #define STATS_BASEMINOR 0 #define STATS_MAX_MINOR 1 #define STATS_DEVICE_NAME "stats" #define SUBSYSTEM_STATS_MAGIC_NUM (0x9d) #define APSS_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 0, \ struct subsystem_stats *) #define MODEM_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 1, \ struct subsystem_stats *) #define WPSS_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 2, \ struct subsystem_stats *) #define ADSP_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 3, \ struct subsystem_stats *) #define ADSP_ISLAND_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 4, \ struct subsystem_stats *) #define CDSP_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 5, \ struct subsystem_stats *) #define SLPI_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 6, \ struct subsystem_stats *) #define GPU_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 7, \ struct subsystem_stats *) #define DISPLAY_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 8, \ struct subsystem_stats *) #define SLPI_ISLAND_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 9, \ struct subsystem_stats *) struct subsystem_stats { u32 version; u32 count; u64 last_entered_at; u64 last_exited_at; u64 accumulated; }; enum subsystem_smem_id { MPSS = 605, ADSP, CDSP, SLPI, GPU, DISPLAY, SLPI_ISLAND = 613, APSS = 631, }; enum subsystem_pid { PID_APSS = 0, PID_MPSS = 1, PID_ADSP = 2, PID_SLPI = 3, PID_CDSP = 5, PID_WPSS = 13, PID_GPU = PID_APSS, PID_DISPLAY = PID_APSS, }; struct sleep_stats_data { dev_t dev_no; struct class *stats_class; struct device *stats_device; struct cdev stats_cdev; }; static DEFINE_MUTEX(sleep_stats_mutex); static long stats_data_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct subsystem_stats *temp, *subsystem_stats_data; int ret = -ENOMEM; unsigned int pid, smem_item; mutex_lock(&sleep_stats_mutex); temp = kzalloc(sizeof(struct subsystem_stats), GFP_KERNEL); if (!temp) goto out_unlock; switch (cmd) { case APSS_IOCTL: pid = QCOM_SMEM_HOST_ANY; smem_item = APSS; break; case MODEM_IOCTL: pid = PID_MPSS; smem_item = MPSS; break; case WPSS_IOCTL: pid = PID_MPSS; smem_item = MPSS; break; case ADSP_IOCTL: pid = PID_ADSP; smem_item = ADSP; break; case ADSP_ISLAND_IOCTL: pid = PID_ADSP; smem_item = SLPI_ISLAND; break; case CDSP_IOCTL: pid = PID_CDSP; smem_item = CDSP; break; case SLPI_IOCTL: pid = PID_SLPI; smem_item = SLPI; break; case GPU_IOCTL: pid = PID_GPU; smem_item = GPU; break; case DISPLAY_IOCTL: pid = PID_DISPLAY; smem_item = DISPLAY; break; case SLPI_ISLAND_IOCTL: pid = PID_SLPI; smem_item = SLPI_ISLAND; break; default: pr_err("Incorrect command error\n"); ret = -EINVAL; goto out_free; } subsystem_stats_data = qcom_smem_get(pid, smem_item, NULL); if (IS_ERR(subsystem_stats_data)) { ret = -ENODEV; goto out_free; } temp->version = subsystem_stats_data->version; temp->count = subsystem_stats_data->count; temp->last_entered_at = subsystem_stats_data->last_entered_at; temp->last_exited_at = subsystem_stats_data->last_exited_at; temp->accumulated = subsystem_stats_data->accumulated; /* * If a subsystem is in sleep when reading the sleep stats from SMEM * adjust the accumulated sleep duration to show actual sleep time. * This ensures that the displayed stats are real when used for * the purpose of computing battery utilization. */ if (temp->last_entered_at > temp->last_exited_at) { temp->accumulated += (__arch_counter_get_cntvct() - temp->last_entered_at); } ret = copy_to_user((void __user *)arg, temp, sizeof(struct subsystem_stats)); kfree(temp); mutex_unlock(&sleep_stats_mutex); return ret; out_free: kfree(temp); out_unlock: mutex_unlock(&sleep_stats_mutex); return ret; } static const struct file_operations stats_data_fops = { .owner = THIS_MODULE, .open = simple_open, .unlocked_ioctl = stats_data_ioctl, }; static int subsystem_stats_probe(struct platform_device *pdev) { struct sleep_stats_data *stats_data; int ret = -ENOMEM; stats_data = devm_kzalloc(&pdev->dev, sizeof(struct sleep_stats_data), GFP_KERNEL); if (!stats_data) return ret; ret = alloc_chrdev_region(&stats_data->dev_no, STATS_BASEMINOR, STATS_MAX_MINOR, STATS_DEVICE_NAME); if (ret) goto fail_alloc_chrdev; cdev_init(&stats_data->stats_cdev, &stats_data_fops); ret = cdev_add(&stats_data->stats_cdev, stats_data->dev_no, 1); if (ret) goto fail_cdev_add; stats_data->stats_class = class_create(THIS_MODULE, STATS_DEVICE_NAME); if (IS_ERR_OR_NULL(stats_data->stats_class)) { ret = -EINVAL; goto fail_class_create; } stats_data->stats_device = device_create(stats_data->stats_class, NULL, stats_data->dev_no, NULL, STATS_DEVICE_NAME); if (IS_ERR_OR_NULL(stats_data->stats_device)) { ret = -EINVAL; goto fail_device_create; } platform_set_drvdata(pdev, stats_data); return 0; fail_device_create: class_destroy(stats_data->stats_class); fail_class_create: cdev_del(&stats_data->stats_cdev); fail_cdev_add: unregister_chrdev_region(stats_data->dev_no, 1); fail_alloc_chrdev: return ret; } static int subsystem_stats_remove(struct platform_device *pdev) { struct sleep_stats_data *stats_data; stats_data = platform_get_drvdata(pdev); if (!stats_data) return 0; class_destroy(stats_data->stats_class); cdev_del(&stats_data->stats_cdev); unregister_chrdev_region(stats_data->dev_no, 1); return 0; } static const struct of_device_id subsystem_stats_table[] = { {.compatible = "qcom,subsystem-sleep-stats"}, {}, }; static struct platform_driver subsystem_sleep_stats_driver = { .probe = subsystem_stats_probe, .remove = subsystem_stats_remove, .driver = { .name = "subsystem_sleep_stats", .of_match_table = subsystem_stats_table, }, }; module_platform_driver(subsystem_sleep_stats_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("Qualcomm Technologies, Inc. subsystem sleep stats driver");