aboutsummaryrefslogtreecommitdiffstats
path: root/target/linux/brcm2708/patches-4.4/0234-bcm2835-sdhost-Reset-the-clock-in-task-context.patch
blob: ebb2cc290f59d8a703507bbfc9b2b9031de4afb3 (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
From d17f0605ff4b3197eb6667368adc07c5ca859679 Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.org>
Date: Mon, 11 Apr 2016 12:50:58 +0100
Subject: [PATCH] bcm2835-sdhost: Reset the clock in task context

Since reprogramming the clock can now involve a round-trip to the
firmware it must not be done at atomic context, and a tasklet
is not a task.

Signed-off-by: Phil Elwell <phil@raspberrypi.org>
---
 drivers/mmc/host/bcm2835-sdhost.c | 25 ++++++++++++++++++-------
 1 file changed, 18 insertions(+), 7 deletions(-)

--- a/drivers/mmc/host/bcm2835-sdhost.c
+++ b/drivers/mmc/host/bcm2835-sdhost.c
@@ -185,6 +185,7 @@ struct bcm2835_host {
 
 	unsigned int			debug:1;		/* Enable debug output */
 	unsigned int			firmware_sets_cdiv:1;	/* Let the firmware manage the clock */
+	unsigned int			reset_clock:1;		/* Reset the clock fore the next request */
 
 	/*DMA part*/
 	struct dma_chan			*dma_chan_rxtx;		/* DMA channel for reads and writes */
@@ -1505,6 +1506,7 @@ void bcm2835_sdhost_set_clock(struct bcm
 {
 	int div = 0; /* Initialized for compiler warning */
 	unsigned int input_clock = clock;
+	unsigned long flags;
 
 	if (host->debug)
 		pr_info("%s: set_clock(%d)\n", mmc_hostname(host->mmc), clock);
@@ -1544,13 +1546,17 @@ void bcm2835_sdhost_set_clock(struct bcm
 				      &msg, sizeof(msg));
 
 		clock = max(msg[1], msg[2]);
+		spin_lock_irqsave(&host->lock, flags);
 	} else {
+		spin_lock_irqsave(&host->lock, flags);
 		if (clock < 100000) {
 			/* Can't stop the clock, but make it as slow as
 			 * possible to show willing
 			 */
 			host->cdiv = SDCDIV_MAX_CDIV;
 			bcm2835_sdhost_write(host, host->cdiv, SDCDIV);
+			mmiowb();
+			spin_unlock_irqrestore(&host->lock, flags);
 			return;
 		}
 
@@ -1605,6 +1611,11 @@ void bcm2835_sdhost_set_clock(struct bcm
 	bcm2835_sdhost_write(host, clock/2, SDTOUT);
 
 	host->mmc->actual_clock = clock;
+	host->clock = input_clock;
+	host->reset_clock = 0;
+
+	mmiowb();
+	spin_unlock_irqrestore(&host->lock, flags);
 }
 
 static void bcm2835_sdhost_request(struct mmc_host *mmc, struct mmc_request *mrq)
@@ -1653,6 +1664,9 @@ static void bcm2835_sdhost_request(struc
 	    (mrq->data->blocks > host->pio_limit))
 		bcm2835_sdhost_prepare_dma(host, mrq->data);
 
+	if (host->reset_clock)
+	    bcm2835_sdhost_set_clock(host, host->clock);
+
 	spin_lock_irqsave(&host->lock, flags);
 
 	WARN_ON(host->mrq != NULL);
@@ -1731,14 +1745,12 @@ static void bcm2835_sdhost_set_ios(struc
 
 	bcm2835_sdhost_write(host, host->hcfg, SDHCFG);
 
-	if (!ios->clock || ios->clock != host->clock) {
-		bcm2835_sdhost_set_clock(host, ios->clock);
-		host->clock = ios->clock;
-	}
-
 	mmiowb();
 
 	spin_unlock_irqrestore(&host->lock, flags);
+
+	if (!ios->clock || ios->clock != host->clock)
+		bcm2835_sdhost_set_clock(host, ios->clock);
 }
 
 static struct mmc_host_ops bcm2835_sdhost_ops = {
@@ -1810,7 +1822,7 @@ static void bcm2835_sdhost_tasklet_finis
 			host->overclock_50--;
 			pr_warn("%s: reducing overclock due to errors\n",
 				mmc_hostname(host->mmc));
-			bcm2835_sdhost_set_clock(host,50*MHZ);
+			host->reset_clock = 1;
 			mrq->cmd->error = -EILSEQ;
 			mrq->cmd->retries = 1;
 		}
@@ -1979,7 +1991,6 @@ static int bcm2835_sdhost_probe(struct p
 	mmc->ops = &bcm2835_sdhost_ops;
 	host = mmc_priv(mmc);
 	host->mmc = mmc;
-	host->cmd_quick_poll_retries = 0;
 	host->pio_timeout = msecs_to_jiffies(500);
 	host->pio_limit = 1;
 	host->max_delay = 1; /* Warn if over 1ms */