xref: /openbmc/linux/arch/mips/cavium-octeon/oct_ilm.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
109c434b8SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20e49caf6SVenkat Subbiah #include <linux/fs.h>
30e49caf6SVenkat Subbiah #include <linux/interrupt.h>
40e49caf6SVenkat Subbiah #include <asm/octeon/octeon.h>
50e49caf6SVenkat Subbiah #include <asm/octeon/cvmx-ciu-defs.h>
60e49caf6SVenkat Subbiah #include <asm/octeon/cvmx.h>
70e49caf6SVenkat Subbiah #include <linux/debugfs.h>
80e49caf6SVenkat Subbiah #include <linux/kernel.h>
90e49caf6SVenkat Subbiah #include <linux/module.h>
100e49caf6SVenkat Subbiah #include <linux/seq_file.h>
110e49caf6SVenkat Subbiah 
120e49caf6SVenkat Subbiah #define TIMER_NUM 3
130e49caf6SVenkat Subbiah 
140e49caf6SVenkat Subbiah static bool reset_stats;
150e49caf6SVenkat Subbiah 
160e49caf6SVenkat Subbiah struct latency_info {
170e49caf6SVenkat Subbiah 	u64 io_interval;
180e49caf6SVenkat Subbiah 	u64 cpu_interval;
190e49caf6SVenkat Subbiah 	u64 timer_start1;
200e49caf6SVenkat Subbiah 	u64 timer_start2;
210e49caf6SVenkat Subbiah 	u64 max_latency;
220e49caf6SVenkat Subbiah 	u64 min_latency;
230e49caf6SVenkat Subbiah 	u64 latency_sum;
240e49caf6SVenkat Subbiah 	u64 average_latency;
250e49caf6SVenkat Subbiah 	u64 interrupt_cnt;
260e49caf6SVenkat Subbiah };
270e49caf6SVenkat Subbiah 
280e49caf6SVenkat Subbiah static struct latency_info li;
290e49caf6SVenkat Subbiah static struct dentry *dir;
300e49caf6SVenkat Subbiah 
oct_ilm_show(struct seq_file * m,void * v)31*6f674034SLiu Shixin static int oct_ilm_show(struct seq_file *m, void *v)
320e49caf6SVenkat Subbiah {
330e49caf6SVenkat Subbiah 	u64 cpuclk, avg, max, min;
340e49caf6SVenkat Subbiah 	struct latency_info curr_li = li;
350e49caf6SVenkat Subbiah 
360e49caf6SVenkat Subbiah 	cpuclk = octeon_get_clock_rate();
370e49caf6SVenkat Subbiah 
380e49caf6SVenkat Subbiah 	max = (curr_li.max_latency * 1000000000) / cpuclk;
390e49caf6SVenkat Subbiah 	min = (curr_li.min_latency * 1000000000) / cpuclk;
400e49caf6SVenkat Subbiah 	avg = (curr_li.latency_sum * 1000000000) / (cpuclk * curr_li.interrupt_cnt);
410e49caf6SVenkat Subbiah 
420e49caf6SVenkat Subbiah 	seq_printf(m, "cnt: %10lld, avg: %7lld ns, max: %7lld ns, min: %7lld ns\n",
430e49caf6SVenkat Subbiah 		   curr_li.interrupt_cnt, avg, max, min);
440e49caf6SVenkat Subbiah 	return 0;
450e49caf6SVenkat Subbiah }
46*6f674034SLiu Shixin DEFINE_SHOW_ATTRIBUTE(oct_ilm);
470e49caf6SVenkat Subbiah 
reset_statistics(void * data,u64 value)480e49caf6SVenkat Subbiah static int reset_statistics(void *data, u64 value)
490e49caf6SVenkat Subbiah {
500e49caf6SVenkat Subbiah 	reset_stats = true;
510e49caf6SVenkat Subbiah 	return 0;
520e49caf6SVenkat Subbiah }
530e49caf6SVenkat Subbiah 
541f4e5f03SYang Li DEFINE_DEBUGFS_ATTRIBUTE(reset_statistics_ops, NULL, reset_statistics, "%llu\n");
550e49caf6SVenkat Subbiah 
init_debugfs(void)569afbb713SGreg Kroah-Hartman static void init_debugfs(void)
570e49caf6SVenkat Subbiah {
580e49caf6SVenkat Subbiah 	dir = debugfs_create_dir("oct_ilm", 0);
59*6f674034SLiu Shixin 	debugfs_create_file("statistics", 0222, dir, NULL, &oct_ilm_fops);
609afbb713SGreg Kroah-Hartman 	debugfs_create_file("reset", 0222, dir, NULL, &reset_statistics_ops);
610e49caf6SVenkat Subbiah }
620e49caf6SVenkat Subbiah 
init_latency_info(struct latency_info * li,int startup)630e49caf6SVenkat Subbiah static void init_latency_info(struct latency_info *li, int startup)
640e49caf6SVenkat Subbiah {
650e49caf6SVenkat Subbiah 	/* interval in milli seconds after which the interrupt will
660e49caf6SVenkat Subbiah 	 * be triggered
670e49caf6SVenkat Subbiah 	 */
680e49caf6SVenkat Subbiah 	int interval = 1;
690e49caf6SVenkat Subbiah 
700e49caf6SVenkat Subbiah 	if (startup) {
710e49caf6SVenkat Subbiah 		/* Calculating by the amounts io clock and cpu clock would
720e49caf6SVenkat Subbiah 		 *  increment in interval amount of ms
730e49caf6SVenkat Subbiah 		 */
740e49caf6SVenkat Subbiah 		li->io_interval = (octeon_get_io_clock_rate() * interval) / 1000;
750e49caf6SVenkat Subbiah 		li->cpu_interval = (octeon_get_clock_rate() * interval) / 1000;
760e49caf6SVenkat Subbiah 	}
770e49caf6SVenkat Subbiah 	li->timer_start1 = 0;
780e49caf6SVenkat Subbiah 	li->timer_start2 = 0;
790e49caf6SVenkat Subbiah 	li->max_latency = 0;
800e49caf6SVenkat Subbiah 	li->min_latency = (u64)-1;
810e49caf6SVenkat Subbiah 	li->latency_sum = 0;
820e49caf6SVenkat Subbiah 	li->interrupt_cnt = 0;
830e49caf6SVenkat Subbiah }
840e49caf6SVenkat Subbiah 
850e49caf6SVenkat Subbiah 
start_timer(int timer,u64 interval)860e49caf6SVenkat Subbiah static void start_timer(int timer, u64 interval)
870e49caf6SVenkat Subbiah {
880e49caf6SVenkat Subbiah 	union cvmx_ciu_timx timx;
890e49caf6SVenkat Subbiah 	unsigned long flags;
900e49caf6SVenkat Subbiah 
910e49caf6SVenkat Subbiah 	timx.u64 = 0;
920e49caf6SVenkat Subbiah 	timx.s.one_shot = 1;
930e49caf6SVenkat Subbiah 	timx.s.len = interval;
940e49caf6SVenkat Subbiah 	raw_local_irq_save(flags);
950e49caf6SVenkat Subbiah 	li.timer_start1 = read_c0_cvmcount();
960e49caf6SVenkat Subbiah 	cvmx_write_csr(CVMX_CIU_TIMX(timer), timx.u64);
970e49caf6SVenkat Subbiah 	/* Read it back to force wait until register is written. */
980e49caf6SVenkat Subbiah 	timx.u64 = cvmx_read_csr(CVMX_CIU_TIMX(timer));
990e49caf6SVenkat Subbiah 	li.timer_start2 = read_c0_cvmcount();
1000e49caf6SVenkat Subbiah 	raw_local_irq_restore(flags);
1010e49caf6SVenkat Subbiah }
1020e49caf6SVenkat Subbiah 
1030e49caf6SVenkat Subbiah 
cvm_oct_ciu_timer_interrupt(int cpl,void * dev_id)1040e49caf6SVenkat Subbiah static irqreturn_t cvm_oct_ciu_timer_interrupt(int cpl, void *dev_id)
1050e49caf6SVenkat Subbiah {
1060e49caf6SVenkat Subbiah 	u64 last_latency;
1070e49caf6SVenkat Subbiah 	u64 last_int_cnt;
1080e49caf6SVenkat Subbiah 
1090e49caf6SVenkat Subbiah 	if (reset_stats) {
1100e49caf6SVenkat Subbiah 		init_latency_info(&li, 0);
1110e49caf6SVenkat Subbiah 		reset_stats = false;
1120e49caf6SVenkat Subbiah 	} else {
1130e49caf6SVenkat Subbiah 		last_int_cnt = read_c0_cvmcount();
1140e49caf6SVenkat Subbiah 		last_latency = last_int_cnt - (li.timer_start1 + li.cpu_interval);
1150e49caf6SVenkat Subbiah 		li.interrupt_cnt++;
1160e49caf6SVenkat Subbiah 		li.latency_sum += last_latency;
1170e49caf6SVenkat Subbiah 		if (last_latency > li.max_latency)
1180e49caf6SVenkat Subbiah 			li.max_latency = last_latency;
1190e49caf6SVenkat Subbiah 		if (last_latency < li.min_latency)
1200e49caf6SVenkat Subbiah 			li.min_latency = last_latency;
1210e49caf6SVenkat Subbiah 	}
1220e49caf6SVenkat Subbiah 	start_timer(TIMER_NUM, li.io_interval);
1230e49caf6SVenkat Subbiah 	return IRQ_HANDLED;
1240e49caf6SVenkat Subbiah }
1250e49caf6SVenkat Subbiah 
disable_timer(int timer)1260e49caf6SVenkat Subbiah static void disable_timer(int timer)
1270e49caf6SVenkat Subbiah {
1280e49caf6SVenkat Subbiah 	union cvmx_ciu_timx timx;
1290e49caf6SVenkat Subbiah 
1300e49caf6SVenkat Subbiah 	timx.s.one_shot = 0;
1310e49caf6SVenkat Subbiah 	timx.s.len = 0;
1320e49caf6SVenkat Subbiah 	cvmx_write_csr(CVMX_CIU_TIMX(timer), timx.u64);
1330e49caf6SVenkat Subbiah 	/* Read it back to force immediate write of timer register*/
1340e49caf6SVenkat Subbiah 	timx.u64 = cvmx_read_csr(CVMX_CIU_TIMX(timer));
1350e49caf6SVenkat Subbiah }
1360e49caf6SVenkat Subbiah 
oct_ilm_module_init(void)1370e49caf6SVenkat Subbiah static __init int oct_ilm_module_init(void)
1380e49caf6SVenkat Subbiah {
1390e49caf6SVenkat Subbiah 	int rc;
1400e49caf6SVenkat Subbiah 	int irq = OCTEON_IRQ_TIMER0 + TIMER_NUM;
1410e49caf6SVenkat Subbiah 
1429afbb713SGreg Kroah-Hartman 	init_debugfs();
1430e49caf6SVenkat Subbiah 
1440e49caf6SVenkat Subbiah 	rc = request_irq(irq, cvm_oct_ciu_timer_interrupt, IRQF_NO_THREAD,
1450e49caf6SVenkat Subbiah 			 "oct_ilm", 0);
1460e49caf6SVenkat Subbiah 	if (rc) {
1470e49caf6SVenkat Subbiah 		WARN(1, "Could not acquire IRQ %d", irq);
1480e49caf6SVenkat Subbiah 		goto err_irq;
1490e49caf6SVenkat Subbiah 	}
1500e49caf6SVenkat Subbiah 
1510e49caf6SVenkat Subbiah 	init_latency_info(&li, 1);
1520e49caf6SVenkat Subbiah 	start_timer(TIMER_NUM, li.io_interval);
1530e49caf6SVenkat Subbiah 
1540e49caf6SVenkat Subbiah 	return 0;
1550e49caf6SVenkat Subbiah err_irq:
1560e49caf6SVenkat Subbiah 	debugfs_remove_recursive(dir);
1570e49caf6SVenkat Subbiah 	return rc;
1580e49caf6SVenkat Subbiah }
1590e49caf6SVenkat Subbiah 
oct_ilm_module_exit(void)1600e49caf6SVenkat Subbiah static __exit void oct_ilm_module_exit(void)
1610e49caf6SVenkat Subbiah {
1620e49caf6SVenkat Subbiah 	disable_timer(TIMER_NUM);
1630e49caf6SVenkat Subbiah 	debugfs_remove_recursive(dir);
1640e49caf6SVenkat Subbiah 	free_irq(OCTEON_IRQ_TIMER0 + TIMER_NUM, 0);
1650e49caf6SVenkat Subbiah }
1660e49caf6SVenkat Subbiah 
1670e49caf6SVenkat Subbiah module_exit(oct_ilm_module_exit);
1680e49caf6SVenkat Subbiah module_init(oct_ilm_module_init);
1690e49caf6SVenkat Subbiah MODULE_AUTHOR("Venkat Subbiah, Cavium");
1700e49caf6SVenkat Subbiah MODULE_DESCRIPTION("Measures interrupt latency on Octeon chips.");
1710e49caf6SVenkat Subbiah MODULE_LICENSE("GPL");
172