From 06c169985c0884ce67377c79d27383e23f41e2cd Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Mon, 7 Mar 2016 16:46:39 +0000 Subject: [PATCH 1/2] 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 --- arch/arm/boot/dts/bcm2708_common.dtsi | 5 +- drivers/mmc/host/bcm2835-sdhost.c | 70 ++++++++++++++++++--------- 2 files changed, 50 insertions(+), 25 deletions(-) diff --git a/arch/arm/boot/dts/bcm2708_common.dtsi b/arch/arm/boot/dts/bcm2708_common.dtsi index 4f65203b784c0a..4f833a93b22fab 100644 --- 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"; diff --git a/drivers/mmc/host/bcm2835-sdhost.c b/drivers/mmc/host/bcm2835-sdhost.c index 4f6cab52a60f5a..4cc4272c869d85 100644 --- 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(struct bcm2835_host *host, 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(struct bcm2835_host *host, 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 bcm2835_host *host) 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 platform_device *pdev) 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); } } From af80d75abc7604cd9eb1788b0171148d000db09d Mon Sep 17 00:00:00 2001 From: Phil Elwell Date: Tue, 8 Mar 2016 09:49:16 +0000 Subject: [PATCH 2/2] 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 --- arch/arm/boot/dts/bcm2708_common.dtsi | 5 +- drivers/mmc/host/bcm2835-mmc.c | 69 +++++++++++++++++++-------- 2 files changed, 51 insertions(+), 23 deletions(-) diff --git a/arch/arm/boot/dts/bcm2708_common.dtsi b/arch/arm/boot/dts/bcm2708_common.dtsi index 4f833a93b22fab..e0be77a00a6c53 100644 --- 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"; }; diff --git a/drivers/mmc/host/bcm2835-mmc.c b/drivers/mmc/host/bcm2835-mmc.c index 104f93ecd51d96..ceb37930e167f4 100644 --- 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(void *param) 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(struct bcm2835_host *host) 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 bcm2835_host *host, u32 intmask) 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 bcm2835_host *host) 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 platform_device *pdev) #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);