|
|
@@ -0,0 +1,170 @@
|
|
|
+From af80d75abc7604cd9eb1788b0171148d000db09d Mon Sep 17 00:00:00 2001
|
|
|
+From: Phil Elwell <[email protected]>
|
|
|
+Date: Tue, 8 Mar 2016 09:49:16 +0000
|
|
|
+Subject: [PATCH 176/180] bcm2835-mmc: Only claim one DMA channel
|
|
|
+
|
|
|
+With both MMC controllers enabled there are few DMA channels left. The
|
|
|
+bcm2835-mmc 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 <[email protected]>
|
|
|
+---
|
|
|
+ arch/arm/boot/dts/bcm2708_common.dtsi | 5 +--
|
|
|
+ drivers/mmc/host/bcm2835-mmc.c | 69 +++++++++++++++++++++++++----------
|
|
|
+ 2 files changed, 51 insertions(+), 23 deletions(-)
|
|
|
+
|
|
|
+--- a/arch/arm/boot/dts/bcm2708_common.dtsi
|
|
|
++++ b/arch/arm/boot/dts/bcm2708_common.dtsi
|
|
|
+@@ -232,9 +232,8 @@
|
|
|
+ reg = <0x7e300000 0x100>;
|
|
|
+ interrupts = <2 30>;
|
|
|
+ clocks = <&clk_mmc>;
|
|
|
+- dmas = <&dma 11>,
|
|
|
+- <&dma 11>;
|
|
|
+- dma-names = "tx", "rx";
|
|
|
++ dmas = <&dma 11>;
|
|
|
++ dma-names = "rx-tx";
|
|
|
+ brcm,overclock-50 = <0>;
|
|
|
+ status = "disabled";
|
|
|
+ };
|
|
|
+--- a/drivers/mmc/host/bcm2835-mmc.c
|
|
|
++++ b/drivers/mmc/host/bcm2835-mmc.c
|
|
|
+@@ -108,8 +108,9 @@ struct bcm2835_host {
|
|
|
+ u32 shadow;
|
|
|
+
|
|
|
+ /*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_rxtx; /* DMA channel for reads and writes */
|
|
|
++ struct dma_slave_config dma_cfg_rx;
|
|
|
++ struct dma_slave_config dma_cfg_tx;
|
|
|
+ struct dma_async_tx_descriptor *tx_desc; /* descriptor */
|
|
|
+
|
|
|
+ bool have_dma;
|
|
|
+@@ -342,7 +343,7 @@ static void bcm2835_mmc_dma_complete(voi
|
|
|
+
|
|
|
+ if (host->data && !(host->data->flags & MMC_DATA_WRITE)) {
|
|
|
+ /* otherwise handled in SDHCI IRQ */
|
|
|
+- dma_chan = host->dma_chan_rx;
|
|
|
++ dma_chan = host->dma_chan_rxtx;
|
|
|
+ dir_data = DMA_FROM_DEVICE;
|
|
|
+
|
|
|
+ dma_unmap_sg(dma_chan->device->dev,
|
|
|
+@@ -493,16 +494,21 @@ static void bcm2835_mmc_transfer_dma(str
|
|
|
+ if (host->blocks == 0)
|
|
|
+ return;
|
|
|
+
|
|
|
++ dma_chan = host->dma_chan_rxtx;
|
|
|
+ if (host->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;
|
|
|
+ }
|
|
|
+
|
|
|
++ /* 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);
|
|
|
++
|
|
|
+ BUG_ON(!dma_chan->device);
|
|
|
+ BUG_ON(!dma_chan->device->dev);
|
|
|
+ BUG_ON(!host->data->sg);
|
|
|
+@@ -936,7 +942,7 @@ static void bcm2835_mmc_data_irq(struct
|
|
|
+ if (host->data->flags & MMC_DATA_WRITE) {
|
|
|
+ /* IRQ handled here */
|
|
|
+
|
|
|
+- dma_chan = host->dma_chan_tx;
|
|
|
++ dma_chan = host->dma_chan_rxtx;
|
|
|
+ dir_data = DMA_TO_DEVICE;
|
|
|
+ dma_unmap_sg(dma_chan->device->dev,
|
|
|
+ host->data->sg, host->data->sg_len,
|
|
|
+@@ -1316,28 +1322,47 @@ static int bcm2835_mmc_add_host(struct b
|
|
|
+ dev_info(dev, "Forcing PIO mode\n");
|
|
|
+ host->have_dma = false;
|
|
|
+ #else
|
|
|
+- if (IS_ERR_OR_NULL(host->dma_chan_tx) ||
|
|
|
+- IS_ERR_OR_NULL(host->dma_chan_rx)) {
|
|
|
+- dev_err(dev, "%s: Unable to initialise DMA channels. Falling back to PIO\n",
|
|
|
++ if (IS_ERR_OR_NULL(host->dma_chan_rxtx)) {
|
|
|
++ dev_err(dev, "%s: Unable to initialise DMA channel. Falling back to PIO\n",
|
|
|
+ DRIVER_NAME);
|
|
|
+ host->have_dma = false;
|
|
|
+ } else {
|
|
|
+- dev_info(dev, "DMA channels allocated");
|
|
|
+- host->have_dma = true;
|
|
|
++ dev_info(dev, "DMA channel allocated");
|
|
|
+
|
|
|
+ cfg.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
|
|
+ cfg.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
|
|
|
+ cfg.slave_id = 11; /* DREQ channel */
|
|
|
+
|
|
|
++ /* Validate the slave configurations */
|
|
|
++
|
|
|
+ cfg.direction = DMA_MEM_TO_DEV;
|
|
|
+ cfg.src_addr = 0;
|
|
|
+ cfg.dst_addr = host->bus_addr + SDHCI_BUFFER;
|
|
|
+- ret = dmaengine_slave_config(host->dma_chan_tx, &cfg);
|
|
|
+
|
|
|
+- cfg.direction = DMA_DEV_TO_MEM;
|
|
|
+- cfg.src_addr = host->bus_addr + SDHCI_BUFFER;
|
|
|
+- 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 + SDHCI_BUFFER;
|
|
|
++ 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. "
|
|
|
++ "Faling back to PIO\n",
|
|
|
++ mmc_hostname(mmc));
|
|
|
++ dma_release_channel(host->dma_chan_rxtx);
|
|
|
++ host->dma_chan_rxtx = NULL;
|
|
|
++ host->use_dma = false;
|
|
|
++ }
|
|
|
+ }
|
|
|
+ #endif
|
|
|
+ mmc->max_segs = 128;
|
|
|
+@@ -1416,16 +1441,20 @@ static int bcm2835_mmc_probe(struct plat
|
|
|
+
|
|
|
+ #ifndef FORCE_PIO
|
|
|
+ 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 = dma_request_channel(mask, NULL, NULL);
|
|
|
++ host->dma_chan_rxtx = dma_request_channel(mask, NULL, NULL);
|
|
|
+ }
|
|
|
+ #endif
|
|
|
+ clk = devm_clk_get(dev, NULL);
|