Received: by 2002:ac0:a5b6:0:0:0:0:0 with SMTP id m51-v6csp5328849imm; Tue, 12 Jun 2018 06:19:37 -0700 (PDT) X-Google-Smtp-Source: ADUXVKLsjtfl40ouEFNpGEztrczQTvW62hrXcd1pyXhnivBkYV2JBWlF9vFPfLu5To6rHXynmBdU X-Received: by 2002:a63:6106:: with SMTP id v6-v6mr269071pgb.441.1528809577098; Tue, 12 Jun 2018 06:19:37 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1528809577; cv=none; d=google.com; s=arc-20160816; b=E0tmNyHsO1QqBfy7sEuoTQEBuVsoOT+bMMgpL+V48EWPSpl1XuDrL8zKLUMj8YgZOM NKqvuEWFRHb/swOH2nbCyF8hT74JSmozuGcLSAKRn+oPSnRPhRnunTCuY0yaUBgR8f2D aiXHG8zHj0FJ337puMq1IKy+9pWQ8PKzNPl3lF2I+LeElkZV3lj6IH1zWBBVBxifrXtz GqjE/fsMAvwC6aYBcXRhCXUw64IgdJ0fjVeYxozLbpbvzQ7+RcgIY8tP7PJ/HiC83AKq Hafb7FS3uew7bXDTX+bH+BrSxbZ1Re9nb+gYc+XIQHlVj0ze7r1hr3hRDlWwvbnesB36 ubdg== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:arc-authentication-results; bh=2sfLjQ8xR+jOTRD4KfozI2HwSoZxpuRVc88KICslBA4=; b=qZ4uKOUAXmz3VewqrSS5oGnM6exAOsK6Ey9KnXhhmAbd4LYy/cIhQj2qcgiGEE++9B sIagfG8cqFSqlVLMGOJoXD4iScMQM9y4zaWUcWtKodqeDnfk3oHN5Cs0IK6Y6mGSK6NI jjnOGDTQrnEEhvDKp1VC4DlIhkzMx/BlT0B9CaBPsEnelWlR0xv1VIpjh9tiqXSaZeCP XhinP+yZzEwFUr0ekJFgNWq6oGzk2xx1spKLySsxYU4uoVa2xmIQuWFgaCllCRAsypL2 vljvRVImN0SYEWB8B2JS6CiOgfd+Vhz0u6xlidtyWHFuOnxaNITrE1arbNYe6XRK/Tns pq7Q== ARC-Authentication-Results: i=1; mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id b4-v6si127280pla.345.2018.06.12.06.19.23; Tue, 12 Jun 2018 06:19:37 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S934185AbeFLNRX (ORCPT + 99 others); Tue, 12 Jun 2018 09:17:23 -0400 Received: from mx08-00178001.pphosted.com ([91.207.212.93]:11702 "EHLO mx07-00178001.pphosted.com" rhost-flags-OK-OK-OK-FAIL) by vger.kernel.org with ESMTP id S934128AbeFLNQ5 (ORCPT ); Tue, 12 Jun 2018 09:16:57 -0400 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 CC: Maxime Coquelin , Alexandre Torgue , Gerald Baeza , , , , , Ludovic Barre 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 Content-Type: text/plain X-Originating-IP: [10.48.0.237] X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10434:,, definitions=2018-06-12_01:,, signatures=0 Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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__ */ -- 2.7.4