Merge "memory_dump: add new feature for cpuss dump"

This commit is contained in:
qctecmdr 2020-02-21 23:21:33 -08:00 • committed by Gerrit - the friendly Code Review server
commit 18ac4e51b9

View file

@ -1,6 +1,6 @@
// 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 <linux/slab.h>
@ -27,6 +27,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
@ -36,6 +37,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;
@ -105,7 +120,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,
@ -182,9 +197,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';
@ -197,7 +211,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;
@ -206,7 +220,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;
@ -222,19 +236,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);
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);
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++;
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) {
@ -250,6 +289,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.
*/
@ -258,9 +340,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;
@ -268,29 +350,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", &register_offset,
&length_in_bytes, &per_core);
if (nval != 2 && nval != 3)
nval = sscanf(buf, "%x %x %c %u", &register_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");
return -EINVAL;
}
if (length_in_bytes == 0) {
dev_err(dev, "Invalid length of 0\n");
return -EINVAL;
if (cmd == 'r' || cmd == 'R') {
if (val == 0) {
dev_err(dev, "Invalid length of 0\n");
return -EINVAL;
}
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");
@ -299,26 +389,25 @@ 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 ||
used_memory > cpudata->size ||
used_memory < cpudata->used_memory) {
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");
ret = -ENOMEM;
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;
@ -326,40 +415,34 @@ 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;
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;
goto err;
}
if (cmd == 'r' && reserve_size == 4)
extra_memory = sizeof(uint32_t) + reserve_size;
else
extra_memory = sizeof(uint32_t) * 2 + reserve_size;
*(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;
goto err;
}
used_memory = cpudata->used_memory + extra_memory;
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");
ret = -ENOMEM;
goto err;
}
*(p + system_reg_end_index) = register_offset |
length_in_words;
ret = config_cpuss_register(dev, p, system_reg_end_index,
cmd, register_offset, val);
if (ret)
goto err;
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);
@ -373,6 +456,24 @@ err:
}
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.
*/
@ -397,6 +498,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,
};