diff --git a/drivers/soc/qcom/subsystem_sleep_stats.c b/drivers/soc/qcom/subsystem_sleep_stats.c index e613317bd8f1..d2ec1cd69430 100644 --- a/drivers/soc/qcom/subsystem_sleep_stats.c +++ b/drivers/soc/qcom/subsystem_sleep_stats.c @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -17,29 +18,50 @@ #define STATS_MAX_MINOR 1 #define STATS_DEVICE_NAME "stats" #define SUBSYSTEM_STATS_MAGIC_NUM (0x9d) +#define SUBSYSTEM_STATS_OTHERS_NUM (-2) + +#define DDR_STATS_MAGIC_KEY 0xA1157A75 +#define DDR_STATS_MAX_NUM_MODES 0x14 +#define DDR_STATS_MAGIC_KEY_ADDR 0x0 +#define DDR_STATS_NUM_MODES_ADDR 0x4 +#define DDR_STATS_NAME_ADDR 0x0 +#define DDR_STATS_COUNT_ADDR 0x4 +#define DDR_STATS_DURATION_ADDR 0x8 #define APSS_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 0, \ - struct subsystem_stats *) + struct sleep_stats *) #define MODEM_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 1, \ - struct subsystem_stats *) + struct sleep_stats *) #define WPSS_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 2, \ - struct subsystem_stats *) + struct sleep_stats *) #define ADSP_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 3, \ - struct subsystem_stats *) + struct sleep_stats *) #define ADSP_ISLAND_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 4, \ - struct subsystem_stats *) + struct sleep_stats *) #define CDSP_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 5, \ - struct subsystem_stats *) + struct sleep_stats *) #define SLPI_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 6, \ - struct subsystem_stats *) + struct sleep_stats *) #define GPU_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 7, \ - struct subsystem_stats *) + struct sleep_stats *) #define DISPLAY_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 8, \ - struct subsystem_stats *) + struct sleep_stats *) #define SLPI_ISLAND_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 9, \ - struct subsystem_stats *) + struct sleep_stats *) -struct subsystem_stats { +#define AOSD_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 10, \ + struct sleep_stats *) + +#define CXSD_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 11, \ + struct sleep_stats *) + +#define DDR_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 12, \ + struct sleep_stats *) + +#define DDR_STATS_IOCTL _IOR(SUBSYSTEM_STATS_MAGIC_NUM, 13, \ + struct sleep_stats *) + +struct sleep_stats { u32 version; u32 count; u64 last_entered_at; @@ -48,6 +70,10 @@ struct subsystem_stats { }; enum subsystem_smem_id { + AOSD = 0, + CXSD = 1, + DDR = 2, + DDR_STATS = 3, MPSS = 605, ADSP, CDSP, @@ -67,6 +93,13 @@ enum subsystem_pid { PID_WPSS = 13, PID_GPU = PID_APSS, PID_DISPLAY = PID_APSS, + PID_OTHERS = -2, +}; + +struct stats_config { + unsigned int offset_addr; + unsigned int ddr_offset_addr; + unsigned int num_records; }; struct sleep_stats_data { @@ -74,20 +107,80 @@ struct sleep_stats_data { struct class *stats_class; struct device *stats_device; struct cdev stats_cdev; + const struct stats_config **config; + void __iomem *reg_base; + void __iomem *ddr_reg; + void __iomem **reg; + u32 ddr_key; + u32 ddr_entry_count; }; static DEFINE_MUTEX(sleep_stats_mutex); +static int stats_data_open(struct inode *inode, struct file *file) +{ + struct sleep_stats_data *drvdata = NULL; + + if (!inode || !inode->i_cdev || !file) + return -EINVAL; + + drvdata = container_of(inode->i_cdev, struct sleep_stats_data, stats_cdev); + file->private_data = drvdata; + + return 0; +} + +static void ddr_stats_sleep_stat(struct sleep_stats_data *stats_data, struct sleep_stats *ddr_stats) +{ + void __iomem *reg; + int i; + + reg = stats_data->ddr_reg + DDR_STATS_NUM_MODES_ADDR + 0x4; + for (i = 0; i < stats_data->ddr_entry_count; i++) { + (ddr_stats + i)->version = readl_relaxed(reg + DDR_STATS_NAME_ADDR); + (ddr_stats + i)->count = readl_relaxed(reg + DDR_STATS_COUNT_ADDR); + (ddr_stats + i)->last_entered_at = 0xDEADDEAD; + (ddr_stats + i)->last_exited_at = 0xDEADDEAD; + (ddr_stats + i)->accumulated = readq_relaxed(reg + DDR_STATS_DURATION_ADDR); + reg += sizeof(struct sleep_stats) - 2 * sizeof(u64); + } +} + +static int subsystem_sleep_stats(struct sleep_stats_data *stats_data, struct sleep_stats *stats, + unsigned int pid, unsigned int idx) +{ + struct sleep_stats *subsystem_stats_data; + + if (pid == SUBSYSTEM_STATS_OTHERS_NUM) + memcpy_fromio(stats, stats_data->reg[idx], sizeof(*stats)); + else { + subsystem_stats_data = qcom_smem_get(pid, idx, NULL); + if (IS_ERR(subsystem_stats_data)) + return -ENODEV; + + stats->version = subsystem_stats_data->version; + stats->count = subsystem_stats_data->count; + stats->last_entered_at = subsystem_stats_data->last_entered_at; + stats->last_exited_at = subsystem_stats_data->last_exited_at; + stats->accumulated = subsystem_stats_data->accumulated; + } + + return 0; +} + static long stats_data_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { - struct subsystem_stats *temp, *subsystem_stats_data; + struct sleep_stats_data *drvdata = file->private_data; + struct sleep_stats *temp; int ret = -ENOMEM; unsigned int pid, smem_item; mutex_lock(&sleep_stats_mutex); - - temp = kzalloc(sizeof(struct subsystem_stats), GFP_KERNEL); + if (cmd != DDR_STATS_IOCTL) + temp = kzalloc(sizeof(struct sleep_stats), GFP_KERNEL); + else + temp = kcalloc(DDR_STATS_MAX_NUM_MODES, sizeof(struct sleep_stats), GFP_KERNEL); if (!temp) goto out_unlock; @@ -132,38 +225,52 @@ static long stats_data_ioctl(struct file *file, unsigned int cmd, pid = PID_SLPI; smem_item = SLPI_ISLAND; break; + case AOSD_IOCTL: + pid = PID_OTHERS; + smem_item = AOSD; + break; + case CXSD_IOCTL: + pid = PID_OTHERS; + smem_item = CXSD; + break; + case DDR_IOCTL: + pid = PID_OTHERS; + smem_item = DDR; + break; + case DDR_STATS_IOCTL: + pid = PID_OTHERS; + smem_item = DDR_STATS; + 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; + if (cmd != DDR_STATS_IOCTL) { + ret = subsystem_sleep_stats(drvdata, temp, pid, smem_item); + if (ret < 0) + goto out_free; + + /* + * 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 sleep_stats)); + } else { + ddr_stats_sleep_stat(drvdata, temp); + ret = copy_to_user((void __user *)arg, temp, + DDR_STATS_MAX_NUM_MODES * sizeof(struct sleep_stats)); } - 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); @@ -178,14 +285,21 @@ out_unlock: static const struct file_operations stats_data_fops = { .owner = THIS_MODULE, - .open = simple_open, + .open = stats_data_open, .unlocked_ioctl = stats_data_ioctl, }; static int subsystem_stats_probe(struct platform_device *pdev) { struct sleep_stats_data *stats_data; + const struct stats_config *config; + struct resource *res; + void __iomem *offset_addr; + phys_addr_t stats_base; + resource_size_t stats_size; int ret = -ENOMEM; + int i; + u32 offset; stats_data = devm_kzalloc(&pdev->dev, sizeof(struct sleep_stats_data), GFP_KERNEL); if (!stats_data) @@ -215,6 +329,70 @@ static int subsystem_stats_probe(struct platform_device *pdev) goto fail_device_create; } + config = device_get_match_data(&pdev->dev); + if (!config) { + ret = -ENODEV; + goto fail_device_create; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + ret = PTR_ERR(res); + goto fail_device_create; + } + + offset_addr = devm_ioremap(&pdev->dev, res->start + config->offset_addr, sizeof(u32)); + if (IS_ERR(offset_addr)) { + ret = PTR_ERR(offset_addr); + goto fail_device_create; + } + + stats_base = res->start | readl_relaxed(offset_addr); + stats_size = resource_size(res); + + stats_data->reg_base = devm_ioremap(&pdev->dev, stats_base, stats_size); + if (!stats_data->reg_base) { + ret = -ENOMEM; + goto fail_device_create; + } + + stats_data->config = devm_kcalloc(&pdev->dev, config->num_records, + sizeof(struct stats_config *), GFP_KERNEL); + + stats_data->reg = devm_kcalloc(&pdev->dev, config->num_records, sizeof(void __iomem *), + GFP_KERNEL); + + for (i = 0; i < config->num_records; i++) { + stats_data->config[i] = config; + offset = (i * sizeof(struct sleep_stats)); + stats_data->reg[i] = stats_data->reg_base + offset; + } + + offset_addr = devm_ioremap(&pdev->dev, res->start + config->ddr_offset_addr, sizeof(u32)); + if (IS_ERR(offset_addr)) { + ret = PTR_ERR(offset_addr); + goto fail_device_create; + } + + stats_base = res->start | readl_relaxed(offset_addr); + stats_data->ddr_reg = devm_ioremap(&pdev->dev, stats_base, stats_size); + if (!stats_data->ddr_reg) { + ret = -ENOMEM; + goto fail_device_create; + } + + stats_data->ddr_key = readl_relaxed(stats_data->ddr_reg + DDR_STATS_MAGIC_KEY_ADDR); + if (stats_data->ddr_key != DDR_STATS_MAGIC_KEY) { + ret = -EINVAL; + goto fail_device_create; + } + + stats_data->ddr_entry_count = readl_relaxed(stats_data->ddr_reg + DDR_STATS_NUM_MODES_ADDR); + if (stats_data->ddr_entry_count > DDR_STATS_MAX_NUM_MODES) { + ret = -EINVAL; + goto fail_device_create; + } + platform_set_drvdata(pdev, stats_data); return 0; @@ -244,8 +422,14 @@ static int subsystem_stats_remove(struct platform_device *pdev) return 0; } +static const struct stats_config rpmh_data = { + .offset_addr = 0x4, + .ddr_offset_addr = 0x1c, + .num_records = 3, +}; + static const struct of_device_id subsystem_stats_table[] = { - {.compatible = "qcom,subsystem-sleep-stats"}, + { .compatible = "qcom,subsystem-sleep-stats", .data = &rpmh_data}, {}, };