aboutsummaryrefslogtreecommitdiffstats
path: root/recipes/linux/linux-2.6.18/mmc-add-detect-card-and-wp-support.patch
blob: f91ddc239a7a571411c954b9afccdfef95dfa1f2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
Index: linux-2.6.18/drivers/mmc/atmel-mci.c
===================================================================
--- linux-2.6.18.orig/drivers/mmc/atmel-mci.c	2007-01-16 14:01:56.000000000 +0100
+++ linux-2.6.18/drivers/mmc/atmel-mci.c	2007-01-16 14:20:23.000000000 +0100
@@ -72,7 +72,6 @@
 	u32			error_status;
 
 	int			present;
-	unsigned int		wp_present:1;
 
 	unsigned long		bus_hz;
 	unsigned long		mapbase;
@@ -538,9 +537,26 @@
 	}
 }
 
+int atmci_get_ro(struct mmc_host *mmc)
+{
+	int read_only = 0;
+	struct atmel_mci *host = mmc_priv(mmc);
+
+	if (host->board->wp_pin != GPIO_PIO_NONE) {
+		read_only = gpio_get_value(host->board->wp_pin);
+		pr_debug("%s: card is %s\n", mmc_hostname(mmc),
+				(read_only ? "read-only" : "read-write") );
+	} else {
+		pr_debug("%s: host does not support reading read-only switch."
+				" Assuming write-enable.\n", mmc_hostname(mmc));
+	}
+	return read_only;
+}
+
 static struct mmc_host_ops atmci_ops = {
 	.request	= atmci_request,
 	.set_ios	= atmci_set_ios,
+	.get_ro		= atmci_get_ro,
 };
 
 static void atmci_request_end(struct mmc_host *mmc, struct mmc_request *mrq)
@@ -695,6 +711,37 @@
 		data->bytes_xfered = data->blocks * data->blksz;
 		atmci_data_complete(host, data);
 	}
+	if (mci_clear_card_detect_is_pending(host)) {
+		/* Reset controller if card is gone */
+		if (!host->present) {
+			mci_writel(host, CR, MCI_BIT(SWRST));
+			mci_writel(host, IDR, ~0UL);
+			mci_writel(host, CR, MCI_BIT(MCIEN));
+		}
+
+		/* Clean up queue if present */
+		if (mrq) {
+			if (!mci_cmd_is_complete(host)
+					&& !mci_cmd_error_is_complete(host)) {
+				mrq->cmd->error = MMC_ERR_TIMEOUT;
+			}
+			if (mrq->data && !mci_data_is_complete(host)
+					&& !mci_data_error_is_complete(host)) {
+				dma_stop_request(host->dma.req.req.dmac,
+						host->dma.req.req.channel);
+				host->data->error = MMC_ERR_TIMEOUT;
+				atmci_data_complete(host, data);
+			}
+			if (mrq->stop && !mci_stop_is_complete(host)
+					&& !mci_stop_error_is_complete(host)) {
+				mrq->stop->error = MMC_ERR_TIMEOUT;
+			}
+
+			host->cmd = NULL;
+			atmci_request_end(mmc, mrq);
+		}
+		mmc_detect_change(host->mmc, msecs_to_jiffies(100));
+	}
 }
 
 static void atmci_cmd_interrupt(struct mmc_host *mmc, u32 status)
@@ -821,6 +868,23 @@
 	return IRQ_HANDLED;
 }
 
+static irqreturn_t atmci_detect_int(int irq, void *dev_id, struct pt_regs *regs)
+{
+	struct mmc_host *mmc = dev_id;
+	struct atmel_mci *host = mmc_priv(mmc);
+
+	int present = !gpio_get_value(irq_to_gpio(irq));
+
+	if (present != host->present) {
+		pr_debug("%s: card %s\n", mmc_hostname(host->mmc),
+				present ? "inserted" : "removed");
+		host->present = present;
+		mci_set_card_detect_pending(host);
+		tasklet_schedule(&host->tasklet);
+	}
+	return IRQ_HANDLED;
+}
+
 static int __devinit atmci_probe(struct platform_device *pdev)
 {
 	struct atmel_mci *host;
@@ -887,15 +951,9 @@
 
 	if (host->board && host->board->wp_pin != GPIO_PIO_NONE) {
 		ret = gpio_request(host->board->wp_pin, "mmc_wp");
-		if (ret) {
+		if (ret)
 			printk(KERN_WARNING "%s: no WP pin available (%d)\n",
 					mmc_hostname(host->mmc), ret);
-			host->wp_present = 0;
-		} else {
-			host->wp_present = 1;
-		}
-	} else {
-		host->wp_present = 0;
 	}
 
 	/* TODO: Get this information from platform data */
@@ -928,6 +986,19 @@
 
 	mmc_add_host(mmc);
 
+	if (host->present != -1) {
+		ret = request_irq(gpio_to_irq(host->board->detect_pin),
+				atmci_detect_int,
+				IRQF_TRIGGER_FALLING|IRQF_TRIGGER_RISING,
+				"mmci", mmc);
+		if (ret) {
+			printk(KERN_ERR "%s: could not request IRQ %d "
+					"for detect pin\n",
+					mmc_hostname(mmc),
+					gpio_to_irq(host->board->detect_pin));
+		}
+	}
+
 	printk(KERN_INFO "%s: Atmel MCI controller at 0x%08lx irq %d\n",
 	       mmc_hostname(mmc), host->mapbase, irq);
 
@@ -936,8 +1007,11 @@
 	return 0;
 
 out_free_irq:
-	if (host->present != -1)
+	if (host->present != -1) {
+		free_irq(gpio_to_irq(host->board->detect_pin), host->mmc);
+		cancel_delayed_work(&host->mmc->detect);
 		gpio_free(host->board->detect_pin);
+	}
 	if (host->board->wp_pin != GPIO_PIO_NONE)
 		gpio_free(host->board->wp_pin);
 	free_irq(irq, mmc);
@@ -971,6 +1045,7 @@
 					host->dma.req.req.channel);
 
 		if (host->present != -1) {
+			free_irq(gpio_to_irq(host->board->detect_pin), host->mmc);
 			cancel_delayed_work(&host->mmc->detect);
 			gpio_free(host->board->detect_pin);
 		}