1*770c69f0SMike Tipton // SPDX-License-Identifier: GPL-2.0-only
2*770c69f0SMike Tipton /*
3*770c69f0SMike Tipton * Copyright (c) 2023, Qualcomm Innovation Center, Inc. All rights reserved.
4*770c69f0SMike Tipton */
5*770c69f0SMike Tipton #include <linux/debugfs.h>
6*770c69f0SMike Tipton #include <linux/interconnect.h>
7*770c69f0SMike Tipton #include <linux/platform_device.h>
8*770c69f0SMike Tipton
9*770c69f0SMike Tipton #include "internal.h"
10*770c69f0SMike Tipton
11*770c69f0SMike Tipton /*
12*770c69f0SMike Tipton * This can be dangerous, therefore don't provide any real compile time
13*770c69f0SMike Tipton * configuration option for this feature.
14*770c69f0SMike Tipton * People who want to use this will need to modify the source code directly.
15*770c69f0SMike Tipton */
16*770c69f0SMike Tipton #undef INTERCONNECT_ALLOW_WRITE_DEBUGFS
17*770c69f0SMike Tipton
18*770c69f0SMike Tipton #if defined(INTERCONNECT_ALLOW_WRITE_DEBUGFS) && defined(CONFIG_DEBUG_FS)
19*770c69f0SMike Tipton
20*770c69f0SMike Tipton static LIST_HEAD(debugfs_paths);
21*770c69f0SMike Tipton static DEFINE_MUTEX(debugfs_lock);
22*770c69f0SMike Tipton
23*770c69f0SMike Tipton static struct platform_device *pdev;
24*770c69f0SMike Tipton static struct icc_path *cur_path;
25*770c69f0SMike Tipton
26*770c69f0SMike Tipton static char *src_node;
27*770c69f0SMike Tipton static char *dst_node;
28*770c69f0SMike Tipton static u32 avg_bw;
29*770c69f0SMike Tipton static u32 peak_bw;
30*770c69f0SMike Tipton static u32 tag;
31*770c69f0SMike Tipton
32*770c69f0SMike Tipton struct debugfs_path {
33*770c69f0SMike Tipton const char *src;
34*770c69f0SMike Tipton const char *dst;
35*770c69f0SMike Tipton struct icc_path *path;
36*770c69f0SMike Tipton struct list_head list;
37*770c69f0SMike Tipton };
38*770c69f0SMike Tipton
get_path(const char * src,const char * dst)39*770c69f0SMike Tipton static struct icc_path *get_path(const char *src, const char *dst)
40*770c69f0SMike Tipton {
41*770c69f0SMike Tipton struct debugfs_path *path;
42*770c69f0SMike Tipton
43*770c69f0SMike Tipton list_for_each_entry(path, &debugfs_paths, list) {
44*770c69f0SMike Tipton if (!strcmp(path->src, src) && !strcmp(path->dst, dst))
45*770c69f0SMike Tipton return path->path;
46*770c69f0SMike Tipton }
47*770c69f0SMike Tipton
48*770c69f0SMike Tipton return NULL;
49*770c69f0SMike Tipton }
50*770c69f0SMike Tipton
icc_get_set(void * data,u64 val)51*770c69f0SMike Tipton static int icc_get_set(void *data, u64 val)
52*770c69f0SMike Tipton {
53*770c69f0SMike Tipton struct debugfs_path *debugfs_path;
54*770c69f0SMike Tipton char *src, *dst;
55*770c69f0SMike Tipton int ret = 0;
56*770c69f0SMike Tipton
57*770c69f0SMike Tipton mutex_lock(&debugfs_lock);
58*770c69f0SMike Tipton
59*770c69f0SMike Tipton rcu_read_lock();
60*770c69f0SMike Tipton src = rcu_dereference(src_node);
61*770c69f0SMike Tipton dst = rcu_dereference(dst_node);
62*770c69f0SMike Tipton
63*770c69f0SMike Tipton /*
64*770c69f0SMike Tipton * If we've already looked up a path, then use the existing one instead
65*770c69f0SMike Tipton * of calling icc_get() again. This allows for updating previous BW
66*770c69f0SMike Tipton * votes when "get" is written to multiple times for multiple paths.
67*770c69f0SMike Tipton */
68*770c69f0SMike Tipton cur_path = get_path(src, dst);
69*770c69f0SMike Tipton if (cur_path) {
70*770c69f0SMike Tipton rcu_read_unlock();
71*770c69f0SMike Tipton goto out;
72*770c69f0SMike Tipton }
73*770c69f0SMike Tipton
74*770c69f0SMike Tipton src = kstrdup(src, GFP_ATOMIC);
75*770c69f0SMike Tipton dst = kstrdup(dst, GFP_ATOMIC);
76*770c69f0SMike Tipton rcu_read_unlock();
77*770c69f0SMike Tipton
78*770c69f0SMike Tipton if (!src || !dst) {
79*770c69f0SMike Tipton ret = -ENOMEM;
80*770c69f0SMike Tipton goto err_free;
81*770c69f0SMike Tipton }
82*770c69f0SMike Tipton
83*770c69f0SMike Tipton cur_path = icc_get(&pdev->dev, src, dst);
84*770c69f0SMike Tipton if (IS_ERR(cur_path)) {
85*770c69f0SMike Tipton ret = PTR_ERR(cur_path);
86*770c69f0SMike Tipton goto err_free;
87*770c69f0SMike Tipton }
88*770c69f0SMike Tipton
89*770c69f0SMike Tipton debugfs_path = kzalloc(sizeof(*debugfs_path), GFP_KERNEL);
90*770c69f0SMike Tipton if (!debugfs_path) {
91*770c69f0SMike Tipton ret = -ENOMEM;
92*770c69f0SMike Tipton goto err_put;
93*770c69f0SMike Tipton }
94*770c69f0SMike Tipton
95*770c69f0SMike Tipton debugfs_path->path = cur_path;
96*770c69f0SMike Tipton debugfs_path->src = src;
97*770c69f0SMike Tipton debugfs_path->dst = dst;
98*770c69f0SMike Tipton list_add_tail(&debugfs_path->list, &debugfs_paths);
99*770c69f0SMike Tipton
100*770c69f0SMike Tipton goto out;
101*770c69f0SMike Tipton
102*770c69f0SMike Tipton err_put:
103*770c69f0SMike Tipton icc_put(cur_path);
104*770c69f0SMike Tipton err_free:
105*770c69f0SMike Tipton kfree(src);
106*770c69f0SMike Tipton kfree(dst);
107*770c69f0SMike Tipton out:
108*770c69f0SMike Tipton mutex_unlock(&debugfs_lock);
109*770c69f0SMike Tipton return ret;
110*770c69f0SMike Tipton }
111*770c69f0SMike Tipton
112*770c69f0SMike Tipton DEFINE_DEBUGFS_ATTRIBUTE(icc_get_fops, NULL, icc_get_set, "%llu\n");
113*770c69f0SMike Tipton
icc_commit_set(void * data,u64 val)114*770c69f0SMike Tipton static int icc_commit_set(void *data, u64 val)
115*770c69f0SMike Tipton {
116*770c69f0SMike Tipton int ret;
117*770c69f0SMike Tipton
118*770c69f0SMike Tipton mutex_lock(&debugfs_lock);
119*770c69f0SMike Tipton
120*770c69f0SMike Tipton if (IS_ERR_OR_NULL(cur_path)) {
121*770c69f0SMike Tipton ret = PTR_ERR(cur_path);
122*770c69f0SMike Tipton goto out;
123*770c69f0SMike Tipton }
124*770c69f0SMike Tipton
125*770c69f0SMike Tipton icc_set_tag(cur_path, tag);
126*770c69f0SMike Tipton ret = icc_set_bw(cur_path, avg_bw, peak_bw);
127*770c69f0SMike Tipton out:
128*770c69f0SMike Tipton mutex_unlock(&debugfs_lock);
129*770c69f0SMike Tipton return ret;
130*770c69f0SMike Tipton }
131*770c69f0SMike Tipton
132*770c69f0SMike Tipton DEFINE_DEBUGFS_ATTRIBUTE(icc_commit_fops, NULL, icc_commit_set, "%llu\n");
133*770c69f0SMike Tipton
icc_debugfs_client_init(struct dentry * icc_dir)134*770c69f0SMike Tipton int icc_debugfs_client_init(struct dentry *icc_dir)
135*770c69f0SMike Tipton {
136*770c69f0SMike Tipton struct dentry *client_dir;
137*770c69f0SMike Tipton int ret;
138*770c69f0SMike Tipton
139*770c69f0SMike Tipton pdev = platform_device_alloc("icc-debugfs-client", PLATFORM_DEVID_NONE);
140*770c69f0SMike Tipton
141*770c69f0SMike Tipton ret = platform_device_add(pdev);
142*770c69f0SMike Tipton if (ret) {
143*770c69f0SMike Tipton pr_err("%s: failed to add platform device: %d\n", __func__, ret);
144*770c69f0SMike Tipton platform_device_put(pdev);
145*770c69f0SMike Tipton return ret;
146*770c69f0SMike Tipton }
147*770c69f0SMike Tipton
148*770c69f0SMike Tipton client_dir = debugfs_create_dir("test_client", icc_dir);
149*770c69f0SMike Tipton
150*770c69f0SMike Tipton debugfs_create_str("src_node", 0600, client_dir, &src_node);
151*770c69f0SMike Tipton debugfs_create_str("dst_node", 0600, client_dir, &dst_node);
152*770c69f0SMike Tipton debugfs_create_file("get", 0200, client_dir, NULL, &icc_get_fops);
153*770c69f0SMike Tipton debugfs_create_u32("avg_bw", 0600, client_dir, &avg_bw);
154*770c69f0SMike Tipton debugfs_create_u32("peak_bw", 0600, client_dir, &peak_bw);
155*770c69f0SMike Tipton debugfs_create_u32("tag", 0600, client_dir, &tag);
156*770c69f0SMike Tipton debugfs_create_file("commit", 0200, client_dir, NULL, &icc_commit_fops);
157*770c69f0SMike Tipton
158*770c69f0SMike Tipton return 0;
159*770c69f0SMike Tipton }
160*770c69f0SMike Tipton
161*770c69f0SMike Tipton #else
162*770c69f0SMike Tipton
icc_debugfs_client_init(struct dentry * icc_dir)163*770c69f0SMike Tipton int icc_debugfs_client_init(struct dentry *icc_dir)
164*770c69f0SMike Tipton {
165*770c69f0SMike Tipton return 0;
166*770c69f0SMike Tipton }
167*770c69f0SMike Tipton
168*770c69f0SMike Tipton #endif
169