From patchwork Tue Jun 12 13:14:38 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ludovic BARRE X-Patchwork-Id: 10460289 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 615CF6020F for ; Tue, 12 Jun 2018 13:22:04 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 4DC3428705 for ; Tue, 12 Jun 2018 13:22:04 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 41EF528762; Tue, 12 Jun 2018 13:22:04 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 9193B28705 for ; Tue, 12 Jun 2018 13:22:03 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:Cc:List-Subscribe:List-Help:List-Post: List-Archive:List-Unsubscribe:List-Id:MIME-Version:References:In-Reply-To: Message-ID:Date:Subject:To:From:Reply-To:Content-ID:Content-Description: Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc:Resent-Message-ID: List-Owner; bh=PfJmREGzdV5Ow9j+jOLDQPxcqvGTSWHSelc2JwfoQHU=; b=j+3LO27qbSZFtK hkl5rRUtnHbeNCfFtkItj42p5wuszh/xkHFh2Vp9vNh2pXqwVKCtZIe6F4I/lCE6n8o6xDSlalJCh AjRpttDUjcP9QgP/+GxXSMO79Nky9TIEIDGnt1BXthsgJ8EL4aWJ+3HL+6HO3Hei2RwutVHMU95C/ azOSomUne85d5V8SBBpqRKQXcgAtEVILiFcyoOYuUgD7RifszGkabEwxctpApsDFrqdqhbzX1Rita 6tIFRpfTNq9OBAgaGzotNk6coKVD3R2Xw6JfS0wLARbNNyeB9CZheWCJotp+2Ll8QWMIU3+7TLALo ONnUP5wzi2Q0Y92R0wgQ==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1fSjF9-0002ww-6R; Tue, 12 Jun 2018 13:21:51 +0000 Received: from mx08-00178001.pphosted.com ([91.207.212.93] helo=mx07-00178001.pphosted.com) by bombadil.infradead.org with esmtps (Exim 4.90_1 #2 (Red Hat Linux)) id 1fSj9o-0006Tw-0O for linux-arm-kernel@lists.infradead.org; Tue, 12 Jun 2018 13:16:55 +0000 Received: from pps.filterd (m0046660.ppops.net [127.0.0.1]) by mx08-.pphosted.com (8.16.0.21/8.16.0.21) with SMTP id w5CDE38u017834; Tue, 12 Jun 2018 15:16:12 +0200 Received: from beta.dmz-eu.st.com (beta.dmz-eu.st.com [164.129.1.35]) by mx08-00178001.pphosted.com with ESMTP id 2jjegrr44s-1 (version=TLSv1 cipher=ECDHE-RSA-AES256-SHA bits=256 verify=NOT); Tue, 12 Jun 2018 15:16:12 +0200 Received: from zeta.dmz-eu.st.com (zeta.dmz-eu.st.com [164.129.230.9]) by beta.dmz-eu.st.com (STMicroelectronics) with ESMTP id EB3294A; Tue, 12 Jun 2018 13:16:07 +0000 (GMT) Received: from Webmail-eu.st.com (Safex1hubcas22.st.com [10.75.90.92]) by zeta.dmz-eu.st.com (STMicroelectronics) with ESMTP id C5C5AA543; Tue, 12 Jun 2018 13:16:07 +0000 (GMT) Received: from SAFEX1HUBCAS21.st.com (10.75.90.44) by Safex1hubcas22.st.com (10.75.90.92) with Microsoft SMTP Server (TLS) id 14.3.361.1; Tue, 12 Jun 2018 15:16:07 +0200 Received: from lmecxl0923.lme.st.com (10.48.0.237) by Webmail-ga.st.com (10.75.90.48) with Microsoft SMTP Server (TLS) id 14.3.361.1; Tue, 12 Jun 2018 15:16:07 +0200 From: Ludovic Barre To: Ulf Hansson , Rob Herring Subject: [PATCH 17/19] mmc: mmci: add stm32 sdmmc idma support Date: Tue, 12 Jun 2018 15:14:38 +0200 Message-ID: <1528809280-31116-18-git-send-email-ludovic.Barre@st.com> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1528809280-31116-1-git-send-email-ludovic.Barre@st.com> References: <1528809280-31116-1-git-send-email-ludovic.Barre@st.com> MIME-Version: 1.0 X-Originating-IP: [10.48.0.237] X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:, , definitions=2018-06-12_01:, , signatures=0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20180612_061620_500732_790D72F8 X-CRM114-Status: GOOD ( 20.99 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: devicetree@vger.kernel.org, Alexandre Torgue , linux-mmc@vger.kernel.org, linux-kernel@vger.kernel.org, Ludovic Barre , Maxime Coquelin , Gerald Baeza , linux-arm-kernel@lists.infradead.org Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP From: Ludovic Barre This patch adds support of Internal DMA (IDMA) for STM32 sdmmc variant. Direct memory access (DMA) is used to provide high-speed transfer between the SDMMC FIFO and the memory. The SDMMC internal DMA (IDMA) provides one channel to be used either for transmit or receive. The IDMA provides 2 modes: - Single buffered channel: the data at the memory side is accessed in a linear matter starting from the base address IDMABASE with DATALENGTH. So, max segment number must be defined to one and max segment size to max datalength. - Linked list channel: the data at the memory side is subsequently accessed from linked buffers, located at base address IDMABASE. The size of the memory buffers is defined by IDMABSIZE. The first linked list item is defined at IDMABAR address, and next item in IDMALAR. In this mode sdmmc variant could transfer several memory segments (not contiguous) in the same request. Signed-off-by: Ludovic Barre --- drivers/mmc/host/mmci.h | 5 ++ drivers/mmc/host/mmci_dma.c | 197 ++++++++++++++++++++++++++++++++++++++++++++ drivers/mmc/host/mmci_dma.h | 2 + 3 files changed, 204 insertions(+) diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h index c2ad724..e36ea18 100644 --- a/drivers/mmc/host/mmci.h +++ b/drivers/mmc/host/mmci.h @@ -343,6 +343,8 @@ struct mmci_host; * @opendrain: bitmask identifying the OPENDRAIN bit inside MMCIPOWER register * @mmci_dma: Pointer to platform-specific DMA callbacks. * @reset: true if variant has need reset signal. + * @dma_lli: true if variant has dma link list feature. + * @stm32_idmabsize_mask: stm32 sdmmc idma buffer size. * @validate_data: if hardware block has specific constraint on validate data * @set_clk_ios: if clock procedure of variant is specific * @set_pwr_ios: if power procedure of variant is specific @@ -391,6 +393,8 @@ struct variant_data { u32 opendrain; struct mmci_dma_ops *mmci_dma; bool reset; + bool dma_lli; + u32 stm32_idmabsize_mask; int (*validate_data)(struct mmci_host *host, struct mmc_data *data); void (*set_clkreg)(struct mmci_host *host, unsigned int desired); void (*set_pwrreg)(struct mmci_host *host, unsigned char power_mode, @@ -445,5 +449,6 @@ struct mmci_host { }; extern struct mmci_dma_ops dmaengine; +extern struct mmci_dma_ops sdmmc_idma; void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl); diff --git a/drivers/mmc/host/mmci_dma.c b/drivers/mmc/host/mmci_dma.c index dd7dae5..27b55c2 100644 --- a/drivers/mmc/host/mmci_dma.c +++ b/drivers/mmc/host/mmci_dma.c @@ -581,3 +581,200 @@ struct mmci_dma_ops dmaengine = { #else struct mmci_dma_ops dmaengine = {}; #endif + +#define SDMMC_LLI_BUF_LEN PAGE_SIZE +#define SDMMC_IDMA_BURST BIT(MMCI_STM32_IDMABNDT_SHIFT) + +struct sdmmc_lli_desc { + u32 idmalar; + u32 idmabase; + u32 idmasize; +}; + +struct sdmmc_next { + s32 cookie; +}; + +struct sdmmc_priv { + dma_addr_t sg_dma; + void *sg_cpu; + struct sdmmc_next next_data; +}; + +static int __sdmmc_idma_prep_data(struct mmci_host *host, struct mmc_data *data) +{ + int n_elem; + + n_elem = dma_map_sg(mmc_dev(host->mmc), + data->sg, + data->sg_len, + mmc_get_dma_dir(data)); + + if (!n_elem) { + dev_err(mmc_dev(host->mmc), "dma_map_sg failed\n"); + return -EINVAL; + } + + return 0; +} + +int sdmmc_idma_validate_data(struct mmci_host *host, + struct mmc_data *data) +{ + struct sdmmc_priv *idma = host->dma_priv; + struct sdmmc_next *nd = &idma->next_data; + struct scatterlist *sg; + int ret, i; + + /* Check if next job is not already prepared. */ + if (data->host_cookie != nd->cookie) { + ret = __sdmmc_idma_prep_data(host, data); + if (ret) + return ret; + } + + /* + * idma has constraints on idmabase & idmasize for each element + * excepted the last element which has no constraint on idmasize + */ + for_each_sg(data->sg, sg, data->sg_len - 1, i) { + if (!IS_ALIGNED(sg_dma_address(data->sg), sizeof(u32)) || + !IS_ALIGNED(sg_dma_len(data->sg), SDMMC_IDMA_BURST)) { + dev_err(mmc_dev(host->mmc), + "unaligned scatterlist: ofst:%x length:%d\n", + data->sg->offset, data->sg->length); + return -EINVAL; + } + } + + if (!IS_ALIGNED(sg_dma_address(data->sg), sizeof(u32))) { + dev_err(mmc_dev(host->mmc), + "unaligned last scatterlist: ofst:%x length:%d\n", + data->sg->offset, data->sg->length); + return -EINVAL; + } + + return 0; +} + +static void sdmmc_idma_pre_req(struct mmci_host *host, struct mmc_data *data) +{ + struct sdmmc_priv *idma = host->dma_priv; + struct sdmmc_next *nd = &idma->next_data; + + if (!__sdmmc_idma_prep_data(host, data)) + data->host_cookie = ++nd->cookie < 0 ? 1 : nd->cookie; +} + +static void sdmmc_idma_post_req(struct mmci_host *host, struct mmc_data *data, + int err) +{ + if (!data || !data->host_cookie) + return; + + dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len, + mmc_get_dma_dir(data)); + + data->host_cookie = 0; +} + +static int sdmmc_idma_setup(struct mmci_host *host) +{ + struct sdmmc_priv *idma; + + idma = devm_kzalloc(mmc_dev(host->mmc), sizeof(*idma), GFP_KERNEL); + if (!idma) + return -ENOMEM; + + host->dma_priv = idma; + + if (host->variant->dma_lli) { + idma->sg_cpu = dmam_alloc_coherent(mmc_dev(host->mmc), + SDMMC_LLI_BUF_LEN, + &idma->sg_dma, GFP_KERNEL); + if (!idma->sg_cpu) { + dev_err(mmc_dev(host->mmc), + "Failed to alloc IDMA descriptor\n"); + return -ENOMEM; + } + host->mmc->max_segs = SDMMC_LLI_BUF_LEN / + sizeof(struct sdmmc_lli_desc); + host->mmc->max_seg_size = host->variant->stm32_idmabsize_mask; + } else { + host->mmc->max_segs = 1; + host->mmc->max_seg_size = host->mmc->max_req_size; + } + + /* initialize pre request cookie */ + idma->next_data.cookie = 1; + + return 0; +} + +static int sdmmc_idma_start(struct mmci_host *host, unsigned int datactrl) + +{ + struct sdmmc_priv *idma = host->dma_priv; + struct sdmmc_lli_desc *desc = (struct sdmmc_lli_desc *)idma->sg_cpu; + struct mmc_data *data = host->data; + struct scatterlist *sg; + int i; + + if (!host->variant->dma_lli || data->sg_len == 1) { + writel_relaxed(sg_dma_address(data->sg), + host->base + MMCI_STM32_IDMABASE0R); + writel_relaxed(MMCI_STM32_IDMAEN, + host->base + MMCI_STM32_IDMACTRLR); + goto out; + } + + for_each_sg(data->sg, sg, data->sg_len, i) { + desc[i].idmalar = (i + 1) * sizeof(struct sdmmc_lli_desc); + desc[i].idmalar |= MMCI_STM32_ULA | MMCI_STM32_ULS + | MMCI_STM32_ABR; + desc[i].idmabase = sg_dma_address(sg); + desc[i].idmasize = sg_dma_len(sg); + } + + /* notice the end of link list */ + desc[data->sg_len - 1].idmalar &= ~MMCI_STM32_ULA; + + dma_wmb(); + writel_relaxed(idma->sg_dma, host->base + MMCI_STM32_IDMABAR); + writel_relaxed(desc[0].idmalar, host->base + MMCI_STM32_IDMALAR); + writel_relaxed(desc[0].idmabase, host->base + MMCI_STM32_IDMABASE0R); + writel_relaxed(desc[0].idmasize, host->base + MMCI_STM32_IDMABSIZER); + writel_relaxed(MMCI_STM32_IDMAEN | MMCI_STM32_IDMALLIEN, + host->base + MMCI_STM32_IDMACTRLR); + + /* mask & datactrl */ +out: + mmci_write_datactrlreg(host, datactrl); + writel(readl(host->base + MMCIMASK0) | MCI_DATAENDMASK, + host->base + MMCIMASK0); + + return 0; +} + +static void sdmmc_idma_finalize(struct mmci_host *host, struct mmc_data *data) +{ + writel_relaxed(0, host->base + MMCI_STM32_IDMACTRLR); +} + +static void sdmmc_idma_get_next_data(struct mmci_host *host, + struct mmc_data *data) +{ + struct sdmmc_priv *idma = host->dma_priv; + struct sdmmc_next *next = &idma->next_data; + + WARN_ON(data->host_cookie && data->host_cookie != next->cookie); +} + +struct mmci_dma_ops sdmmc_idma = { + .setup = sdmmc_idma_setup, + .pre_req = sdmmc_idma_pre_req, + .start = sdmmc_idma_start, + .finalize = sdmmc_idma_finalize, + .post_req = sdmmc_idma_post_req, + .get_next_data = sdmmc_idma_get_next_data, +}; diff --git a/drivers/mmc/host/mmci_dma.h b/drivers/mmc/host/mmci_dma.h index 33e4e8a..cbfda89 100644 --- a/drivers/mmc/host/mmci_dma.h +++ b/drivers/mmc/host/mmci_dma.h @@ -28,4 +28,6 @@ void mmci_dma_post_req(struct mmci_host *host, void mmci_dma_error(struct mmci_host *host); void mmci_dma_get_next_data(struct mmci_host *host, struct mmc_data *data); +int sdmmc_idma_validate_data(struct mmci_host *host, + struct mmc_data *data); #endif /* __MMC_DMA_H__ */