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