summaryrefslogtreecommitdiffstats
path: root/target/linux/brcm2708/patches-4.4/0167-bcm2835-sdhost-Only-claim-one-DMA-channel.patch
blob: c487d6629228ed7bf628a8d95799f22f1f5dd06a (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
160
From 5f0af4369aeb791f481e05447e1d4d05579367cd Mon Sep 17 00:00:00 2001
From: Phil Elwell <phil@raspberrypi.org>
Date: Mon, 7 Mar 2016 16:46:39 +0000
Subject: [PATCH] bcm2835-sdhost: Only claim one DMA channel

With both MMC controllers enabled there are few DMA channels left. The
bcm2835-sdhost driver only uses DMA in one direction at a time, so it
doesn't need to claim two channels.

See: https://github.com/raspberrypi/linux/issues/1327

Signed-off-by: Phil Elwell <phil@raspberrypi.org>
---
 arch/arm/boot/dts/bcm2708_common.dtsi |  5 +--
 drivers/mmc/host/bcm2835-sdhost.c     | 70 ++++++++++++++++++++++++-----------
 2 files changed, 50 insertions(+), 25 deletions(-)

--- a/arch/arm/boot/dts/bcm2708_common.dtsi
+++ b/arch/arm/boot/dts/bcm2708_common.dtsi
@@ -136,9 +136,8 @@
 			reg = <0x7e202000 0x100>;
 			interrupts = <2 24>;
 			clocks = <&clk_core>;
-			dmas = <&dma 13>,
-			       <&dma 13>;
-			dma-names = "tx", "rx";
+			dmas = <&dma 13>;
+			dma-names = "rx-tx";
 			brcm,overclock-50 = <0>;
 			brcm,pio-limit = <1>;
 			status = "disabled";
--- a/drivers/mmc/host/bcm2835-sdhost.c
+++ b/drivers/mmc/host/bcm2835-sdhost.c
@@ -185,9 +185,10 @@ struct bcm2835_host {
 	unsigned int			debug:1;		/* Enable debug output */
 
 	/*DMA part*/
-	struct dma_chan			*dma_chan_rx;		/* DMA channel for reads */
-	struct dma_chan			*dma_chan_tx;		/* DMA channel for writes */
-	struct dma_chan			*dma_chan;		/* Channel in used */
+	struct dma_chan			*dma_chan_rxtx;		/* DMA channel for reads and writes */
+	struct dma_chan			*dma_chan;		/* Channel in use */
+	struct dma_slave_config		dma_cfg_rx;
+	struct dma_slave_config		dma_cfg_tx;
 	struct dma_async_tx_descriptor	*dma_desc;
 	u32				dma_dir;
 	u32				drain_words;
@@ -771,12 +772,11 @@ static void bcm2835_sdhost_prepare_dma(s
 	log_event("PRD<", (u32)data, 0);
 	pr_debug("bcm2835_sdhost_prepare_dma()\n");
 
+	dma_chan = host->dma_chan_rxtx;
 	if (data->flags & MMC_DATA_READ) {
-		dma_chan = host->dma_chan_rx;
 		dir_data = DMA_FROM_DEVICE;
 		dir_slave = DMA_DEV_TO_MEM;
 	} else {
-		dma_chan = host->dma_chan_tx;
 		dir_data = DMA_TO_DEVICE;
 		dir_slave = DMA_MEM_TO_DEV;
 	}
@@ -813,6 +813,12 @@ static void bcm2835_sdhost_prepare_dma(s
 		host->drain_words = len/4;
 	}
 
+	/* The parameters have already been validated, so this will not fail */
+	(void)dmaengine_slave_config(dma_chan,
+				     (dir_data == DMA_FROM_DEVICE) ?
+				     &host->dma_cfg_rx :
+				     &host->dma_cfg_tx);
+
 	len = dma_map_sg(dma_chan->device->dev, data->sg, data->sg_len,
 			 dir_data);
 
@@ -1805,28 +1811,46 @@ int bcm2835_sdhost_add_host(struct bcm28
 	spin_lock_init(&host->lock);
 
 	if (host->allow_dma) {
-		if (IS_ERR_OR_NULL(host->dma_chan_tx) ||
-		    IS_ERR_OR_NULL(host->dma_chan_rx)) {
-			pr_err("%s: unable to initialise DMA channels. "
+		if (IS_ERR_OR_NULL(host->dma_chan_rxtx)) {
+			pr_err("%s: unable to initialise DMA channel. "
 			       "Falling back to PIO\n",
 			       mmc_hostname(mmc));
 			host->use_dma = false;
 		} else {
-			host->use_dma = true;
-
 			cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 			cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
 			cfg.slave_id = 13;		/* DREQ channel */
 
+			/* Validate the slave configurations */
+
 			cfg.direction = DMA_MEM_TO_DEV;
 			cfg.src_addr = 0;
 			cfg.dst_addr = host->bus_addr + SDDATA;
-			ret = dmaengine_slave_config(host->dma_chan_tx, &cfg);
 
-			cfg.direction = DMA_DEV_TO_MEM;
-			cfg.src_addr = host->bus_addr + SDDATA;
-			cfg.dst_addr = 0;
-			ret = dmaengine_slave_config(host->dma_chan_rx, &cfg);
+			ret = dmaengine_slave_config(host->dma_chan_rxtx, &cfg);
+
+			if (ret == 0) {
+				host->dma_cfg_tx = cfg;
+
+				cfg.direction = DMA_DEV_TO_MEM;
+				cfg.src_addr = host->bus_addr + SDDATA;
+				cfg.dst_addr = 0;
+
+				ret = dmaengine_slave_config(host->dma_chan_rxtx, &cfg);
+			}
+
+			if (ret == 0) {
+				host->dma_cfg_rx = cfg;
+
+				host->use_dma = true;
+			} else {
+				pr_err("%s: unable to configure DMA channel. "
+				       "Falling back to PIO\n",
+				       mmc_hostname(mmc));
+				dma_release_channel(host->dma_chan_rxtx);
+				host->dma_chan_rxtx = NULL;
+				host->use_dma = false;
+			}
 		}
 	} else {
 		host->use_dma = false;
@@ -1948,19 +1972,21 @@ static int bcm2835_sdhost_probe(struct p
 
 	if (host->allow_dma) {
 		if (node) {
-			host->dma_chan_tx =
-				dma_request_slave_channel(dev, "tx");
-			host->dma_chan_rx =
-				dma_request_slave_channel(dev, "rx");
+			host->dma_chan_rxtx =
+				dma_request_slave_channel(dev, "rx-tx");
+			if (!host->dma_chan_rxtx)
+				host->dma_chan_rxtx =
+					dma_request_slave_channel(dev, "tx");
+			if (!host->dma_chan_rxtx)
+				host->dma_chan_rxtx =
+					dma_request_slave_channel(dev, "rx");
 		} else {
 			dma_cap_mask_t mask;
 
 			dma_cap_zero(mask);
 			/* we don't care about the channel, any would work */
 			dma_cap_set(DMA_SLAVE, mask);
-			host->dma_chan_tx =
-				dma_request_channel(mask, NULL, NULL);
-			host->dma_chan_rx =
+			host->dma_chan_rxtx =
 				dma_request_channel(mask, NULL, NULL);
 		}
 	}