blob: 113290382587bd99f319b948d6f948bf12337e8d [file] [log] [blame]
developerfd40db22021-04-29 10:08:25 +08001/* Copyright 2016 MediaTek Inc.
2 * Author: Nelson Chang <nelson.chang@mediatek.com>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13#include "raether.h"
14#include "ra_dbg_proc.h"
15
16#define MCSI_A_PMU_CTL 0x10390100 /* PMU CTRL */
17#define MCSI_A_PMU_CYC_CNT 0x10399004 /* Cycle counter */
18#define MCSI_A_PMU_CYC_CTL 0x10399008 /* Cycle counter CTRL */
19
20#define MCSI_A_PMU_EVN_SEL0 0x1039A000 /* EVENT SELECT 0 */
21#define MCSI_A_PMU_EVN_CNT0 0x1039A004 /* Event Count 0 */
22#define MCSI_A_PMU_EVN_CTL0 0x1039A008 /* Event Count control 0 */
23
24#define MCSI_A_PMU_EVN_SEL1 0x1039B000 /* EVENT SELECT 1 */
25#define MCSI_A_PMU_EVN_CNT1 0x1039B004 /* Event Count 1 */
26#define MCSI_A_PMU_EVN_CTL1 0x1039B008 /* Event Count control 1 */
27
28#define MCSI_A_PMU_EVN_SEL2 0x1039C000 /* EVENT SELECT 2 */
29#define MCSI_A_PMU_EVN_CNT2 0x1039C004 /* Event Count 2 */
30#define MCSI_A_PMU_EVN_CTL2 0x1039C008 /* Event Count control 2 */
31
32#define MCSI_A_PMU_EVN_SEL3 0x1039D000 /* EVENT SELECT 3 */
33#define MCSI_A_PMU_EVN_CNT3 0x1039D004 /* Event Count 3 */
34#define MCSI_A_PMU_EVN_CTL3 0x1039D008 /* Event Count control 3 */
35
36#define PMU_EVN_SEL_S0 (0x0 << 5)
37#define PMU_EVN_SEL_S1 (0x1 << 5)
38#define PMU_EVN_SEL_S2 (0x2 << 5)
39#define PMU_EVN_SEL_S3 (0x3 << 5)
40#define PMU_EVN_SEL_S4 (0x4 << 5)
41#define PMU_EVN_SEL_S5 (0x5 << 5)
42#define PMU_EVN_SEL_M0 (0x6 << 5)
43#define PMU_EVN_SEL_M1 (0x7 << 5)
44#define PMU_EVN_SEL_M2 (0x8 << 5)
45
46#define PMU_EVN_READ_ANY 0x0
47#define PMU_EVN_READ_SNOOP 0x3
48#define PMU_EVN_READ_HIT 0xA
49#define PMU_EVN_WRITE_ANY 0xC
50#define PMU_EVN_WU_SNOOP 0x10
51#define PMU_EVN_WLU_SNOOP 0x11
52
53#define PMU_0_SEL (PMU_EVN_SEL_S2 | PMU_EVN_READ_SNOOP)
54#define PMU_1_SEL (PMU_EVN_SEL_S2 | PMU_EVN_READ_HIT)
55#define PMU_2_SEL (PMU_EVN_SEL_S4 | PMU_EVN_READ_SNOOP)
56#define PMU_3_SEL (PMU_EVN_SEL_S4 | PMU_EVN_READ_HIT)
57
58#define MCSI_A_PMU_CTL_BASE MCSI_A_PMU_CTL
59#define MCSI_A_PMU_CNT0_BASE MCSI_A_PMU_EVN_SEL0
60#define MCSI_A_PMU_CNT1_BASE MCSI_A_PMU_EVN_SEL1
61#define MCSI_A_PMU_CNT2_BASE MCSI_A_PMU_EVN_SEL2
62#define MCSI_A_PMU_CNT3_BASE MCSI_A_PMU_EVN_SEL3
63
64typedef int (*IOC_SET_FUNC) (int par1, int par2, int par3);
65static struct proc_dir_entry *proc_hw_io_coherent;
66
67unsigned int reg_pmu_evn_phys[] = {
68 MCSI_A_PMU_CNT0_BASE,
69 MCSI_A_PMU_CNT1_BASE,
70 MCSI_A_PMU_CNT2_BASE,
71 MCSI_A_PMU_CNT3_BASE,
72};
73
74int ioc_pmu_cnt_config(int pmu_no, int interface, int event)
75{
76 void *reg_pmu_cnt;
77 unsigned int pmu_sel;
78
79 reg_pmu_cnt = ioremap(reg_pmu_evn_phys[pmu_no], 0x10);
80
81 /* Event Select Register
82 * bit[31:8] -> Reserved
83 * bit[7:5] -> Event code to define which interface to monitor
84 * bit[4:0] -> Event code to define which event to monitor
85 */
86 pmu_sel = (interface << 5) | event;
87 sys_reg_write(reg_pmu_cnt, pmu_sel);
88
89 /*Counter Control Registers
90 * bit[31:1] -> Reserved
91 * bit[0:0] -> Counter enable
92 */
93 sys_reg_write(reg_pmu_cnt + 0x8, 0x1);
94
95 iounmap(reg_pmu_cnt);
96
97 return 0;
98}
99
100int ioc_pmu_ctl_config(int enable, int ignore1, int ignore2)
101{
102 void *reg_pmu_ctl;
103
104 reg_pmu_ctl = ioremap(MCSI_A_PMU_CTL_BASE, 0x10);
105
106 /*Performance Monitor Control Register
107 * bit[31:16] -> Reserved
108 * bit[15:12] -> Specifies the number of counters implemented
109 * bit[11:6] -> Reserved
110 * bit[5:5] -> DP: Disables cycle counter
111 * bit[4:4] -> EX: Enable export of the events to the event bus
112 * bit[3:3] -> CCD: Cycle count divider
113 * bit[2:2] -> CCR: Cycle counter reset
114 * bit[1:1] -> RST: Performance counter reset
115 * bit[0:0] -> CEN: Enable bit
116 */
117 if (enable) {
118 sys_reg_write(reg_pmu_ctl, BIT(1));
119 sys_reg_write(reg_pmu_ctl, BIT(0));
120 } else {
121 sys_reg_write(reg_pmu_ctl, 0x0);
122 }
123
124 iounmap(reg_pmu_ctl);
125
126 return 0;
127}
128
129int ioc_set_usage(int ignore1, int ignore2, int ignore3)
130{
131 pr_info("<Usage> echo \"[OP Mode] [Arg1] [Arg2 | Arg3]\" > /proc/%s\n\r",
132 PROCREG_HW_IO_COHERENT);
133 pr_info("\tControl PMU counter: echo \"1 [Enable]\" > /proc/%s\n\r",
134 PROCREG_HW_IO_COHERENT);
135 pr_info("\t\t[Enable]:\n\r\t\t\t1: enable\n\r\t\t\t0: disable\n\r");
136 pr_info("\tConfigure PMU counter: echo \"2 [CNT No.] [IF] [EVN]\" > /proc/%s\n\r",
137 PROCREG_HW_IO_COHERENT);
138 pr_info("\t\t[CNT No.]: 0/1/2/3 PMU Counter\n\r");
139 pr_info("\t\t[IF]:\n\r");
140 pr_info("\t\t\t0: PMU_EVN_SEL_S0\n\r");
141 pr_info("\t\t\t1: PMU_EVN_SEL_S1\n\r");
142 pr_info("\t\t\t2: PMU_EVN_SEL_S2\n\r");
143 pr_info("\t\t\t3: PMU_EVN_SEL_S3\n\r");
144 pr_info("\t\t\t4: PMU_EVN_SEL_S4\n\r");
145 pr_info("\t\t\t5: PMU_EVN_SEL_S5\n\r");
146 pr_info("\t\t\t6: PMU_EVN_SEL_M0\n\r");
147 pr_info("\t\t\t7: PMU_EVN_SEL_M1\n\r");
148 pr_info("\t\t\t8: PMU_EVN_SEL_M2\n\r");
149 pr_info("\t\t[EVN]:\n\r");
150 pr_info("\t\t\t0: PMU_EVN_READ_ANY\n\r");
151 pr_info("\t\t\t3: PMU_EVN_READ_SNOOP\n\r");
152 pr_info("\t\t\tA: PMU_EVN_READ_HIT\n\r");
153 pr_info("\t\t\tC: PMU_EVN_WRITE_ANY\n\r");
154 pr_info("\t\t\t10: PMU_EVN_WU_SNOOP\n\r");
155 pr_info("\t\t\t11: PMU_EVN_WLU_SNOOP\n\r");
156
157 return 0;
158}
159
160static const IOC_SET_FUNC iocoherent_set_func[] = {
161 [0] = ioc_set_usage,
162 [1] = ioc_pmu_ctl_config,
163 [2] = ioc_pmu_cnt_config,
164};
165
166ssize_t ioc_pmu_write(struct file *file, const char __user *buffer,
167 size_t count, loff_t *data)
168{
169 char buf[32];
170 char *p_buf;
171 int len = count;
172 long arg0 = 0, arg1 = 0, arg2 = 0, arg3 = 0;
173 char *p_token = NULL;
174 char *p_delimiter = " \t";
175 int ret;
176
177 if (len >= sizeof(buf)) {
178 pr_err("input handling fail!\n");
179 len = sizeof(buf) - 1;
180 return -1;
181 }
182
183 if (copy_from_user(buf, buffer, len))
184 return -EFAULT;
185
186 buf[len] = '\0';
187 pr_info("write parameter data = %s\n\r", buf);
188
189 p_buf = buf;
190 p_token = strsep(&p_buf, p_delimiter);
191 if (!p_token)
192 arg0 = 0;
193 else
194 ret = kstrtol(p_token, 16, &arg0);
195
196 switch (arg0) {
197 case 1:
198 p_token = strsep(&p_buf, p_delimiter);
199 if (!p_token)
200 arg1 = 0;
201 else
202 ret = kstrtol(p_token, 16, &arg1);
203 break;
204 case 2:
205 p_token = strsep(&p_buf, p_delimiter);
206 if (!p_token)
207 arg1 = 0;
208 else
209 ret = kstrtol(p_token, 16, &arg1);
210 p_token = strsep(&p_buf, p_delimiter);
211 if (!p_token)
212 arg2 = 0;
213 else
214 ret = kstrtol(p_token, 16, &arg2);
215 p_token = strsep(&p_buf, p_delimiter);
216 if (!p_token)
217 arg3 = 0;
218 else
219 ret = kstrtol(p_token, 16, &arg3);
220 break;
221 }
222
223 if (iocoherent_set_func[arg0] &&
224 (ARRAY_SIZE(iocoherent_set_func) > arg0)) {
225 (*iocoherent_set_func[arg0]) (arg1, arg2, arg3);
226 } else {
227 pr_info("no handler defined for command id(0x%08lx)\n\r", arg0);
228 (*iocoherent_set_func[0]) (0, 0, 0);
229 }
230
231 return len;
232}
233
234int ioc_pmu_read(struct seq_file *seq, void *v)
235{
236 void __iomem *reg_virt_0, *reg_virt_1, *reg_virt_2, *reg_virt_3;
237
238 reg_virt_0 = ioremap(MCSI_A_PMU_EVN_SEL0, 0x10);
239 reg_virt_1 = ioremap(MCSI_A_PMU_EVN_SEL1, 0x10);
240 reg_virt_2 = ioremap(MCSI_A_PMU_EVN_SEL2, 0x10);
241 reg_virt_3 = ioremap(MCSI_A_PMU_EVN_SEL3, 0x10);
242
243 seq_printf(seq, "MCSI_A_PMU_EVN_SEL0 = 0x%x\n",
244 sys_reg_read(reg_virt_0));
245 seq_printf(seq, "MCSI_A_PMU_EVN_CNT0 = 0x%x\n",
246 sys_reg_read(reg_virt_0 + 0x4));
247 seq_printf(seq, "MCSI_A_PMU_EVN_CTL0 = 0x%x\n",
248 sys_reg_read(reg_virt_0 + 0x8));
249 seq_printf(seq, "MCSI_A_PMU_EVN_SEL1 = 0x%x\n",
250 sys_reg_read(reg_virt_1));
251 seq_printf(seq, "MCSI_A_PMU_EVN_CNT1 = 0x%x\n",
252 sys_reg_read(reg_virt_1 + 0x4));
253 seq_printf(seq, "MCSI_A_PMU_EVN_CTL1 = 0x%x\n",
254 sys_reg_read(reg_virt_1 + 0x8));
255
256 seq_printf(seq, "MCSI_A_PMU_EVN_SEL2 = 0x%x\n",
257 sys_reg_read(reg_virt_2));
258 seq_printf(seq, "MCSI_A_PMU_EVN_CNT2 = 0x%x\n",
259 sys_reg_read(reg_virt_2 + 0x4));
260 seq_printf(seq, "MCSI_A_PMU_EVN_CTL2 = 0x%x\n",
261 sys_reg_read(reg_virt_2 + 0x8));
262
263 seq_printf(seq, "MCSI_A_PMU_EVN_SEL3 = 0x%x\n",
264 sys_reg_read(reg_virt_3));
265 seq_printf(seq, "MCSI_A_PMU_EVN_CNT3 = 0x%x\n",
266 sys_reg_read(reg_virt_3 + 0x4));
267 seq_printf(seq, "MCSI_A_PMU_EVN_CTL3 = 0x%x\n",
268 sys_reg_read(reg_virt_3 + 0x8));
269
270 iounmap(reg_virt_0);
271 iounmap(reg_virt_1);
272 iounmap(reg_virt_2);
273 iounmap(reg_virt_3);
274 return 0;
275}
276
277static int ioc_pmu_open(struct inode *inode, struct file *file)
278{
279 return single_open(file, ioc_pmu_read, NULL);
280}
281
282static const struct file_operations ioc_pmu_fops = {
283 .owner = THIS_MODULE,
284 .open = ioc_pmu_open,
285 .read = seq_read,
286 .llseek = seq_lseek,
287 .write = ioc_pmu_write,
288 .release = single_release
289};
290
291void hwioc_debug_proc_init(struct proc_dir_entry *proc_reg_dir)
292{
293 proc_hw_io_coherent =
294 proc_create(PROCREG_HW_IO_COHERENT, 0, proc_reg_dir,
295 &ioc_pmu_fops);
296 if (!proc_hw_io_coherent)
297 pr_err("FAIL to create %s PROC!\n", PROCREG_HW_IO_COHERENT);
298}
299EXPORT_SYMBOL(hwioc_debug_proc_init);
300
301void hwioc_debug_proc_exit(struct proc_dir_entry *proc_reg_dir)
302{
303 if (proc_hw_io_coherent)
304 remove_proc_entry(PROCREG_HW_IO_COHERENT, proc_reg_dir);
305}
306EXPORT_SYMBOL(hwioc_debug_proc_exit);