18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2009 Simtec Electronics
48c2ecf20Sopenharmony_ci *	http://armlinux.simtec.co.uk/
58c2ecf20Sopenharmony_ci *	Ben Dooks <ben@simtec.co.uk>
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * S3C24XX CPU Frequency scaling - debugfs status support
88c2ecf20Sopenharmony_ci*/
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/init.h>
138c2ecf20Sopenharmony_ci#include <linux/export.h>
148c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
158c2ecf20Sopenharmony_ci#include <linux/ioport.h>
168c2ecf20Sopenharmony_ci#include <linux/cpufreq.h>
178c2ecf20Sopenharmony_ci#include <linux/debugfs.h>
188c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
198c2ecf20Sopenharmony_ci#include <linux/err.h>
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include <linux/soc/samsung/s3c-cpufreq-core.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_cistatic struct dentry *dbgfs_root;
248c2ecf20Sopenharmony_cistatic struct dentry *dbgfs_file_io;
258c2ecf20Sopenharmony_cistatic struct dentry *dbgfs_file_info;
268c2ecf20Sopenharmony_cistatic struct dentry *dbgfs_file_board;
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_ci#define print_ns(x) ((x) / 10), ((x) % 10)
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic void show_max(struct seq_file *seq, struct s3c_freq *f)
318c2ecf20Sopenharmony_ci{
328c2ecf20Sopenharmony_ci	seq_printf(seq, "MAX: F=%lu, H=%lu, P=%lu, A=%lu\n",
338c2ecf20Sopenharmony_ci		   f->fclk, f->hclk, f->pclk, f->armclk);
348c2ecf20Sopenharmony_ci}
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_cistatic int board_show(struct seq_file *seq, void *p)
378c2ecf20Sopenharmony_ci{
388c2ecf20Sopenharmony_ci	struct s3c_cpufreq_config *cfg;
398c2ecf20Sopenharmony_ci	struct s3c_cpufreq_board *brd;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	cfg = s3c_cpufreq_getconfig();
428c2ecf20Sopenharmony_ci	if (!cfg) {
438c2ecf20Sopenharmony_ci		seq_printf(seq, "no configuration registered\n");
448c2ecf20Sopenharmony_ci		return 0;
458c2ecf20Sopenharmony_ci	}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	brd = cfg->board;
488c2ecf20Sopenharmony_ci	if (!brd) {
498c2ecf20Sopenharmony_ci		seq_printf(seq, "no board definition set?\n");
508c2ecf20Sopenharmony_ci		return 0;
518c2ecf20Sopenharmony_ci	}
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	seq_printf(seq, "SDRAM refresh %u ns\n", brd->refresh);
548c2ecf20Sopenharmony_ci	seq_printf(seq, "auto_io=%u\n", brd->auto_io);
558c2ecf20Sopenharmony_ci	seq_printf(seq, "need_io=%u\n", brd->need_io);
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci	show_max(seq, &brd->max);
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	return 0;
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(board);
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_cistatic int info_show(struct seq_file *seq, void *p)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct s3c_cpufreq_config *cfg;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	cfg = s3c_cpufreq_getconfig();
708c2ecf20Sopenharmony_ci	if (!cfg) {
718c2ecf20Sopenharmony_ci		seq_printf(seq, "no configuration registered\n");
728c2ecf20Sopenharmony_ci		return 0;
738c2ecf20Sopenharmony_ci	}
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	seq_printf(seq, "  FCLK %ld Hz\n", cfg->freq.fclk);
768c2ecf20Sopenharmony_ci	seq_printf(seq, "  HCLK %ld Hz (%lu.%lu ns)\n",
778c2ecf20Sopenharmony_ci		   cfg->freq.hclk, print_ns(cfg->freq.hclk_tns));
788c2ecf20Sopenharmony_ci	seq_printf(seq, "  PCLK %ld Hz\n", cfg->freq.hclk);
798c2ecf20Sopenharmony_ci	seq_printf(seq, "ARMCLK %ld Hz\n", cfg->freq.armclk);
808c2ecf20Sopenharmony_ci	seq_printf(seq, "\n");
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	show_max(seq, &cfg->max);
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	seq_printf(seq, "Divisors: P=%d, H=%d, A=%d, dvs=%s\n",
858c2ecf20Sopenharmony_ci		   cfg->divs.h_divisor, cfg->divs.p_divisor,
868c2ecf20Sopenharmony_ci		   cfg->divs.arm_divisor, cfg->divs.dvs ? "on" : "off");
878c2ecf20Sopenharmony_ci	seq_printf(seq, "\n");
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	seq_printf(seq, "lock_pll=%u\n", cfg->lock_pll);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	return 0;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(info);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_cistatic int io_show(struct seq_file *seq, void *p)
978c2ecf20Sopenharmony_ci{
988c2ecf20Sopenharmony_ci	void (*show_bank)(struct seq_file *, struct s3c_cpufreq_config *, union s3c_iobank *);
998c2ecf20Sopenharmony_ci	struct s3c_cpufreq_config *cfg;
1008c2ecf20Sopenharmony_ci	struct s3c_iotimings *iot;
1018c2ecf20Sopenharmony_ci	union s3c_iobank *iob;
1028c2ecf20Sopenharmony_ci	int bank;
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	cfg = s3c_cpufreq_getconfig();
1058c2ecf20Sopenharmony_ci	if (!cfg) {
1068c2ecf20Sopenharmony_ci		seq_printf(seq, "no configuration registered\n");
1078c2ecf20Sopenharmony_ci		return 0;
1088c2ecf20Sopenharmony_ci	}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci	show_bank = cfg->info->debug_io_show;
1118c2ecf20Sopenharmony_ci	if (!show_bank) {
1128c2ecf20Sopenharmony_ci		seq_printf(seq, "no code to show bank timing\n");
1138c2ecf20Sopenharmony_ci		return 0;
1148c2ecf20Sopenharmony_ci	}
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	iot = s3c_cpufreq_getiotimings();
1178c2ecf20Sopenharmony_ci	if (!iot) {
1188c2ecf20Sopenharmony_ci		seq_printf(seq, "no io timings registered\n");
1198c2ecf20Sopenharmony_ci		return 0;
1208c2ecf20Sopenharmony_ci	}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	seq_printf(seq, "hclk period is %lu.%lu ns\n", print_ns(cfg->freq.hclk_tns));
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	for (bank = 0; bank < MAX_BANKS; bank++) {
1258c2ecf20Sopenharmony_ci		iob = &iot->bank[bank];
1268c2ecf20Sopenharmony_ci
1278c2ecf20Sopenharmony_ci		seq_printf(seq, "bank %d: ", bank);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci		if (!iob->io_2410) {
1308c2ecf20Sopenharmony_ci			seq_printf(seq, "nothing set\n");
1318c2ecf20Sopenharmony_ci			continue;
1328c2ecf20Sopenharmony_ci		}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci		show_bank(seq, cfg, iob);
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	return 0;
1388c2ecf20Sopenharmony_ci}
1398c2ecf20Sopenharmony_ci
1408c2ecf20Sopenharmony_ciDEFINE_SHOW_ATTRIBUTE(io);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_cistatic int __init s3c_freq_debugfs_init(void)
1438c2ecf20Sopenharmony_ci{
1448c2ecf20Sopenharmony_ci	dbgfs_root = debugfs_create_dir("s3c-cpufreq", NULL);
1458c2ecf20Sopenharmony_ci	if (IS_ERR(dbgfs_root)) {
1468c2ecf20Sopenharmony_ci		pr_err("%s: error creating debugfs root\n", __func__);
1478c2ecf20Sopenharmony_ci		return PTR_ERR(dbgfs_root);
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	dbgfs_file_io = debugfs_create_file("io-timing", S_IRUGO, dbgfs_root,
1518c2ecf20Sopenharmony_ci					    NULL, &io_fops);
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_ci	dbgfs_file_info = debugfs_create_file("info", S_IRUGO, dbgfs_root,
1548c2ecf20Sopenharmony_ci					      NULL, &info_fops);
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	dbgfs_file_board = debugfs_create_file("board", S_IRUGO, dbgfs_root,
1578c2ecf20Sopenharmony_ci					       NULL, &board_fops);
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	return 0;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cilate_initcall(s3c_freq_debugfs_init);
1638c2ecf20Sopenharmony_ci
164