aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mmc/host/atmel-mci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mmc/host/atmel-mci.c')
-rw-r--r--drivers/mmc/host/atmel-mci.c206
1 files changed, 198 insertions, 8 deletions
diff --git a/drivers/mmc/host/atmel-mci.c b/drivers/mmc/host/atmel-mci.c
index cce873c5a149..992b4beb757c 100644
--- a/drivers/mmc/host/atmel-mci.c
+++ b/drivers/mmc/host/atmel-mci.c
@@ -9,13 +9,18 @@
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>
14#include <linux/err.h>
15#include <linux/gpio.h>
13#include <linux/init.h> 16#include <linux/init.h>
14#include <linux/interrupt.h> 17#include <linux/interrupt.h>
15#include <linux/ioport.h> 18#include <linux/ioport.h>
16#include <linux/module.h> 19#include <linux/module.h>
17#include <linux/platform_device.h> 20#include <linux/platform_device.h>
18#include <linux/scatterlist.h> 21#include <linux/scatterlist.h>
22#include <linux/seq_file.h>
23#include <linux/stat.h>
19 24
20#include <linux/mmc/host.h> 25#include <linux/mmc/host.h>
21 26
@@ -24,7 +29,6 @@
24#include <asm/unaligned.h> 29#include <asm/unaligned.h>
25 30
26#include <asm/arch/board.h> 31#include <asm/arch/board.h>
27#include <asm/arch/gpio.h>
28 32
29#include "atmel-mci-regs.h" 33#include "atmel-mci-regs.h"
30 34
@@ -88,6 +92,188 @@ struct atmel_mci {
88#define atmci_clear_pending(host, event) \ 92#define atmci_clear_pending(host, event) \
89 clear_bit(event, &host->pending_events) 93 clear_bit(event, &host->pending_events)
90 94
95/*
96 * The debugfs stuff below is mostly optimized away when
97 * CONFIG_DEBUG_FS is not set.
98 */
99static int atmci_req_show(struct seq_file *s, void *v)
100{
101 struct atmel_mci *host = s->private;
102 struct mmc_request *mrq = host->mrq;
103 struct mmc_command *cmd;
104 struct mmc_command *stop;
105 struct mmc_data *data;
106
107 /* Make sure we get a consistent snapshot */
108 spin_lock_irq(&host->mmc->lock);
109
110 if (mrq) {
111 cmd = mrq->cmd;
112 data = mrq->data;
113 stop = mrq->stop;
114
115 if (cmd)
116 seq_printf(s,
117 "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
118 cmd->opcode, cmd->arg, cmd->flags,
119 cmd->resp[0], cmd->resp[1], cmd->resp[2],
120 cmd->resp[2], cmd->error);
121 if (data)
122 seq_printf(s, "DATA %u / %u * %u flg %x err %d\n",
123 data->bytes_xfered, data->blocks,
124 data->blksz, data->flags, data->error);
125 if (stop)
126 seq_printf(s,
127 "CMD%u(0x%x) flg %x rsp %x %x %x %x err %d\n",
128 stop->opcode, stop->arg, stop->flags,
129 stop->resp[0], stop->resp[1], stop->resp[2],
130 stop->resp[2], stop->error);
131 }
132
133 spin_unlock_irq(&host->mmc->lock);
134
135 return 0;
136}
137
138static int atmci_req_open(struct inode *inode, struct file *file)
139{
140 return single_open(file, atmci_req_show, inode->i_private);
141}
142
143static const struct file_operations atmci_req_fops = {
144 .owner = THIS_MODULE,
145 .open = atmci_req_open,
146 .read = seq_read,
147 .llseek = seq_lseek,
148 .release = single_release,
149};
150
151static void atmci_show_status_reg(struct seq_file *s,
152 const char *regname, u32 value)
153{
154 static const char *sr_bit[] = {
155 [0] = "CMDRDY",
156 [1] = "RXRDY",
157 [2] = "TXRDY",
158 [3] = "BLKE",
159 [4] = "DTIP",
160 [5] = "NOTBUSY",
161 [8] = "SDIOIRQA",
162 [9] = "SDIOIRQB",
163 [16] = "RINDE",
164 [17] = "RDIRE",
165 [18] = "RCRCE",
166 [19] = "RENDE",
167 [20] = "RTOE",
168 [21] = "DCRCE",
169 [22] = "DTOE",
170 [30] = "OVRE",
171 [31] = "UNRE",
172 };
173 unsigned int i;
174
175 seq_printf(s, "%s:\t0x%08x", regname, value);
176 for (i = 0; i < ARRAY_SIZE(sr_bit); i++) {
177 if (value & (1 << i)) {
178 if (sr_bit[i])
179 seq_printf(s, " %s", sr_bit[i]);
180 else
181 seq_puts(s, " UNKNOWN");
182 }
183 }
184 seq_putc(s, '\n');
185}
186
187static int atmci_regs_show(struct seq_file *s, void *v)
188{
189 struct atmel_mci *host = s->private;
190 u32 *buf;
191
192 buf = kmalloc(MCI_REGS_SIZE, GFP_KERNEL);
193 if (!buf)
194 return -ENOMEM;
195
196 /* Grab a more or less consistent snapshot */
197 spin_lock_irq(&host->mmc->lock);
198 memcpy_fromio(buf, host->regs, MCI_REGS_SIZE);
199 spin_unlock_irq(&host->mmc->lock);
200
201 seq_printf(s, "MR:\t0x%08x%s%s CLKDIV=%u\n",
202 buf[MCI_MR / 4],
203 buf[MCI_MR / 4] & MCI_MR_RDPROOF ? " RDPROOF" : "",
204 buf[MCI_MR / 4] & MCI_MR_WRPROOF ? " WRPROOF" : "",
205 buf[MCI_MR / 4] & 0xff);
206 seq_printf(s, "DTOR:\t0x%08x\n", buf[MCI_DTOR / 4]);
207 seq_printf(s, "SDCR:\t0x%08x\n", buf[MCI_SDCR / 4]);
208 seq_printf(s, "ARGR:\t0x%08x\n", buf[MCI_ARGR / 4]);
209 seq_printf(s, "BLKR:\t0x%08x BCNT=%u BLKLEN=%u\n",
210 buf[MCI_BLKR / 4],
211 buf[MCI_BLKR / 4] & 0xffff,
212 (buf[MCI_BLKR / 4] >> 16) & 0xffff);
213
214 /* Don't read RSPR and RDR; it will consume the data there */
215
216 atmci_show_status_reg(s, "SR", buf[MCI_SR / 4]);
217 atmci_show_status_reg(s, "IMR", buf[MCI_IMR / 4]);
218
219 return 0;
220}
221
222static int atmci_regs_open(struct inode *inode, struct file *file)
223{
224 return single_open(file, atmci_regs_show, inode->i_private);
225}
226
227static const struct file_operations atmci_regs_fops = {
228 .owner = THIS_MODULE,
229 .open = atmci_regs_open,
230 .read = seq_read,
231 .llseek = seq_lseek,
232 .release = single_release,
233};
234
235static void atmci_init_debugfs(struct atmel_mci *host)
236{
237 struct mmc_host *mmc;
238 struct dentry *root;
239 struct dentry *node;
240 struct resource *res;
241
242 mmc = host->mmc;
243 root = mmc->debugfs_root;
244 if (!root)
245 return;
246
247 node = debugfs_create_file("regs", S_IRUSR, root, host,
248 &atmci_regs_fops);
249 if (IS_ERR(node))
250 return;
251 if (!node)
252 goto err;
253
254 res = platform_get_resource(host->pdev, IORESOURCE_MEM, 0);
255 node->d_inode->i_size = res->end - res->start + 1;
256
257 node = debugfs_create_file("req", S_IRUSR, root, host, &atmci_req_fops);
258 if (!node)
259 goto err;
260
261 node = debugfs_create_x32("pending_events", S_IRUSR, root,
262 (u32 *)&host->pending_events);
263 if (!node)
264 goto err;
265
266 node = debugfs_create_x32("completed_events", S_IRUSR, root,
267 (u32 *)&host->completed_events);
268 if (!node)
269 goto err;
270
271 return;
272
273err:
274 dev_err(&host->pdev->dev,
275 "failed to initialize debugfs for controller\n");
276}
91 277
92static void atmci_enable(struct atmel_mci *host) 278static void atmci_enable(struct atmel_mci *host)
93{ 279{
@@ -388,7 +574,7 @@ static int atmci_get_ro(struct mmc_host *mmc)
388 int read_only = 0; 574 int read_only = 0;
389 struct atmel_mci *host = mmc_priv(mmc); 575 struct atmel_mci *host = mmc_priv(mmc);
390 576
391 if (host->wp_pin >= 0) { 577 if (gpio_is_valid(host->wp_pin)) {
392 read_only = gpio_get_value(host->wp_pin); 578 read_only = gpio_get_value(host->wp_pin);
393 dev_dbg(&mmc->class_dev, "card is %s\n", 579 dev_dbg(&mmc->class_dev, "card is %s\n",
394 read_only ? "read-only" : "read-write"); 580 read_only ? "read-only" : "read-write");
@@ -450,7 +636,7 @@ static void atmci_detect_change(unsigned long data)
450 * been freed. 636 * been freed.
451 */ 637 */
452 smp_rmb(); 638 smp_rmb();
453 if (host->detect_pin < 0) 639 if (!gpio_is_valid(host->detect_pin))
454 return; 640 return;
455 641
456 enable_irq(gpio_to_irq(host->detect_pin)); 642 enable_irq(gpio_to_irq(host->detect_pin));
@@ -865,7 +1051,7 @@ static int __init atmci_probe(struct platform_device *pdev)
865 1051
866 /* Assume card is present if we don't have a detect pin */ 1052 /* Assume card is present if we don't have a detect pin */
867 host->present = 1; 1053 host->present = 1;
868 if (host->detect_pin >= 0) { 1054 if (gpio_is_valid(host->detect_pin)) {
869 if (gpio_request(host->detect_pin, "mmc_detect")) { 1055 if (gpio_request(host->detect_pin, "mmc_detect")) {
870 dev_dbg(&mmc->class_dev, "no detect pin available\n"); 1056 dev_dbg(&mmc->class_dev, "no detect pin available\n");
871 host->detect_pin = -1; 1057 host->detect_pin = -1;
@@ -873,7 +1059,7 @@ static int __init atmci_probe(struct platform_device *pdev)
873 host->present = !gpio_get_value(host->detect_pin); 1059 host->present = !gpio_get_value(host->detect_pin);
874 } 1060 }
875 } 1061 }
876 if (host->wp_pin >= 0) { 1062 if (gpio_is_valid(host->wp_pin)) {
877 if (gpio_request(host->wp_pin, "mmc_wp")) { 1063 if (gpio_request(host->wp_pin, "mmc_wp")) {
878 dev_dbg(&mmc->class_dev, "no WP pin available\n"); 1064 dev_dbg(&mmc->class_dev, "no WP pin available\n");
879 host->wp_pin = -1; 1065 host->wp_pin = -1;
@@ -884,7 +1070,7 @@ static int __init atmci_probe(struct platform_device *pdev)
884 1070
885 mmc_add_host(mmc); 1071 mmc_add_host(mmc);
886 1072
887 if (host->detect_pin >= 0) { 1073 if (gpio_is_valid(host->detect_pin)) {
888 setup_timer(&host->detect_timer, atmci_detect_change, 1074 setup_timer(&host->detect_timer, atmci_detect_change,
889 (unsigned long)host); 1075 (unsigned long)host);
890 1076
@@ -905,6 +1091,8 @@ static int __init atmci_probe(struct platform_device *pdev)
905 "Atmel MCI controller at 0x%08lx irq %d\n", 1091 "Atmel MCI controller at 0x%08lx irq %d\n",
906 host->mapbase, irq); 1092 host->mapbase, irq);
907 1093
1094 atmci_init_debugfs(host);
1095
908 return 0; 1096 return 0;
909 1097
910err_request_irq: 1098err_request_irq:
@@ -923,7 +1111,9 @@ static int __exit atmci_remove(struct platform_device *pdev)
923 platform_set_drvdata(pdev, NULL); 1111 platform_set_drvdata(pdev, NULL);
924 1112
925 if (host) { 1113 if (host) {
926 if (host->detect_pin >= 0) { 1114 /* Debugfs stuff is cleaned up by mmc core */
1115
1116 if (gpio_is_valid(host->detect_pin)) {
927 int pin = host->detect_pin; 1117 int pin = host->detect_pin;
928 1118
929 /* Make sure the timer doesn't enable the interrupt */ 1119 /* Make sure the timer doesn't enable the interrupt */
@@ -943,7 +1133,7 @@ static int __exit atmci_remove(struct platform_device *pdev)
943 mci_readl(host, SR); 1133 mci_readl(host, SR);
944 clk_disable(host->mck); 1134 clk_disable(host->mck);
945 1135
946 if (host->wp_pin >= 0) 1136 if (gpio_is_valid(host->wp_pin))
947 gpio_free(host->wp_pin); 1137 gpio_free(host->wp_pin);
948 1138
949 free_irq(platform_get_irq(pdev, 0), host->mmc); 1139 free_irq(platform_get_irq(pdev, 0), host->mmc);