diff options
Diffstat (limited to 'drivers/mmc/host/bcm2835.c')
-rw-r--r-- | drivers/mmc/host/bcm2835.c | 71 |
1 files changed, 39 insertions, 32 deletions
diff --git a/drivers/mmc/host/bcm2835.c b/drivers/mmc/host/bcm2835.c index 768972af8b85..50293529d6de 100644 --- a/drivers/mmc/host/bcm2835.c +++ b/drivers/mmc/host/bcm2835.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * bcm2835 sdhost driver. * @@ -25,18 +26,6 @@ * sdhci-bcm2708.c by Broadcom * sdhci-bcm2835.c by Stephen Warren and Oleksandr Tymoshenko * sdhci.c and sdhci-pci.c by Pierre Ossman - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include <linux/clk.h> #include <linux/delay.h> @@ -286,6 +275,7 @@ static void bcm2835_reset(struct mmc_host *mmc) if (host->dma_chan) dmaengine_terminate_sync(host->dma_chan); + host->dma_chan = NULL; bcm2835_reset_internal(host); } @@ -463,7 +453,7 @@ static void bcm2835_transfer_pio(struct bcm2835_host *host) static void bcm2835_prepare_dma(struct bcm2835_host *host, struct mmc_data *data) { - int len, dir_data, dir_slave; + int sg_len, dir_data, dir_slave; struct dma_async_tx_descriptor *desc = NULL; struct dma_chan *dma_chan; @@ -509,23 +499,24 @@ void bcm2835_prepare_dma(struct bcm2835_host *host, struct mmc_data *data) &host->dma_cfg_rx : &host->dma_cfg_tx); - len = dma_map_sg(dma_chan->device->dev, data->sg, data->sg_len, - dir_data); + sg_len = dma_map_sg(dma_chan->device->dev, data->sg, data->sg_len, + dir_data); + if (!sg_len) + return; - if (len > 0) { - desc = dmaengine_prep_slave_sg(dma_chan, data->sg, - len, dir_slave, - DMA_PREP_INTERRUPT | - DMA_CTRL_ACK); - } + desc = dmaengine_prep_slave_sg(dma_chan, data->sg, sg_len, dir_slave, + DMA_PREP_INTERRUPT | DMA_CTRL_ACK); - if (desc) { - desc->callback = bcm2835_dma_complete; - desc->callback_param = host; - host->dma_desc = desc; - host->dma_chan = dma_chan; - host->dma_dir = dir_data; + if (!desc) { + dma_unmap_sg(dma_chan->device->dev, data->sg, sg_len, dir_data); + return; } + + desc->callback = bcm2835_dma_complete; + desc->callback_param = host; + host->dma_desc = desc; + host->dma_chan = dma_chan; + host->dma_dir = dir_data; } static void bcm2835_start_dma(struct bcm2835_host *host) @@ -607,7 +598,7 @@ static void bcm2835_finish_request(struct bcm2835_host *host) struct dma_chan *terminate_chan = NULL; struct mmc_request *mrq; - cancel_delayed_work(&host->timeout_work); + cancel_delayed_work_sync(&host->timeout_work); mrq = host->mrq; @@ -772,6 +763,8 @@ static void bcm2835_finish_command(struct bcm2835_host *host) if (!(sdhsts & SDHSTS_CRC7_ERROR) || (host->cmd->opcode != MMC_SEND_OP_COND)) { + u32 edm, fsm; + if (sdhsts & SDHSTS_CMD_TIME_OUT) { host->cmd->error = -ETIMEDOUT; } else { @@ -780,6 +773,13 @@ static void bcm2835_finish_command(struct bcm2835_host *host) bcm2835_dumpregs(host); host->cmd->error = -EILSEQ; } + edm = readl(host->ioaddr + SDEDM); + fsm = edm & SDEDM_FSM_MASK; + if (fsm == SDEDM_FSM_READWAIT || + fsm == SDEDM_FSM_WRITESTART1) + /* Kick the FSM out of its wait */ + writel(edm | SDEDM_FORCE_DATA_MODE, + host->ioaddr + SDEDM); bcm2835_finish_request(host); return; } @@ -837,6 +837,8 @@ static void bcm2835_timeout(struct work_struct *work) dev_err(dev, "timeout waiting for hardware interrupt.\n"); bcm2835_dumpregs(host); + bcm2835_reset(host->mmc); + if (host->data) { host->data->error = -ETIMEDOUT; bcm2835_finish_data(host); @@ -1052,10 +1054,12 @@ static void bcm2835_dma_complete_work(struct work_struct *work) { struct bcm2835_host *host = container_of(work, struct bcm2835_host, dma_work); - struct mmc_data *data = host->data; + struct mmc_data *data; mutex_lock(&host->mutex); + data = host->data; + if (host->dma_chan) { dma_unmap_sg(host->dma_chan->device->dev, data->sg, data->sg_len, @@ -1180,9 +1184,6 @@ static void bcm2835_request(struct mmc_host *mmc, struct mmc_request *mrq) return; } - if (host->use_dma && mrq->data && (mrq->data->blocks > PIO_THRESHOLD)) - bcm2835_prepare_dma(host, mrq->data); - mutex_lock(&host->mutex); WARN_ON(host->mrq); @@ -1206,6 +1207,9 @@ static void bcm2835_request(struct mmc_host *mmc, struct mmc_request *mrq) return; } + if (host->use_dma && mrq->data && (mrq->data->blocks > PIO_THRESHOLD)) + bcm2835_prepare_dma(host, mrq->data); + host->use_sbc = !!mrq->sbc && host->mrq->data && (host->mrq->data->flags & MMC_DATA_READ); if (host->use_sbc) { @@ -1445,6 +1449,9 @@ static int bcm2835_remove(struct platform_device *pdev) cancel_work_sync(&host->dma_work); cancel_delayed_work_sync(&host->timeout_work); + if (host->dma_chan_rxtx) + dma_release_channel(host->dma_chan_rxtx); + mmc_free_host(host->mmc); platform_set_drvdata(pdev, NULL); |