Ver código fonte

disp: msm: sde: add profiling counters support for RSC

Add support for enabling and reading profiling counters via
debugfs. This change also introduces RSC rev 4 (first rev
supporting profiling counters), enabling all relevant rev 3
features as well.

Change-Id: I0326215b069a37c91072965379b0b4843916ee0a
Signed-off-by: Steve Cohen <[email protected]>
Steve Cohen 5 anos atrás
pai
commit
80aa9f9c32
5 arquivos alterados com 240 adições e 9 exclusões
  1. 2 1
      include/linux/sde_rsc.h
  2. 173 4
      msm/sde_rsc.c
  3. 5 1
      msm/sde_rsc_hw.h
  4. 49 1
      msm/sde_rsc_hw_v3.c
  5. 11 2
      msm/sde_rsc_priv.h

+ 2 - 1
include/linux/sde_rsc.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _SDE_RSC_H_
@@ -12,6 +12,7 @@
 #define SDE_RSC_INDEX		0
 
 #define MAX_RSC_CLIENT_NAME_LEN 128
+#define NUM_RSC_PROFILING_COUNTERS 3
 
 /* DRM Object IDs are numbered excluding 0, use 0 to indicate invalid CRTC */
 #define SDE_RSC_INVALID_CRTC_ID 0

+ 173 - 4
msm/sde_rsc.c

@@ -682,9 +682,9 @@ static int sde_rsc_switch_to_vid(struct sde_rsc_priv *rsc,
 		rc = rsc->hw_ops.state_update(rsc, SDE_RSC_VID_STATE);
 		if (!rc) {
 			rpmh_mode_solver_set(rsc->rpmh_dev,
-				rsc->version == SDE_RSC_REV_3 ? true : false);
+				rsc->version >= SDE_RSC_REV_3);
 			sde_rsc_set_data_bus_mode(&rsc->phandle,
-				rsc->version == SDE_RSC_REV_3 ?
+				rsc->version >= SDE_RSC_REV_3 ?
 				QCOM_ICC_TAG_WAKE : QCOM_ICC_TAG_AMC);
 		}
 	}
@@ -1176,6 +1176,49 @@ static int _sde_debugfs_status_open(struct inode *inode, struct file *file)
 	return single_open(file, _sde_debugfs_status_show, inode->i_private);
 }
 
