ARM: S3C: CPUFREQ: Add debugfs support for cpufreq

Add debugfs support for the cpufreq driver to allow
information about the system state to be exported to
the user.

Signed-off-by: Ben Dooks <ben@simtec.co.uk>
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
This commit is contained in:
Ben Dooks 2009-07-30 23:23:42 +01:00 committed by Ben Dooks
parent dfff4e95d7
commit e6d197a695
10 changed files with 317 additions and 0 deletions

View file

@ -1329,6 +1329,12 @@ config CPU_FREQ_S3C24XX_IODEBUG
help
Enable s3c_freq_iodbg for the Samsung S3C CPUfreq core
config CPU_FREQ_S3C24XX_DEBUGFS
bool "Export debugfs for CPUFreq"
depends on CPU_FREQ_S3C24XX && DEBUG_FS
help
Export status information via debugfs.
endif
source "drivers/cpuidle/Kconfig"

View file

@ -111,6 +111,8 @@ static struct s3c_cpufreq_info s3c2410_cpufreq_info = {
.set_refresh = s3c2410_cpufreq_setrefresh,
.set_divs = s3c2410_cpufreq_setdivs,
.calc_divs = s3c2410_cpufreq_calcdivs,
.debug_io_show = s3c_cpufreq_debugfs_call(s3c2410_iotiming_debugfs),
};
static int s3c2410_cpufreq_add(struct sys_device *sysdev)

View file

@ -190,6 +190,8 @@ static struct s3c_cpufreq_info s3c2412_cpufreq_info = {
.get_iotiming = s3c2412_iotiming_get,
.resume_clocks = s3c2412_setup_clocks,
.debug_io_show = s3c_cpufreq_debugfs_call(s3c2412_iotiming_debugfs),
};
static int s3c2412_cpufreq_add(struct sys_device *sysdev)

View file

@ -21,6 +21,7 @@ obj-y += clock.o
obj-$(CONFIG_S3C24XX_DCLK) += clock-dclk.o
obj-$(CONFIG_CPU_FREQ_S3C24XX) += cpu-freq.o
obj-$(CONFIG_CPU_FREQ_S3C24XX_DEBUGFS) += cpu-freq-debugfs.o
# Architecture dependant builds

View file

