Loading drivers/soc/qcom/memory_dump_v2.c +173 −71 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014-2017, 2019, The Linux Foundation. All rights reserved. * Copyright (c) 2014-2017, 2019-2020, The Linux Foundation. All rights reserved. */ #include <asm/cacheflush.h> Loading Loading @@ -28,6 +28,7 @@ #define INPUT_DATA_BY_HLOS 0x00C0FFEE #define FORMAT_VERSION_1 0x1 #define FORMAT_VERSION_2 0x2 #define CORE_REG_NUM_DEFAULT 0x1 #define MAGIC_INDEX 0 Loading @@ -37,6 +38,20 @@ #define PERCORE_INDEX 4 #define SYSTEM_REGS_INPUT_INDEX 5 #define CMD_REPEAT_READ (0x2 << 24) #define CMD_DELAY (0x1 << 24) #define CMD_READ 0x0 #define CMD_READ_WORD 0x1 #define CMD_WRITE 0x2 #define CMD_EXTRA 0x3 #define CMD_MASK 0x3 #define OFFSET_MASK GENMASK(31, 2) #define EXTRA_CMD_MASK GENMASK(31, 24) #define EXTRA_VALUE_MASK GENMASK(23, 0) #define MAX_EXTRA_VALUE 0xffffff struct cpuss_dump_data { void *dump_vaddr; u32 size; Loading Loading @@ -119,7 +134,7 @@ static int update_reg_dump_table(struct device *dev, u32 core_reg_num) memset(cpudata->dump_vaddr, 0xDE, cpudata->size); p = (struct reg_dump_data *)cpudata->dump_vaddr; p->magic = INPUT_DATA_BY_HLOS; p->version = FORMAT_VERSION_1; p->version = FORMAT_VERSION_2; p->system_regs_input_index = system_regs_input_index; p->regdump_output_byte_offset = regdump_output_byte_offset; memset((uint32_t *)cpudata->dump_vaddr + PERCORE_INDEX, 0x0, Loading Loading @@ -197,9 +212,8 @@ static ssize_t register_config_show(struct device *dev, char local_buf[64]; int len = 0, count = 0; int index, system_index_start, index_end; uint32_t register_offset, length_in_bytes; uint32_t length_in_words; uint32_t *p; uint32_t register_offset, val; uint32_t *p, cmd; struct cpuss_dump_data *cpudata = dev_get_drvdata(dev); buf[0] = '\0'; Loading @@ -212,7 +226,7 @@ static ssize_t register_config_show(struct device *dev, p = (uint32_t *)cpudata->dump_vaddr; /* print per-core & system registers */ len = snprintf(local_buf, 64, "per-core registers:\n"); len = scnprintf(local_buf, 64, "per-core registers:\n"); strlcat(buf, local_buf, PAGE_SIZE); count += len; Loading @@ -221,7 +235,7 @@ static ssize_t register_config_show(struct device *dev, cpudata->sys_reg_size / sizeof(uint32_t) + 1; for (index = PERCORE_INDEX; index < index_end;) { if (index == system_index_start) { len = snprintf(local_buf, 64, "system registers:\n"); len = scnprintf(local_buf, 64, "system registers:\n"); if ((count + len) > PAGE_SIZE) { dev_err(dev, "Couldn't write complete config\n"); break; Loading @@ -237,19 +251,44 @@ static ssize_t register_config_show(struct device *dev, continue; } if (register_offset & 0x3) { length_in_words = register_offset & 0x3; length_in_bytes = length_in_words << 2; len = snprintf(local_buf, 64, "Index: 0x%x, addr: 0x%x\n", index, register_offset); cmd = register_offset & CMD_MASK; register_offset &= OFFSET_MASK; switch (cmd) { case CMD_READ: val = *(p + index + 1); len = scnprintf(local_buf, 64, "0x%x, 0x%x, r\n", register_offset, val); index += 2; break; case CMD_READ_WORD: len = scnprintf(local_buf, 64, "0x%x, 0x%x, r\n", register_offset, 0x4); index++; } else { length_in_bytes = *(p + index + 1); len = snprintf(local_buf, 64, "Index: 0x%x, addr: 0x%x, length: 0x%x\n", index, register_offset, length_in_bytes); break; case CMD_WRITE: val = *(p + index + 1); len = scnprintf(local_buf, 64, "0x%x, 0x%x, w\n", register_offset, val); index += 2; break; case CMD_EXTRA: val = *(p + index + 1); cmd = val & EXTRA_CMD_MASK; val &= EXTRA_VALUE_MASK; if (cmd == CMD_DELAY) len = scnprintf(local_buf, 64, "0x%x, 0x%x, d\n", register_offset, val); else len = scnprintf(local_buf, 64, "0x%x, 0x%x, R\n", register_offset, val); index += 2; break; } if ((count + len) > PAGE_SIZE) { Loading @@ -265,6 +304,49 @@ static ssize_t register_config_show(struct device *dev, return count; } static int config_cpuss_register(struct device *dev, uint32_t *p, uint32_t index, char cmd, uint32_t register_offset, uint32_t val) { int ret = 0; switch (cmd) { case 'r': if (val > 4) { *(p + index) = register_offset; *(p + index + 1) = val; } else { *(p + index) = register_offset | CMD_READ_WORD; } break; case 'R': if (val > MAX_EXTRA_VALUE) { dev_err(dev, "repeat read time exceeded the limit\n"); ret = -EINVAL; return ret; } *(p + index) = register_offset | CMD_EXTRA; *(p + index + 1) = val | CMD_REPEAT_READ; break; case 'd': if (val > MAX_EXTRA_VALUE) { dev_err(dev, "sleep time exceeded the limit\n"); ret = -EINVAL; return ret; } *(p + index) = CMD_EXTRA; *(p + index + 1) = val | CMD_DELAY; break; case 'w': *(p + index) = register_offset | CMD_WRITE; *(p + index + 1) = val; break; default: dev_err(dev, "Don't support this command\n"); ret = -EINVAL; } return ret; } /** * This function sets configs of per-core or system registers. */ Loading @@ -273,9 +355,9 @@ static ssize_t register_config_store(struct device *dev, const char *buf, size_t size) { int ret; uint32_t register_offset, length_in_bytes, per_core = 0; uint32_t length_in_words; uint32_t register_offset, val, reserve_size = 4, per_core = 0; int nval; char cmd; uint32_t num_cores; u32 extra_memory; u32 used_memory; Loading @@ -283,29 +365,37 @@ static ssize_t register_config_store(struct device *dev, uint32_t *p; struct cpuss_dump_data *cpudata = dev_get_drvdata(dev); nval = sscanf(buf, "%x %x %u", ®ister_offset, &length_in_bytes, &per_core); if (nval != 2 && nval != 3) nval = sscanf(buf, "%x %x %c %u", ®ister_offset, &val, &cmd, &per_core); if (nval < 2) return -EINVAL; if (nval == 2) cmd = 'r'; if (per_core > 1) return -EINVAL; if (register_offset & 0x3) { dev_err(dev, "Invalid address, must be 4 byte aligned\n"); return -EINVAL; } if (length_in_bytes & 0x3) { dev_err(dev, "Invalid length, must be 4 byte aligned\n"); if (cmd == 'r' || cmd == 'R') { if (val == 0) { dev_err(dev, "Invalid length of 0\n"); return -EINVAL; } if (length_in_bytes == 0) { dev_err(dev, "Invalid length of 0\n"); if (cmd == 'r' && val & 0x3) { dev_err(dev, "Invalid length, must be 4 byte aligned\n"); return -EINVAL; } if (cmd == 'R') reserve_size = val * 4; else reserve_size = val; } mutex_lock(&cpudata->mutex); p = (uint32_t *)cpudata->dump_vaddr; length_in_words = length_in_bytes >> 2; if (per_core) { /* per-core register */ if (cpudata->core_reg_used_num == cpudata->core_reg_num) { dev_err(dev, "Couldn't add per-core config, out of range\n"); Loading @@ -314,9 +404,9 @@ static ssize_t register_config_store(struct device *dev, } num_cores = num_possible_cpus(); extra_memory = length_in_bytes * num_cores; extra_memory = reserve_size * num_cores; used_memory = cpudata->used_memory + extra_memory; if (extra_memory / num_cores < length_in_bytes || if (extra_memory / num_cores < reserve_size || used_memory > cpudata->size || used_memory < cpudata->used_memory) { dev_err(dev, "Couldn't add per-core reg config, no enough memory\n"); Loading @@ -324,16 +414,15 @@ static ssize_t register_config_store(struct device *dev, goto err; } if (length_in_words > 3) { *(p + cpudata->core_reg_end_index) = register_offset; *(p + cpudata->core_reg_end_index + 1) = length_in_bytes; cpudata->core_reg_end_index += 2; } else { *(p + cpudata->core_reg_end_index) = register_offset | length_in_words; ret = config_cpuss_register(dev, p, cpudata->core_reg_end_index, cmd, register_offset, val); if (ret) goto err; if (cmd == 'r' && val == 4) cpudata->core_reg_end_index++; } else cpudata->core_reg_end_index += 2; cpudata->core_reg_used_num++; cpudata->used_memory = used_memory; Loading @@ -341,10 +430,13 @@ static ssize_t register_config_store(struct device *dev, system_reg_end_index = *(p + SYS_REG_INPUT_INDEX) + cpudata->sys_reg_size / sizeof(uint32_t); if (length_in_words > 3) { extra_memory = sizeof(uint32_t) * 2 + length_in_bytes; if (cmd == 'r' && reserve_size == 4) extra_memory = sizeof(uint32_t) + reserve_size; else extra_memory = sizeof(uint32_t) * 2 + reserve_size; used_memory = cpudata->used_memory + extra_memory; if (extra_memory < length_in_bytes || if (extra_memory < reserve_size || used_memory > cpudata->size || used_memory < cpudata->used_memory) { dev_err(dev, "Couldn't add system reg config, no enough memory\n"); Loading @@ -352,29 +444,20 @@ static ssize_t register_config_store(struct device *dev, goto err; } *(p + system_reg_end_index) = register_offset; *(p + system_reg_end_index + 1) = length_in_bytes; system_reg_end_index += 2; cpudata->sys_reg_size += sizeof(uint32_t) * 2; } else { extra_memory = sizeof(uint32_t) + length_in_bytes; used_memory = cpudata->used_memory + extra_memory; if (extra_memory < length_in_bytes || used_memory > cpudata->size || used_memory < cpudata->used_memory) { dev_err(dev, "Couldn't add system reg config, no enough memory\n"); ret = -ENOMEM; ret = config_cpuss_register(dev, p, system_reg_end_index, cmd, register_offset, val); if (ret) goto err; } *(p + system_reg_end_index) = register_offset | length_in_words; if (cmd == 'r' && val == 4) { system_reg_end_index++; cpudata->sys_reg_size += sizeof(uint32_t); } else { system_reg_end_index += 2; cpudata->sys_reg_size += sizeof(uint32_t) * 2; } cpudata->used_memory = used_memory; *(p + system_reg_end_index) = 0x0; *(p + OUTPUT_DUMP_INDEX) = (system_reg_end_index + 1) * sizeof(uint32_t); Loading @@ -388,6 +471,24 @@ static ssize_t register_config_store(struct device *dev, } static DEVICE_ATTR_RW(register_config); static ssize_t format_version_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret; struct reg_dump_data *p; struct cpuss_dump_data *cpudata = dev_get_drvdata(dev); if (!cpudata) return -EFAULT; mutex_lock(&cpudata->mutex); p = (struct reg_dump_data *)cpudata->dump_vaddr; ret = scnprintf(buf, PAGE_SIZE, "%u\n", p->version); mutex_unlock(&cpudata->mutex); return ret; } static DEVICE_ATTR_RO(format_version); /** * This function resets the register dump table. */ Loading @@ -412,6 +513,7 @@ static const struct device_attribute *register_dump_attrs[] = { &dev_attr_core_reg_num, &dev_attr_register_config, &dev_attr_register_reset, &dev_attr_format_version, NULL, }; Loading Loading
drivers/soc/qcom/memory_dump_v2.c +173 −71 Original line number Diff line number Diff line // SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014-2017, 2019, The Linux Foundation. All rights reserved. * Copyright (c) 2014-2017, 2019-2020, The Linux Foundation. All rights reserved. */ #include <asm/cacheflush.h> Loading Loading @@ -28,6 +28,7 @@ #define INPUT_DATA_BY_HLOS 0x00C0FFEE #define FORMAT_VERSION_1 0x1 #define FORMAT_VERSION_2 0x2 #define CORE_REG_NUM_DEFAULT 0x1 #define MAGIC_INDEX 0 Loading @@ -37,6 +38,20 @@ #define PERCORE_INDEX 4 #define SYSTEM_REGS_INPUT_INDEX 5 #define CMD_REPEAT_READ (0x2 << 24) #define CMD_DELAY (0x1 << 24) #define CMD_READ 0x0 #define CMD_READ_WORD 0x1 #define CMD_WRITE 0x2 #define CMD_EXTRA 0x3 #define CMD_MASK 0x3 #define OFFSET_MASK GENMASK(31, 2) #define EXTRA_CMD_MASK GENMASK(31, 24) #define EXTRA_VALUE_MASK GENMASK(23, 0) #define MAX_EXTRA_VALUE 0xffffff struct cpuss_dump_data { void *dump_vaddr; u32 size; Loading Loading @@ -119,7 +134,7 @@ static int update_reg_dump_table(struct device *dev, u32 core_reg_num) memset(cpudata->dump_vaddr, 0xDE, cpudata->size); p = (struct reg_dump_data *)cpudata->dump_vaddr; p->magic = INPUT_DATA_BY_HLOS; p->version = FORMAT_VERSION_1; p->version = FORMAT_VERSION_2; p->system_regs_input_index = system_regs_input_index; p->regdump_output_byte_offset = regdump_output_byte_offset; memset((uint32_t *)cpudata->dump_vaddr + PERCORE_INDEX, 0x0, Loading Loading @@ -197,9 +212,8 @@ static ssize_t register_config_show(struct device *dev, char local_buf[64]; int len = 0, count = 0; int index, system_index_start, index_end; uint32_t register_offset, length_in_bytes; uint32_t length_in_words; uint32_t *p; uint32_t register_offset, val; uint32_t *p, cmd; struct cpuss_dump_data *cpudata = dev_get_drvdata(dev); buf[0] = '\0'; Loading @@ -212,7 +226,7 @@ static ssize_t register_config_show(struct device *dev, p = (uint32_t *)cpudata->dump_vaddr; /* print per-core & system registers */ len = snprintf(local_buf, 64, "per-core registers:\n"); len = scnprintf(local_buf, 64, "per-core registers:\n"); strlcat(buf, local_buf, PAGE_SIZE); count += len; Loading @@ -221,7 +235,7 @@ static ssize_t register_config_show(struct device *dev, cpudata->sys_reg_size / sizeof(uint32_t) + 1; for (index = PERCORE_INDEX; index < index_end;) { if (index == system_index_start) { len = snprintf(local_buf, 64, "system registers:\n"); len = scnprintf(local_buf, 64, "system registers:\n"); if ((count + len) > PAGE_SIZE) { dev_err(dev, "Couldn't write complete config\n"); break; Loading @@ -237,19 +251,44 @@ static ssize_t register_config_show(struct device *dev, continue; } if (register_offset & 0x3) { length_in_words = register_offset & 0x3; length_in_bytes = length_in_words << 2; len = snprintf(local_buf, 64, "Index: 0x%x, addr: 0x%x\n", index, register_offset); cmd = register_offset & CMD_MASK; register_offset &= OFFSET_MASK; switch (cmd) { case CMD_READ: val = *(p + index + 1); len = scnprintf(local_buf, 64, "0x%x, 0x%x, r\n", register_offset, val); index += 2; break; case CMD_READ_WORD: len = scnprintf(local_buf, 64, "0x%x, 0x%x, r\n", register_offset, 0x4); index++; } else { length_in_bytes = *(p + index + 1); len = snprintf(local_buf, 64, "Index: 0x%x, addr: 0x%x, length: 0x%x\n", index, register_offset, length_in_bytes); break; case CMD_WRITE: val = *(p + index + 1); len = scnprintf(local_buf, 64, "0x%x, 0x%x, w\n", register_offset, val); index += 2; break; case CMD_EXTRA: val = *(p + index + 1); cmd = val & EXTRA_CMD_MASK; val &= EXTRA_VALUE_MASK; if (cmd == CMD_DELAY) len = scnprintf(local_buf, 64, "0x%x, 0x%x, d\n", register_offset, val); else len = scnprintf(local_buf, 64, "0x%x, 0x%x, R\n", register_offset, val); index += 2; break; } if ((count + len) > PAGE_SIZE) { Loading @@ -265,6 +304,49 @@ static ssize_t register_config_show(struct device *dev, return count; } static int config_cpuss_register(struct device *dev, uint32_t *p, uint32_t index, char cmd, uint32_t register_offset, uint32_t val) { int ret = 0; switch (cmd) { case 'r': if (val > 4) { *(p + index) = register_offset; *(p + index + 1) = val; } else { *(p + index) = register_offset | CMD_READ_WORD; } break; case 'R': if (val > MAX_EXTRA_VALUE) { dev_err(dev, "repeat read time exceeded the limit\n"); ret = -EINVAL; return ret; } *(p + index) = register_offset | CMD_EXTRA; *(p + index + 1) = val | CMD_REPEAT_READ; break; case 'd': if (val > MAX_EXTRA_VALUE) { dev_err(dev, "sleep time exceeded the limit\n"); ret = -EINVAL; return ret; } *(p + index) = CMD_EXTRA; *(p + index + 1) = val | CMD_DELAY; break; case 'w': *(p + index) = register_offset | CMD_WRITE; *(p + index + 1) = val; break; default: dev_err(dev, "Don't support this command\n"); ret = -EINVAL; } return ret; } /** * This function sets configs of per-core or system registers. */ Loading @@ -273,9 +355,9 @@ static ssize_t register_config_store(struct device *dev, const char *buf, size_t size) { int ret; uint32_t register_offset, length_in_bytes, per_core = 0; uint32_t length_in_words; uint32_t register_offset, val, reserve_size = 4, per_core = 0; int nval; char cmd; uint32_t num_cores; u32 extra_memory; u32 used_memory; Loading @@ -283,29 +365,37 @@ static ssize_t register_config_store(struct device *dev, uint32_t *p; struct cpuss_dump_data *cpudata = dev_get_drvdata(dev); nval = sscanf(buf, "%x %x %u", ®ister_offset, &length_in_bytes, &per_core); if (nval != 2 && nval != 3) nval = sscanf(buf, "%x %x %c %u", ®ister_offset, &val, &cmd, &per_core); if (nval < 2) return -EINVAL; if (nval == 2) cmd = 'r'; if (per_core > 1) return -EINVAL; if (register_offset & 0x3) { dev_err(dev, "Invalid address, must be 4 byte aligned\n"); return -EINVAL; } if (length_in_bytes & 0x3) { dev_err(dev, "Invalid length, must be 4 byte aligned\n"); if (cmd == 'r' || cmd == 'R') { if (val == 0) { dev_err(dev, "Invalid length of 0\n"); return -EINVAL; } if (length_in_bytes == 0) { dev_err(dev, "Invalid length of 0\n"); if (cmd == 'r' && val & 0x3) { dev_err(dev, "Invalid length, must be 4 byte aligned\n"); return -EINVAL; } if (cmd == 'R') reserve_size = val * 4; else reserve_size = val; } mutex_lock(&cpudata->mutex); p = (uint32_t *)cpudata->dump_vaddr; length_in_words = length_in_bytes >> 2; if (per_core) { /* per-core register */ if (cpudata->core_reg_used_num == cpudata->core_reg_num) { dev_err(dev, "Couldn't add per-core config, out of range\n"); Loading @@ -314,9 +404,9 @@ static ssize_t register_config_store(struct device *dev, } num_cores = num_possible_cpus(); extra_memory = length_in_bytes * num_cores; extra_memory = reserve_size * num_cores; used_memory = cpudata->used_memory + extra_memory; if (extra_memory / num_cores < length_in_bytes || if (extra_memory / num_cores < reserve_size || used_memory > cpudata->size || used_memory < cpudata->used_memory) { dev_err(dev, "Couldn't add per-core reg config, no enough memory\n"); Loading @@ -324,16 +414,15 @@ static ssize_t register_config_store(struct device *dev, goto err; } if (length_in_words > 3) { *(p + cpudata->core_reg_end_index) = register_offset; *(p + cpudata->core_reg_end_index + 1) = length_in_bytes; cpudata->core_reg_end_index += 2; } else { *(p + cpudata->core_reg_end_index) = register_offset | length_in_words; ret = config_cpuss_register(dev, p, cpudata->core_reg_end_index, cmd, register_offset, val); if (ret) goto err; if (cmd == 'r' && val == 4) cpudata->core_reg_end_index++; } else cpudata->core_reg_end_index += 2; cpudata->core_reg_used_num++; cpudata->used_memory = used_memory; Loading @@ -341,10 +430,13 @@ static ssize_t register_config_store(struct device *dev, system_reg_end_index = *(p + SYS_REG_INPUT_INDEX) + cpudata->sys_reg_size / sizeof(uint32_t); if (length_in_words > 3) { extra_memory = sizeof(uint32_t) * 2 + length_in_bytes; if (cmd == 'r' && reserve_size == 4) extra_memory = sizeof(uint32_t) + reserve_size; else extra_memory = sizeof(uint32_t) * 2 + reserve_size; used_memory = cpudata->used_memory + extra_memory; if (extra_memory < length_in_bytes || if (extra_memory < reserve_size || used_memory > cpudata->size || used_memory < cpudata->used_memory) { dev_err(dev, "Couldn't add system reg config, no enough memory\n"); Loading @@ -352,29 +444,20 @@ static ssize_t register_config_store(struct device *dev, goto err; } *(p + system_reg_end_index) = register_offset; *(p + system_reg_end_index + 1) = length_in_bytes; system_reg_end_index += 2; cpudata->sys_reg_size += sizeof(uint32_t) * 2; } else { extra_memory = sizeof(uint32_t) + length_in_bytes; used_memory = cpudata->used_memory + extra_memory; if (extra_memory < length_in_bytes || used_memory > cpudata->size || used_memory < cpudata->used_memory) { dev_err(dev, "Couldn't add system reg config, no enough memory\n"); ret = -ENOMEM; ret = config_cpuss_register(dev, p, system_reg_end_index, cmd, register_offset, val); if (ret) goto err; } *(p + system_reg_end_index) = register_offset | length_in_words; if (cmd == 'r' && val == 4) { system_reg_end_index++; cpudata->sys_reg_size += sizeof(uint32_t); } else { system_reg_end_index += 2; cpudata->sys_reg_size += sizeof(uint32_t) * 2; } cpudata->used_memory = used_memory; *(p + system_reg_end_index) = 0x0; *(p + OUTPUT_DUMP_INDEX) = (system_reg_end_index + 1) * sizeof(uint32_t); Loading @@ -388,6 +471,24 @@ static ssize_t register_config_store(struct device *dev, } static DEVICE_ATTR_RW(register_config); static ssize_t format_version_show(struct device *dev, struct device_attribute *attr, char *buf) { int ret; struct reg_dump_data *p; struct cpuss_dump_data *cpudata = dev_get_drvdata(dev); if (!cpudata) return -EFAULT; mutex_lock(&cpudata->mutex); p = (struct reg_dump_data *)cpudata->dump_vaddr; ret = scnprintf(buf, PAGE_SIZE, "%u\n", p->version); mutex_unlock(&cpudata->mutex); return ret; } static DEVICE_ATTR_RO(format_version); /** * This function resets the register dump table. */ Loading @@ -412,6 +513,7 @@ static const struct device_attribute *register_dump_attrs[] = { &dev_attr_core_reg_num, &dev_attr_register_config, &dev_attr_register_reset, &dev_attr_format_version, NULL, }; Loading