+static int _sde_debugfs_counters_show(struct seq_file *s, void *data)
+{
+	struct sde_rsc_priv *rsc = s->private;
+	u32 counters[NUM_RSC_PROFILING_COUNTERS];
+	int i, ret;
+
+	if (!rsc)
+		return -EINVAL;
+
+	if (!rsc->hw_ops.get_counters) {
+		seq_puts(s, "counters are not supported on this target\n");
+		return 0;
+	}
+
+	memset(counters, 0, sizeof(counters));
+	mutex_lock(&rsc->client_lock);
+	if (rsc->current_state == SDE_RSC_IDLE_STATE) {
+		pr_debug("counters are not supported during idle state\n");
+		seq_puts(s, "no access to counters during idle pc\n");
+		goto end;
+	}
+
+	ret = rsc->hw_ops.get_counters(rsc, counters);
+	if (ret) {
+		pr_err("sde rsc: get_counters failed ret:%d\n", ret);
+		seq_puts(s, "failed to retrieve counts!\n");
+		goto end;
+	}
+
+	seq_puts(s, "rsc profiling counters:\n");
+	for (i = 0; i < NUM_RSC_PROFILING_COUNTERS; ++i)
+		seq_printf(s, "\tmode[%d] = 0x%08x:\n", i, counters[i]);
+
+end:
+	mutex_unlock(&rsc->client_lock);
+	return 0;
+}
+
+static int _sde_debugfs_counters_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, _sde_debugfs_counters_show, inode->i_private);
+}
+
 static int _sde_debugfs_generic_noseek_open(struct inode *inode,
 		struct file *file)
 {
@@ -1184,6 +1227,108 @@ static int _sde_debugfs_generic_noseek_open(struct inode *inode,
 	return nonseekable_open(inode, file);
 }
 
+static ssize_t _sde_debugfs_profiling_read(struct file *file, char __user *buf,
+				size_t count, loff_t *ppos)
+{
+	struct sde_rsc_priv *rsc = file->private_data;
+	size_t max_size = min_t(size_t, count, MAX_BUFFER_SIZE);
+	char buffer[MAX_BUFFER_SIZE];
+	int blen = 0;
+
+	if (*ppos || !rsc)
+		return 0;
+
+	if (!rsc->hw_ops.setup_counters) {
+		blen += scnprintf(&buffer[blen], max_size - blen,
+				"counters are not supported on this target\n");
+		goto end;
+	}
+
+	mutex_lock(&rsc->client_lock);
+	if (rsc->current_state == SDE_RSC_IDLE_STATE) {
+		pr_debug("counters are not supported during idle state\n");
+		blen += scnprintf(&buffer[blen], max_size - blen,
+				"no access to counters during idle pc\n");
+		goto unlock;
+	}
+
+	blen += scnprintf(&buffer[blen], max_size - blen,
+			"%s\n", rsc->profiling_en ? "Y" : "N");
+
+unlock:
+	mutex_unlock(&rsc->client_lock);
+end:
+	if (copy_to_user(buf, buffer, blen))
+		return -EFAULT;
+
+	*ppos += blen;
+	return blen;
+}
+
+static ssize_t _sde_debugfs_profiling_write(struct file *file,
+			const char __user *p, size_t count, loff_t *ppos)
+{
+	struct sde_rsc_priv *rsc = file->private_data;
+	bool input_valid, input_value;
+	char *input;
+	int rc;
+
+	if (!rsc || !rsc->hw_ops.setup_counters || !count ||
+			count > MAX_COUNT_SIZE_SUPPORTED)
+		return 0;
+
+	input = kmalloc(count + 1, GFP_KERNEL);
+	if (!input)
+		return -ENOMEM;
+
+	if (copy_from_user(input, p, count)) {
+		kfree(input);
+		return -EFAULT;
+	}
+	input[count] = '\0';
+
+	switch (input[0]) {
+	case 'y':
+	case 'Y':
+	case '1':
+		input_valid = true;
+		input_value = true;
+		break;
+	case 'n':
+	case 'N':
+	case '0':
+		input_valid = true;
+		input_value = false;
+		break;
+	default:
+		input_valid = false;
+		count = 0;
+		break;
+	}
+
+	mutex_lock(&rsc->client_lock);
+	if (rsc->current_state == SDE_RSC_IDLE_STATE) {
+		pr_debug("debug node is not supported during idle state\n");
+		count = 0;
+		goto end;
+	}
+
+	pr_debug("input %s, profiling_en: %d\n",
+			input_valid ? "valid" : "invalid", rsc->profiling_en);
+
+	if (input_valid) {
+		rsc->profiling_en = input_value;
+		rc = rsc->hw_ops.setup_counters(rsc, rsc->profiling_en);
+		if (rc)
+			pr_err("setup_counters failed, rc:%d\n", rc);
+	}
+
+end:
+	mutex_unlock(&rsc->client_lock);
+	kfree(input);
+	return count;
+}
+
 static ssize_t _sde_debugfs_mode_ctrl_read(struct file *file, char __user *buf,
 				size_t count, loff_t *ppos)
 {
@@ -1380,6 +1525,19 @@ static const struct file_operations vsync_status_fops = {
 	.write =	_sde_debugfs_vsync_mode_write,
 };
 
+static const struct file_operations profiling_enable_fops = {
+	.open =		_sde_debugfs_generic_noseek_open,
+	.read =		_sde_debugfs_profiling_read,
+	.write =	_sde_debugfs_profiling_write,
+};
+
+static const struct file_operations profiling_counts_fops = {
+	.open =		_sde_debugfs_counters_open,
+	.read =		seq_read,
+	.llseek =	seq_lseek,
+	.release =	single_release,
+};
+
 static void _sde_rsc_init_debugfs(struct sde_rsc_priv *rsc, char *name)
 {
 	rsc->debugfs_root = debugfs_create_dir(name, NULL);
@@ -1393,6 +1551,14 @@ static void _sde_rsc_init_debugfs(struct sde_rsc_priv *rsc, char *name)
 							&mode_control_fops);
 	debugfs_create_file("vsync_mode", 0600, rsc->debugfs_root, rsc,
 							&vsync_status_fops);
+	if (rsc->profiling_supp) {
+		debugfs_create_file("profiling_en", 0600, rsc->debugfs_root,
+				rsc, &profiling_enable_fops);
+		debugfs_create_file("profiling_counts", 0400,
+				rsc->debugfs_root, rsc,
+				&profiling_counts_fops);
+	}
+
 	debugfs_create_x32("debug_mode", 0600, rsc->debugfs_root,
 							&rsc->debug_mode);
 }
@@ -1515,12 +1681,12 @@ static int sde_rsc_probe(struct platform_device *pdev)
 	of_property_read_u32(pdev->dev.of_node, "qcom,sde-rsc-version",
 								&rsc->version);
 
-	if (rsc->version == SDE_RSC_REV_2)
+	if (rsc->version >= SDE_RSC_REV_2)
 		rsc->single_tcs_execution_time = SINGLE_TCS_EXECUTION_TIME_V2;
 	else
 		rsc->single_tcs_execution_time = SINGLE_TCS_EXECUTION_TIME_V1;
 
-	if (rsc->version == SDE_RSC_REV_3) {
+	if (rsc->version >= SDE_RSC_REV_3) {
 		rsc->time_slot_0_ns = rsc->single_tcs_execution_time
 					+ RSC_MODE_INSTRUCTION_TIME;
 		rsc->backoff_time_ns = RSC_MODE_INSTRUCTION_TIME;
@@ -1534,6 +1700,9 @@ static int sde_rsc_probe(struct platform_device *pdev)
 						+ RSC_MODE_THRESHOLD_OVERHEAD;
 	}
 
+	if (rsc->version >= SDE_RSC_REV_4)
+		rsc->profiling_supp = true;
+
 	ret = sde_power_resource_init(pdev, &rsc->phandle);
 	if (ret) {
 		pr_err("sde rsc:power resource init failed ret:%d\n", ret);

+ 5 - 1
msm/sde_rsc_hw.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0 */
 /*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _SDE_RSC_HW_H_
@@ -65,6 +65,10 @@
 
 #define SDE_RSCC_TCS_DRV0_CONTROL			0x1c14
 
+#define SDE_RSCC_LPM_PROFILING_COUNTER0_EN_DRV0		0x4d00
+#define SDE_RSCC_LPM_PROFILING_COUNTER0_CLR_DRV0	0x4d04
+#define SDE_RSCC_LPM_PROFILING_COUNTER0_STATUS_DRV0	0x4d08
+
 #define SDE_RSCC_WRAPPER_CTRL				0x000
 #define SDE_RSCC_WRAPPER_OVERRIDE_CTRL			0x004
 #define SDE_RSCC_WRAPPER_STATIC_WAKEUP_0		0x008

+ 49 - 1
msm/sde_rsc_hw_v3.c

@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
  */
 
 #define pr_fmt(fmt)	"[sde_rsc_hw:%s:%d]: " fmt, __func__, __LINE__
@@ -561,6 +561,50 @@ int rsc_hw_bwi_status_v3(struct sde_rsc_priv *rsc, bool bw_indication)
 	return rc;
 }
 
+static int rsc_hw_profiling_counter_ctrl(struct sde_rsc_priv *rsc, bool enable)
+{
+	int i;
+
+	if (!rsc) {
+		pr_debug("invalid input param\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < NUM_RSC_PROFILING_COUNTERS; ++i) {
+		dss_reg_w(&rsc->drv_io,
+			SDE_RSCC_LPM_PROFILING_COUNTER0_EN_DRV0 +
+			(0x20 * i), enable ? 1 : 0, rsc->debug_mode);
+		dss_reg_w(&rsc->drv_io,
+			SDE_RSCC_LPM_PROFILING_COUNTER0_CLR_DRV0 +
+			(0x20 * i), 1, rsc->debug_mode);
+	}
+
+	wmb(); /* make sure counters are cleared now */
+	pr_debug("rsc profiling counters %s and cleared\n",
+			enable ? "enabled" : "disabled");
+
+	return 0;
+}
+
+static int rsc_hw_get_profiling_counter_status(struct sde_rsc_priv *rsc,
+		u32 *counters)
+{
+	int i;
+
+	if (!rsc || !counters) {
+		pr_debug("invalid input param, %d %d\n",
+				rsc ? 0 : 1, counters ? 0 : 1);
+		return -EINVAL;
+	}
+
+	for (i = 0; i < NUM_RSC_PROFILING_COUNTERS; ++i)
+		counters[i] = dss_reg_r(&rsc->drv_io,
+				SDE_RSCC_LPM_PROFILING_COUNTER0_STATUS_DRV0 +
+				(0x20 * i), rsc->debug_mode);
+
+	return 0;
+}
+
 static int rsc_hw_timer_update_v3(struct sde_rsc_priv *rsc)
 {
 	if (!rsc) {
@@ -620,6 +664,10 @@ int sde_rsc_hw_register_v3(struct sde_rsc_priv *rsc)
 	rsc->hw_ops.debug_show = sde_rsc_debug_show;
 	rsc->hw_ops.mode_ctrl = rsc_hw_mode_ctrl;
 	rsc->hw_ops.debug_dump = rsc_hw_debug_dump;
+	if (rsc->profiling_supp) {
+		rsc->hw_ops.setup_counters = rsc_hw_profiling_counter_ctrl;
+		rsc->hw_ops.get_counters = rsc_hw_get_profiling_counter_status;
+	}
 
 	return 0;
 }

+ 11 - 2
msm/sde_rsc_priv.h

@@ -1,6 +1,6 @@
 /* SPDX-License-Identifier: GPL-2.0-only */
 /*
- * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
  */
 
 #ifndef _SDE_RSC_PRIV_H_
@@ -27,6 +27,7 @@
 #define SDE_RSC_REV_1			0x1
 #define SDE_RSC_REV_2			0x2
 #define SDE_RSC_REV_3			0x3
+#define SDE_RSC_REV_4			0x4
 
 #define SDE_RSC_HW_MAJOR_MINOR_STEP(major, minor, step) \
 	(((major & 0xff) << 16) |\
@@ -78,8 +79,10 @@ enum rsc_vsync_req {
  * @debug_dump:			dump debug bus registers or enable debug bus
  * @state_update:		Enable/override the solver based on rsc state
  *                              status (command/video)
- * @mode_show:			shows current mode status, mode0/1/2
  * @debug_show:			Show current debug status.
+ * @mode_ctrl:			shows current mode status, mode0/1/2
+ * @setup_counters:		Enable/disable RSC profiling counters
+ * @get_counters:		Get current status of profiling counters
  */
 
 struct sde_rsc_hw_ops {
@@ -96,6 +99,8 @@ struct sde_rsc_hw_ops {
 	int (*debug_show)(struct seq_file *s, struct sde_rsc_priv *rsc);
 	int (*mode_ctrl)(struct sde_rsc_priv *rsc, enum rsc_mode_req request,
 		char *buffer, int buffer_size, u32 mode);
+	int (*setup_counters)(struct sde_rsc_priv *rsc, bool enable);
+	int (*get_counters)(struct sde_rsc_priv *rsc, u32 *counters);
 };
 
 /**
@@ -186,6 +191,8 @@ struct sde_rsc_bw_config {
  * bw_config:		check sde_rsc_bw_config structure description.
  * dev:			rsc device node
  * resource_refcount:	Track rsc resource refcount
+ * profiling_supp:	Indicates if HW has support for profiling counters
+ * profiling_en:	Flag for rsc lpm profiling counters, true=enabled
  */
 struct sde_rsc_priv {
 	u32 version;
@@ -227,6 +234,8 @@ struct sde_rsc_priv {
 	struct sde_rsc_bw_config bw_config;
 	struct device *dev;
 	atomic_t resource_refcount;
+	bool profiling_supp;
+	bool profiling_en;
 };
 
 /**