diff options
author | Ben Dooks <ben@simtec.co.uk> | 2009-10-01 18:44:17 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-10-01 19:11:15 -0400 |
commit | 9bdd203b4dc82e9047486f0fed1977eef8185c6d (patch) | |
tree | f547098e58f7d4f4d417a45d40d86614072db82c /drivers/mmc/host | |
parent | e6130aeffd93d342e72ca85cfd335d066f680792 (diff) |
s3cmci: add debugfs support for examining driver and hardware state
Export driver state and hardware register state via debugfs entries
created under a directory formed from dev_name() on the probed device when
CONFIG_DEBUG_FS is set.
Signed-off-by: Ben Dooks <ben@simtec.co.uk>
Cc: <linux-mmc@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/mmc/host')
-rw-r--r-- | drivers/mmc/host/s3cmci.c | 126 | ||||
-rw-r--r-- | drivers/mmc/host/s3cmci.h | 6 |
2 files changed, 132 insertions, 0 deletions
diff --git a/drivers/mmc/host/s3cmci.c b/drivers/mmc/host/s3cmci.c index 28a4a4535f38..8c2c8b456087 100644 --- a/drivers/mmc/host/s3cmci.c +++ b/drivers/mmc/host/s3cmci.c | |||
@@ -17,6 +17,8 @@ | |||
17 | #include <linux/mmc/host.h> | 17 | #include <linux/mmc/host.h> |
18 | #include <linux/platform_device.h> | 18 | #include <linux/platform_device.h> |
19 | #include <linux/cpufreq.h> | 19 | #include <linux/cpufreq.h> |
20 | #include <linux/debugfs.h> | ||
21 | #include <linux/seq_file.h> | ||
20 | #include <linux/gpio.h> | 22 | #include <linux/gpio.h> |
21 | #include <linux/irq.h> | 23 | #include <linux/irq.h> |
22 | #include <linux/io.h> | 24 | #include <linux/io.h> |
@@ -1244,6 +1246,127 @@ static inline void s3cmci_cpufreq_deregister(struct s3cmci_host *host) | |||
1244 | #endif | 1246 | #endif |
1245 | 1247 | ||
1246 | 1248 | ||
1249 | #ifdef CONFIG_DEBUG_FS | ||
1250 | |||
1251 | static int s3cmci_state_show(struct seq_file *seq, void *v) | ||
1252 | { | ||
1253 | struct s3cmci_host *host = seq->private; | ||
1254 | |||
1255 | seq_printf(seq, "Register base = 0x%08x\n", (u32)host->base); | ||
1256 | seq_printf(seq, "Clock rate = %ld\n", host->clk_rate); | ||
1257 | seq_printf(seq, "Prescale = %d\n", host->prescaler); | ||
1258 | seq_printf(seq, "is2440 = %d\n", host->is2440); | ||
1259 | seq_printf(seq, "IRQ = %d\n", host->irq); | ||
1260 | seq_printf(seq, "CD IRQ = %d\n", host->irq_cd); | ||
1261 | seq_printf(seq, "Do DMA = %d\n", host->dodma); | ||
1262 | seq_printf(seq, "SDIIMSK at %d\n", host->sdiimsk); | ||
1263 | seq_printf(seq, "SDIDATA at %d\n", host->sdidata); | ||
1264 | |||
1265 | return 0; | ||
1266 | } | ||
1267 | |||
1268 | static int s3cmci_state_open(struct inode *inode, struct file *file) | ||
1269 | { | ||
1270 | return single_open(file, s3cmci_state_show, inode->i_private); | ||
1271 | } | ||
1272 | |||
1273 | static const struct file_operations s3cmci_fops_state = { | ||
1274 | .owner = THIS_MODULE, | ||
1275 | .open = s3cmci_state_open, | ||
1276 | .read = seq_read, | ||
1277 | .llseek = seq_lseek, | ||
1278 | .release = single_release, | ||
1279 | }; | ||
1280 | |||
1281 | #define DBG_REG(_r) { .addr = S3C2410_SDI##_r, .name = #_r } | ||
1282 | |||
1283 | struct s3cmci_reg { | ||
1284 | unsigned short addr; | ||
1285 | unsigned char *name; | ||
1286 | } debug_regs[] = { | ||
1287 | DBG_REG(CON), | ||
1288 | DBG_REG(PRE), | ||
1289 | DBG_REG(CMDARG), | ||
1290 | DBG_REG(CMDCON), | ||
1291 | DBG_REG(CMDSTAT), | ||
1292 | DBG_REG(RSP0), | ||
1293 | DBG_REG(RSP1), | ||
1294 | DBG_REG(RSP2), | ||
1295 | DBG_REG(RSP3), | ||
1296 | DBG_REG(TIMER), | ||
1297 | DBG_REG(BSIZE), | ||
1298 | DBG_REG(DCON), | ||
1299 | DBG_REG(DCNT), | ||
1300 | DBG_REG(DSTA), | ||
1301 | DBG_REG(FSTA), | ||
1302 | {} | ||
1303 | }; | ||
1304 | |||
1305 | static int s3cmci_regs_show(struct seq_file *seq, void *v) | ||
1306 | { | ||
1307 | struct s3cmci_host *host = seq->private; | ||
1308 | struct s3cmci_reg *rptr = debug_regs; | ||
1309 | |||
1310 | for (; rptr->name; rptr++) | ||
1311 | seq_printf(seq, "SDI%s\t=0x%08x\n", rptr->name, | ||
1312 | readl(host->base + rptr->addr)); | ||
1313 | |||
1314 | seq_printf(seq, "SDIIMSK\t=0x%08x\n", readl(host->base + host->sdiimsk)); | ||
1315 | |||
1316 | return 0; | ||
1317 | } | ||
1318 | |||
1319 | static int s3cmci_regs_open(struct inode *inode, struct file *file) | ||
1320 | { | ||
1321 | return single_open(file, s3cmci_regs_show, inode->i_private); | ||
1322 | } | ||
1323 | |||
1324 | static const struct file_operations s3cmci_fops_regs = { | ||
1325 | .owner = THIS_MODULE, | ||
1326 | .open = s3cmci_regs_open, | ||
1327 | .read = seq_read, | ||
1328 | .llseek = seq_lseek, | ||
1329 | .release = single_release, | ||
1330 | }; | ||
1331 | |||
1332 | static void s3cmci_debugfs_attach(struct s3cmci_host *host) | ||
1333 | { | ||
1334 | struct device *dev = &host->pdev->dev; | ||
1335 | |||
1336 | host->debug_root = debugfs_create_dir(dev_name(dev), NULL); | ||
1337 | if (IS_ERR(host->debug_root)) { | ||
1338 | dev_err(dev, "failed to create debugfs root\n"); | ||
1339 | return; | ||
1340 | } | ||
1341 | |||
1342 | host->debug_state = debugfs_create_file("state", 0444, | ||
1343 | host->debug_root, host, | ||
1344 | &s3cmci_fops_state); | ||
1345 | |||
1346 | if (IS_ERR(host->debug_state)) | ||
1347 | dev_err(dev, "failed to create debug state file\n"); | ||
1348 | |||
1349 | host->debug_regs = debugfs_create_file("regs", 0444, | ||
1350 | host->debug_root, host, | ||
1351 | &s3cmci_fops_regs); | ||
1352 | |||
1353 | if (IS_ERR(host->debug_regs)) | ||
1354 | dev_err(dev, "failed to create debug regs file\n"); | ||
1355 | } | ||
1356 | |||
1357 | static void s3cmci_debugfs_remove(struct s3cmci_host *host) | ||
1358 | { | ||
1359 | debugfs_remove(host->debug_regs); | ||
1360 | debugfs_remove(host->debug_state); | ||
1361 | debugfs_remove(host->debug_root); | ||
1362 | } | ||
1363 | |||
1364 | #else | ||
1365 | static inline void s3cmci_debugfs_attach(struct s3cmci_host *host) { } | ||
1366 | static inline void s3cmci_debugfs_remove(struct s3cmci_host *host) { } | ||
1367 | |||
1368 | #endif /* CONFIG_DEBUG_FS */ | ||
1369 | |||
1247 | static int __devinit s3cmci_probe(struct platform_device *pdev) | 1370 | static int __devinit s3cmci_probe(struct platform_device *pdev) |
1248 | { | 1371 | { |
1249 | struct s3cmci_host *host; | 1372 | struct s3cmci_host *host; |
@@ -1435,6 +1558,8 @@ static int __devinit s3cmci_probe(struct platform_device *pdev) | |||
1435 | goto free_cpufreq; | 1558 | goto free_cpufreq; |
1436 | } | 1559 | } |
1437 | 1560 | ||
1561 | s3cmci_debugfs_attach(host); | ||
1562 | |||
1438 | platform_set_drvdata(pdev, mmc); | 1563 | platform_set_drvdata(pdev, mmc); |
1439 | dev_info(&pdev->dev, "initialisation done.\n"); | 1564 | dev_info(&pdev->dev, "initialisation done.\n"); |
1440 | 1565 | ||
@@ -1489,6 +1614,7 @@ static void s3cmci_shutdown(struct platform_device *pdev) | |||
1489 | if (host->irq_cd >= 0) | 1614 | if (host->irq_cd >= 0) |
1490 | free_irq(host->irq_cd, host); | 1615 | free_irq(host->irq_cd, host); |
1491 | 1616 | ||
1617 | s3cmci_debugfs_remove(host); | ||
1492 | s3cmci_cpufreq_deregister(host); | 1618 | s3cmci_cpufreq_deregister(host); |
1493 | mmc_remove_host(mmc); | 1619 | mmc_remove_host(mmc); |
1494 | clk_disable(host->clk); | 1620 | clk_disable(host->clk); |
diff --git a/drivers/mmc/host/s3cmci.h b/drivers/mmc/host/s3cmci.h index ca1ba3d58cfd..fc96194a06f4 100644 --- a/drivers/mmc/host/s3cmci.h +++ b/drivers/mmc/host/s3cmci.h | |||
@@ -68,6 +68,12 @@ struct s3cmci_host { | |||
68 | unsigned int ccnt, dcnt; | 68 | unsigned int ccnt, dcnt; |
69 | struct tasklet_struct pio_tasklet; | 69 | struct tasklet_struct pio_tasklet; |
70 | 70 | ||
71 | #ifdef CONFIG_DEBUG_FS | ||
72 | struct dentry *debug_root; | ||
73 | struct dentry *debug_state; | ||
74 | struct dentry *debug_regs; | ||
75 | #endif | ||
76 | |||
71 | #ifdef CONFIG_CPU_FREQ | 77 | #ifdef CONFIG_CPU_FREQ |
72 | struct notifier_block freq_transition; | 78 | struct notifier_block freq_transition; |
73 | #endif | 79 | #endif |