aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHaavard Skinnemoen <haavard.skinnemoen@atmel.com>2008-07-24 08:18:59 -0400
committerPierre Ossman <drzeus@drzeus.cx>2008-07-26 19:26:17 -0400
commitdeec9ae31e6079551ce9260d29a4cf83e5b19a83 (patch)
tree271a323032fb4a9db9c3a8d3c73d1c922c5f31d9
parentf4b7f927b531ca350cfc4ca1bdc3377dac7f9a32 (diff)
atmel-mci: debugfs support
Create additional files under the host's debugfs directory containing additional host-specific debug information. Signed-off-by: Haavard Skinnemoen <haavard.skinnemoen@atmel.com> Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
-rw-r--r--drivers/mmc/host/atmel-mci-regs.h2
-rw-r--r--drivers/mmc/host/atmel-mci.c189
2 files changed, 191 insertions, 0 deletions
diff --git a/drivers/mmc/host/atmel-mci-regs.h b/drivers/mmc/host/atmel-mci-regs.h
index a9a5657706c6..26bd80e65031 100644
--- a/drivers/mmc/host/atmel-mci-regs.h
+++ b/drivers/mmc/host/atmel-mci-regs.h
@@ -82,6 +82,8 @@
82# define MCI_OVRE ( 1 << 30) /* RX Overrun Error */ 82# define MCI_OVRE ( 1 << 30) /* RX Overrun Error */
83# define MCI_UNRE ( 1 << 31) /* TX Underrun Error */ 83# define MCI_UNRE ( 1 << 31) /* TX Underrun Error */
84 84
85#define MCI_REGS_SIZE 0x100
86
85/* Register access macros */ 87/* Register access macros */
86#define mci_readl(port,reg) \ 88#define mci_readl(port,reg) \
87 __raw_readl((port)->regs + MCI_##reg) 89 __raw_readl((port)->regs + MCI_##reg)
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index cce873c5a149..b68381f7bfdd 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -9,6 +9,7 @@
9 */ 9 */
10#include <linux/blkdev.h> 10#include <linux/blkdev.h>
11#include <linux/clk.h> 11#include <linux/clk.h>
12#include <linux/debugfs.h>
12#include <linux/device.h> 13#include <linux/device.h>
13#include <linux/init.h> 14#include <linux/init.h>
14#include <linux/interrupt.h> 15#include <linux/interrupt.h>
@@ -16,6 +17,8 @@
16#include <linux/module.h> 17#include <linux/module.h>
17#include <linux/platform_device.h> 18#include <linux/platform_device.h>
18#include <linux/scatterlist.h> 19#include <linux/scatterlist.h>
20#include <linux/seq_file.h>
21#include <linux/stat.h>
19 22
20#include <linux/mmc/host.h> 23#include <linux/mmc/host.h>
21 24
@@ -88,6 +91,188 @@ struct atmel_mci {
88#define atmci_clear_pending(host, event) \ 91#define atmci_clear_pending(host, event) \
89 clear_bit(event, &host->pending_events) 92 clear_bit(event, &host->pending_events)
90 93
94/*
95 * The debugfs stuff below is mostly optimized away when
96 * CONFIG_DEBUG_FS is not set.
97 */
98static int atmci_req_show(struct seq_file *s, void *v)
99{
100 struct atmel_mci *host = s->private;
101 struct mmc_request *mrq = host->mrq;
102 struct mmc_command *cmd;
103 struct mmc_command *stop;
104 struct mmc_data *data;
105
106 /* Make sure we get a consistent snapshot */
107 spin_lock_irq(&host->mmc->lock);
108
109 if (mrq) {
110 cmd = mrq->cmd;
111 data = mrq->data;
112 stop = mrq->stop;
113
114 if (cmd)
115 seq_printf(s,
116 "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
117 cmd->opcode, cmd->arg, cmd->flags,
118 cmd->resp[0], cmd->resp[1], cmd->resp[2],
119 cmd->resp[2], cmd->error);
120 if (data)
121 seq_printf(s, "DATA %u / %u * %u flg %x err %d\n",
122 data->bytes_xfered, data->blocks,
123 data->blksz, data->flags, data->error);
124 if (stop)
125 seq_printf(s,
126 "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
127 stop->opcode, stop->arg, stop->flags,
128 stop->resp[0], stop->resp[1], stop->resp[2],
129 stop->resp[2], stop->error);
130 }
131
132 spin_unlock_irq(&host->mmc->lock);
133
134 return 0;
135}
136
137static int atmci_req_open(struct inode *inode, struct file *file)
138{
139 return single_open(file, atmci_req_show, inode->i_private);
140}
141
142static const struct file_operations atmci_req_fops = {
143 .owner = THIS_MODULE,
144 .open = atmci_req_open,
145 .read = seq_read,
146 .llseek = seq_lseek,
147 .release = single_release,
148};
149
150static void atmci_show_status_reg(struct seq_file *s,
151 const char *regname, u32 value)
152{
153 static const char *sr_bit[] = {
154 [0] = "CMDRDY",
155 [1] = "RXRDY",
156 [2] = "TXRDY",
157 [3] = "BLKE",
158 [4] = "DTIP",
159 [5] = "NOTBUSY",
160 [8] = "SDIOIRQA",
161 [9] = "SDIOIRQB",
162 [16] = "RINDE",
163 [17] = "RDIRE",
164 [18] = "RCRCE",
165 [19] = "RENDE",
166 [20] = "RTOE",
167 [21] = "DCRCE",
168 [22] = "DTOE",
169 [30] = "OVRE",
170 [31] = "UNRE",
171 };
172 unsigned int i;
173
174 seq_printf(s, "%s:\t0x%08x", regname, value);
175 for (i = 0; i < ARRAY_SIZE(sr_bit); i++) {
176 if (value & (1 << i)) {
177 if (sr_bit[i])
178 seq_printf(s, " %s", sr_bit[i]);
179 else
180 seq_puts(s, " UNKNOWN");
181 }
182 }
183 seq_putc(s, '\n');
184}
185
186static int atmci_regs_show(struct seq_file *s, void *v)
187{
188 struct atmel_mci *host = s->private;
189 u32 *buf;
190
191 buf = kmalloc(MCI_REGS_SIZE, GFP_KERNEL);
192 if (!buf)
193 return -ENOMEM;
194
195 /* Grab a more or less consistent snapshot */
196 spin_lock_irq(&host->mmc->lock);
197 memcpy_fromio(buf, host->regs, MCI_REGS_SIZE);
198 spin_unlock_irq(&host->mmc->lock);
199
200 seq_printf(s, "MR:\t0x%08x%s%s CLKDIV=%u\n",
201 buf[MCI_MR / 4],
202 buf[MCI_MR / 4] & MCI_MR_RDPROOF ? " RDPROOF" : "",
203 buf[MCI_MR / 4] & MCI_MR_WRPROOF ? " WRPROOF" : "",
204 buf[MCI_MR / 4] & 0xff);
205 seq_printf(s, "DTOR:\t0x%08x\n", buf[MCI_DTOR / 4]);
206 seq_printf(s, "SDCR:\t0x%08x\n", buf[MCI_SDCR / 4]);
207 seq_printf(s, "ARGR:\t0x%08x\n", buf[MCI_ARGR / 4]);
208 seq_printf(s, "BLKR:\t0x%08x BCNT=%u BLKLEN=%u\n",
209 buf[MCI_BLKR / 4],
210 buf[MCI_BLKR / 4] & 0xffff,
211 (buf[MCI_BLKR / 4] >> 16) & 0xffff);
212
213 /* Don't read RSPR and RDR; it will consume the data there */
214
215 atmci_show_status_reg(s, "SR", buf[MCI_SR / 4]);
216 atmci_show_status_reg(s, "IMR", buf[MCI_IMR / 4]);
217
218 return 0;
219}
220
221static int atmci_regs_open(struct inode *inode, struct file *file)
222{
223 return single_open(file, atmci_regs_show, inode->i_private);
224}
225
226static const struct file_operations atmci_regs_fops = {
227 .owner = THIS_MODULE,
228 .open = atmci_regs_open,
229 .read = seq_read,
230 .llseek = seq_lseek,
231 .release = single_release,
232};
233
234static void atmci_init_debugfs(struct atmel_mci *host)
235{
236 struct mmc_host *mmc;
237 struct dentry *root;
238 struct dentry *node;
239 struct resource *res;
240
241 mmc = host->mmc;
242 root = mmc->debugfs_root;
243 if (!root)
244 return;
245
246 node = debugfs_create_file("regs", S_IRUSR, root, host,
247 &atmci_regs_fops);
248 if (IS_ERR(node))
249 return;
250 if (!node)
251 goto err;
252
253 res = platform_get_resource(host->pdev, IORESOURCE_MEM, 0);
254 node->d_inode->i_size = res->end - res->start + 1;
255
256 node = debugfs_create_file("req", S_IRUSR, root, host, &atmci_req_fops);
257 if (!node)
258 goto err;
259
260 node = debugfs_create_x32("pending_events", S_IRUSR, root,
261 (u32 *)&host->pending_events);
262 if (!node)
263 goto err;
264
265 node = debugfs_create_x32("completed_events", S_IRUSR, root,
266 (u32 *)&host->completed_events);
267 if (!node)
268 goto err;
269
270 return;
271
272err:
273 dev_err(&host->pdev->dev,
274 "failed to initialize debugfs for controller\n");
275}
91 276
92static void atmci_enable(struct atmel_mci *host) 277static void atmci_enable(struct atmel_mci *host)
93{ 278{
@@ -905,6 +1090,8 @@ static int __init atmci_probe(struct platform_device *pdev)
905 "Atmel MCI controller at 0x%08lx irq %d\n", 1090 "Atmel MCI controller at 0x%08lx irq %d\n",
906 host->mapbase, irq); 1091 host->mapbase, irq);
907 1092
1093 atmci_init_debugfs(host);
1094
908 return 0; 1095 return 0;
909 1096
910err_request_irq: 1097err_request_irq:
@@ -923,6 +1110,8 @@ static int __exit atmci_remove(struct platform_device *pdev)
923 platform_set_drvdata(pdev, NULL); 1110 platform_set_drvdata(pdev, NULL);
924 1111
925 if (host) { 1112 if (host) {
1113 /* Debugfs stuff is cleaned up by mmc core */
1114
926 if (host->detect_pin >= 0) { 1115 if (host->detect_pin >= 0) {
927 int pin = host->detect_pin; 1116 int pin = host->detect_pin;
928 1117