@ -0,0 +1,199 @@
/* linux/arch/arm/plat-s3c24xx/cpu-freq-debugfs.c
*
* Copyright (c) 2009 Simtec Electronics
* http://armlinux.simtec.co.uk/
* Ben Dooks <ben@simtec.co.uk>
*
* S3C24XX CPU Frequency scaling - debugfs status support
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/cpufreq.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/err.h>
#include <plat/cpu-freq-core.h>
static struct dentry *dbgfs_root;
static struct dentry *dbgfs_file_io;
static struct dentry *dbgfs_file_info;
static struct dentry *dbgfs_file_board;
#define print_ns(x) ((x) / 10), ((x) % 10)
static void show_max(struct seq_file *seq, struct s3c_freq *f)
{
seq_printf(seq, "MAX: F=%lu, H=%lu, P=%lu, A=%lu\n",
f->fclk, f->hclk, f->pclk, f->armclk);
}
static int board_show(struct seq_file *seq, void *p)
{
struct s3c_cpufreq_config *cfg;
struct s3c_cpufreq_board *brd;
cfg = s3c_cpufreq_getconfig();
if (!cfg) {
seq_printf(seq, "no configuration registered\n");
return 0;
}
brd = cfg->board;
if (!brd) {
seq_printf(seq, "no board definition set?\n");
return 0;
}
seq_printf(seq, "SDRAM refresh %u ns\n", brd->refresh);
seq_printf(seq, "auto_io=%u\n", brd->auto_io);
seq_printf(seq, "need_io=%u\n", brd->need_io);
show_max(seq, &brd->max);
return 0;
}
static int fops_board_open(struct inode *inode, struct file *file)
{
return single_open(file, board_show, NULL);
}
static const struct file_operations fops_board = {
.open = fops_board_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int info_show(struct seq_file *seq, void *p)
{
struct s3c_cpufreq_config *cfg;
cfg = s3c_cpufreq_getconfig();
if (!cfg) {
seq_printf(seq, "no configuration registered\n");
return 0;
}
seq_printf(seq, " FCLK %ld Hz\n", cfg->freq.fclk);
seq_printf(seq, " HCLK %ld Hz (%lu.%lu ns)\n",
cfg->freq.hclk, print_ns(cfg->freq.hclk_tns));
seq_printf(seq, " PCLK %ld Hz\n", cfg->freq.hclk);
seq_printf(seq, "ARMCLK %ld Hz\n", cfg->freq.armclk);
seq_printf(seq, "\n");
show_max(seq, &cfg->max);
seq_printf(seq, "Divisors: P=%d, H=%d, A=%d, dvs=%s\n",
cfg->divs.h_divisor, cfg->divs.p_divisor,
cfg->divs.arm_divisor, cfg->divs.dvs ? "on" : "off");
seq_printf(seq, "\n");
seq_printf(seq, "lock_pll=%u\n", cfg->lock_pll);
return 0;
}
static int fops_info_open(struct inode *inode, struct file *file)
{
return single_open(file, info_show, NULL);
}
static const struct file_operations fops_info = {
.open = fops_info_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int io_show(struct seq_file *seq, void *p)
{
void (*show_bank)(struct seq_file *, struct s3c_cpufreq_config *, union s3c_iobank *);
struct s3c_cpufreq_config *cfg;
struct s3c_iotimings *iot;
union s3c_iobank *iob;
int bank;
cfg = s3c_cpufreq_getconfig();
if (!cfg) {
seq_printf(seq, "no configuration registered\n");
return 0;
}
show_bank = cfg->info->debug_io_show;
if (!show_bank) {
seq_printf(seq, "no code to show bank timing\n");
return 0;
}
iot = s3c_cpufreq_getiotimings();
if (!iot) {
seq_printf(seq, "no io timings registered\n");
return 0;
}
seq_printf(seq, "hclk period is %lu.%lu ns\n", print_ns(cfg->freq.hclk_tns));
for (bank = 0; bank < MAX_BANKS; bank++) {
iob = &iot->bank[bank];
seq_printf(seq, "bank %d: ", bank);
if (!iob->io_2410) {
seq_printf(seq, "nothing set\n");
continue;
}
show_bank(seq, cfg, iob);
}
return 0;
}
static int fops_io_open(struct inode *inode, struct file *file)
{
return single_open(file, io_show, NULL);
}
static const struct file_operations fops_io = {
.open = fops_io_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
.owner = THIS_MODULE,
};
static int __init s3c_freq_debugfs_init(void)
{
dbgfs_root = debugfs_create_dir("s3c-cpufreq", NULL);
if (IS_ERR(dbgfs_root)) {
printk(KERN_ERR "%s: error creating debugfs root\n", __func__);
return PTR_ERR(dbgfs_root);
}
dbgfs_file_io = debugfs_create_file("io-timing", S_IRUGO, dbgfs_root,
NULL, &fops_io);
dbgfs_file_info = debugfs_create_file("info", S_IRUGO, dbgfs_root,
NULL, &fops_info);
dbgfs_file_board = debugfs_create_file("board", S_IRUGO, dbgfs_root,
NULL, &fops_board);
return 0;
}
late_initcall(s3c_freq_debugfs_init);

View file

@ -50,6 +50,18 @@ static struct clk *clk_hclk;
static struct clk *clk_pclk;
static struct clk *clk_arm;
#ifdef CONFIG_CPU_FREQ_S3C24XX_DEBUGFS
struct s3c_cpufreq_config *s3c_cpufreq_getconfig(void)
{
return &cpu_cur;
}
struct s3c_iotimings *s3c_cpufreq_getiotimings(void)
{
return &s3c24xx_iotiming;
}
#endif /* CONFIG_CPU_FREQ_S3C24XX_DEBUGFS */
static void s3c_cpufreq_getcur(struct s3c_cpufreq_config *cfg)
{
unsigned long fclk, pclk, hclk, armclk;

View file

@ -13,6 +13,8 @@
#include <plat/cpu-freq.h>
struct seq_file;
#define MAX_BANKS (8)
#define S3C2412_MAX_IO (8)
@ -181,6 +183,10 @@ struct s3c_cpufreq_info {
struct cpufreq_frequency_table *t,
size_t table_size);
void (*debug_io_show)(struct seq_file *seq,
struct s3c_cpufreq_config *cfg,
union s3c_iobank *iob);
void (*set_refresh)(struct s3c_cpufreq_config *cfg);
void (*set_fvco)(struct s3c_cpufreq_config *cfg);
void (*set_divs)(struct s3c_cpufreq_config *cfg);
@ -191,6 +197,24 @@ extern int s3c_cpufreq_register(struct s3c_cpufreq_info *info);
extern int s3c_plltab_register(struct cpufreq_frequency_table *plls, unsigned int plls_no);
/* exports and utilities for debugfs */
extern struct s3c_cpufreq_config *s3c_cpufreq_getconfig(void);
extern struct s3c_iotimings *s3c_cpufreq_getiotimings(void);
extern void s3c2410_iotiming_debugfs(struct seq_file *seq,
struct s3c_cpufreq_config *cfg,
union s3c_iobank *iob);
extern void s3c2412_iotiming_debugfs(struct seq_file *seq,
struct s3c_cpufreq_config *cfg,
union s3c_iobank *iob);
#ifdef CONFIG_CPU_FREQ_S3C24XX_DEBUGFS
#define s3c_cpufreq_debugfs_call(x) x
#else
#define s3c_cpufreq_debugfs_call(x) NULL
#endif
/* Useful utility functions. */
extern struct clk *s3c_cpufreq_clk_get(struct device *, const char *);

View file

@ -15,6 +15,7 @@
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/cpufreq.h>
#include <linux/seq_file.h>
#include <linux/io.h>
#include <mach/map.h>
@ -303,6 +304,50 @@ void s3c2410_iotiming_getbank(struct s3c_cpufreq_config *cfg,
bt->tacc = get_tacc(hclk, bankcon >> S3C2410_BANKCON_Tacc_SHIFT);
}
/**
* s3c2410_iotiming_debugfs - debugfs show io bank timing information
* @seq: The seq_file to write output to using seq_printf().
* @cfg: The current configuration.
* @iob: The IO bank information to decode.
*/
void s3c2410_iotiming_debugfs(struct seq_file *seq,
struct s3c_cpufreq_config *cfg,
union s3c_iobank *iob)
{
struct s3c2410_iobank_timing *bt = iob->io_2410;
unsigned long bankcon = bt->bankcon;
unsigned long hclk = cfg->freq.hclk_tns;
unsigned int tacs;
unsigned int tcos;
unsigned int tacc;
unsigned int tcoh;
unsigned int tcah;
seq_printf(seq, "BANKCON=0x%08lx\n", bankcon);
tcah = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tcah_SHIFT);
tcoh = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tcoh_SHIFT);
tcos = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tcos_SHIFT);
tacs = get_0124(hclk, bankcon >> S3C2410_BANKCON_Tacs_SHIFT);
tacc = get_tacc(hclk, bankcon >> S3C2410_BANKCON_Tacc_SHIFT);
seq_printf(seq,
"\tRead: Tacs=%d.%d, Tcos=%d.%d, Tacc=%d.%d, Tcoh=%d.%d, Tcah=%d.%d\n",
print_ns(bt->tacs),
print_ns(bt->tcos),
print_ns(bt->tacc),
print_ns(bt->tcoh),
print_ns(bt->tcah));
seq_printf(seq,
"\t Set: Tacs=%d.%d, Tcos=%d.%d, Tacc=%d.%d, Tcoh=%d.%d, Tcah=%d.%d\n",
print_ns(tacs),
print_ns(tcos),
print_ns(tacc),
print_ns(tcoh),
print_ns(tcah));
}
/**
* s3c2410_iotiming_calc - Calculate bank timing for frequency change.
* @cfg: The frequency configuration

View file

@ -16,6 +16,7 @@
#include <linux/interrupt.h>
#include <linux/ioport.h>
#include <linux/cpufreq.h>
#include <linux/seq_file.h>
#include <linux/sysdev.h>
#include <linux/delay.h>
#include <linux/clk.h>
@ -108,6 +109,29 @@ static int s3c2412_calc_bank(struct s3c_cpufreq_config *cfg,
return err;
}
/**
* s3c2412_iotiming_debugfs - debugfs show io bank timing information
* @seq: The seq_file to write output to using seq_printf().
* @cfg: The current configuration.
* @iob: The IO bank information to decode.
*/
void s3c2412_iotiming_debugfs(struct seq_file *seq,
struct s3c_cpufreq_config *cfg,
union s3c_iobank *iob)
{
struct s3c2412_iobank_timing *bt = iob->io_2412;
seq_printf(seq,
"\tRead: idcy=%d.%d wstrd=%d.%d wstwr=%d,%d"
"wstoen=%d.%d wstwen=%d.%d wstbrd=%d.%d\n",
print_ns(bt->idcy),
print_ns(bt->wstrd),
print_ns(bt->wstwr),
print_ns(bt->wstoen),
print_ns(bt->wstwen),
print_ns(bt->wstbrd));
}
/**
* s3c2412_iotiming_calc - calculate all the bank divisor settings.
* @cfg: The current frequency configuration.

View file

@ -266,6 +266,8 @@ struct s3c_cpufreq_info s3c2440_cpufreq_info = {
.calc_freqtable = s3c2440_cpufreq_calctable,
.resume_clocks = s3c244x_setup_clocks,
.debug_io_show = s3c_cpufreq_debugfs_call(s3c2410_iotiming_debugfs),
};
static int s3c2440_cpufreq_add(struct sys_device *sysdev)