Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit a70fac8a authored by qctecmdr's avatar qctecmdr Committed by Gerrit - the friendly Code Review server
Browse files

Merge "memory_dump: add new feature for cpuss dump"

parents 8a1d4d88 d1ec36b8
Loading
Loading
Loading
Loading
+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>
@@ -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
@@ -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;
@@ -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,
@@ -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';
@@ -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;

@@ -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;
@@ -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) {
@@ -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.
 */
@@ -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;
@@ -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", &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");

	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");
@@ -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");
@@ -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;
@@ -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");
@@ -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);
@@ -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.
 */
@@ -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,
};