LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant
@ 2018-06-12 13:14 Ludovic Barre
  2018-06-12 13:14 ` [PATCH 01/19] mmc: mmci: regroup and define dma operations Ludovic Barre
                   ` (19 more replies)
  0 siblings, 20 replies; 41+ messages in thread
From: Ludovic Barre @ 2018-06-12 13:14 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	linux-arm-kernel, linux-kernel, devicetree, linux-mmc,
	Ludovic Barre

From: Ludovic Barre <ludovic.barre@st.com>

This patch series adapts mmci driver to add support for stm32
sdmmc variant. stm32h7 SoC integrates the first revision of
stm32 sdmmc.

This series is composed of 3 parts:
-Prepare mmci driver to manage dma interfaces by adding property.
 New mmci dma API is defined according to the legacy needs.
-Adapt mmci driver to dedicated constraints of stm32 sdmmc variant,
 defined under some specific properties.
-Add stm32 sdmmc variant. As Internal DMA way satisfies data
transfer, the mmci driver hasn't been modified for pio_read/write.
Specific adds-ons to stm32 sdmmc:
 + registers
 + clk/power functions
 + idma interface

Ludovic Barre (19):
  mmc: mmci: regroup and define dma operations
  mmc: mmci: merge qcom dml feature into mmci dma
  mmc: mmci: add datactrl block size variant property
  mmc: mmci: expand startbiterr to irqmask and error check
  mmc: mmci: allow to overwrite clock/power procedure to specific
    variant
  mmc: mmci: add variant properties to define cpsm & cmdresp bits
  mmc: mmci: add variant property to define dpsm bit
  mmc: mmci: add variant property to define irq pio mask
  mmc: mmci: add variant property to write datactrl before command
  mmc: mmci: add variant property to allow remain data
  mmc: mmci: add variant property to check specific data constraint
  mmc: mmci: add variant property to request a reset
  mmc: mmci: send stop cmd if a data command fail
  mmc: mmci: add clock divider for stm32 sdmmc
  mmc: mmci: add stm32 sdmmc registers
  mmc: mmci: add DT bindings for STM32 sdmmc
  mmc: mmci: add stm32 sdmmc idma support
  mmc: mmci: add specific clk/pwr procedure for stm32 sdmmc
  mmc: mmci: add stm32 sdmmc variant

 Documentation/devicetree/bindings/mmc/mmci.txt |  11 +
 drivers/mmc/host/Makefile                      |   3 +-
 drivers/mmc/host/mmci.c                        | 846 +++++++++++--------------
 drivers/mmc/host/mmci.h                        | 237 ++++++-
 drivers/mmc/host/mmci_dma.c                    | 780 +++++++++++++++++++++++
 drivers/mmc/host/mmci_dma.h                    |  33 +
 drivers/mmc/host/mmci_qcom_dml.c               | 177 ------
 drivers/mmc/host/mmci_qcom_dml.h               |  31 -
 8 files changed, 1410 insertions(+), 708 deletions(-)
 create mode 100644 drivers/mmc/host/mmci_dma.c
 create mode 100644 drivers/mmc/host/mmci_dma.h
 delete mode 100644 drivers/mmc/host/mmci_qcom_dml.c
 delete mode 100644 drivers/mmc/host/mmci_qcom_dml.h

-- 
2.7.4


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 01/19] mmc: mmci: regroup and define dma operations
  2018-06-12 13:14 [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
@ 2018-06-12 13:14 ` Ludovic Barre
  2018-07-05 15:17   ` Ulf Hansson
  2018-06-12 13:14 ` [PATCH 02/19] mmc: mmci: merge qcom dml feature into mmci dma Ludovic Barre
                   ` (18 subsequent siblings)
  19 siblings, 1 reply; 41+ messages in thread
From: Ludovic Barre @ 2018-06-12 13:14 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	linux-arm-kernel, linux-kernel, devicetree, linux-mmc,
	Ludovic Barre

From: Ludovic Barre <ludovic.barre@st.com>

Prepare mmci driver to manage dma interface by new property.
This patch defines and regroups dma operations for mmci drivers.
mmci_dma_XX prototypes are added to call member of mmci_dma_ops
if not null. Based on legacy need, a mmci dma interface has been
defined with:
-mmci_dma_setup
-mmci_dma_release
-mmci_dma_pre_req
-mmci_dma_start
-mmci_dma_finalize
-mmci_dma_post_req
-mmci_dma_error
-mmci_dma_get_next_data

A dma_priv is added to host structure to contain private dma needs.

Legacy dmaengine functions have been moved with just some adaptation on:
-dma_in_progress moves to dmaengine_priv structure, called in
 dmaengine_error and dmaengine_finalize.
-centralized dma_unmap_sg into dmaengine_unmap, called by
 dmaengine_error and dmaengine_finalize.

Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
---
 drivers/mmc/host/Makefile   |   2 +-
 drivers/mmc/host/mmci.c     | 437 ++----------------------------------------
 drivers/mmc/host/mmci.h     |  95 ++++++++--
 drivers/mmc/host/mmci_dma.c | 450 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/host/mmci_dma.h |  31 +++
 5 files changed, 579 insertions(+), 436 deletions(-)
 create mode 100644 drivers/mmc/host/mmci_dma.c
 create mode 100644 drivers/mmc/host/mmci_dma.h

diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 85dc132..daecaa98 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -4,7 +4,7 @@
 #
 
 obj-$(CONFIG_MMC_ARMMMCI) += armmmci.o
-armmmci-y := mmci.o
+armmmci-y := mmci.o mmci_dma.o
 armmmci-$(CONFIG_MMC_QCOM_DML) += mmci_qcom_dml.o
 obj-$(CONFIG_MMC_PXA)		+= pxamci.o
 obj-$(CONFIG_MMC_MXC)		+= mxcmmc.o
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index f184977..8868be0 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -42,84 +42,13 @@
 #include <asm/io.h>
 
 #include "mmci.h"
+#include "mmci_dma.h"
 #include "mmci_qcom_dml.h"
 
 #define DRIVER_NAME "mmci-pl18x"
 
 static unsigned int fmax = 515633;
 
-/**
- * struct variant_data - MMCI variant-specific quirks
- * @clkreg: default value for MCICLOCK register
- * @clkreg_enable: enable value for MMCICLOCK register
- * @clkreg_8bit_bus_enable: enable value for 8 bit bus
- * @clkreg_neg_edge_enable: enable value for inverted data/cmd output
- * @datalength_bits: number of bits in the MMCIDATALENGTH register
- * @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY
- *	      is asserted (likewise for RX)
- * @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY
- *		  is asserted (likewise for RX)
- * @data_cmd_enable: enable value for data commands.
- * @st_sdio: enable ST specific SDIO logic
- * @st_clkdiv: true if using a ST-specific clock divider algorithm
- * @datactrl_mask_ddrmode: ddr mode mask in datactrl register.
- * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register
- * @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl
- *		     register
- * @datactrl_mask_sdio: SDIO enable mask in datactrl register
- * @pwrreg_powerup: power up value for MMCIPOWER register
- * @f_max: maximum clk frequency supported by the controller.
- * @signal_direction: input/out direction of bus signals can be indicated
- * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock
- * @busy_detect: true if the variant supports busy detection on DAT0.
- * @busy_dpsm_flag: bitmask enabling busy detection in the DPSM
- * @busy_detect_flag: bitmask identifying the bit in the MMCISTATUS register
- *		      indicating that the card is busy
- * @busy_detect_mask: bitmask identifying the bit in the MMCIMASK0 to mask for
- *		      getting busy end detection interrupts
- * @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply
- * @explicit_mclk_control: enable explicit mclk control in driver.
- * @qcom_fifo: enables qcom specific fifo pio read logic.
- * @qcom_dml: enables qcom specific dma glue for dma transfers.
- * @reversed_irq_handling: handle data irq before cmd irq.
- * @mmcimask1: true if variant have a MMCIMASK1 register.
- * @start_err: bitmask identifying the STARTBITERR bit inside MMCISTATUS
- *	       register.
- * @opendrain: bitmask identifying the OPENDRAIN bit inside MMCIPOWER register
- */
-struct variant_data {
-	unsigned int		clkreg;
-	unsigned int		clkreg_enable;
-	unsigned int		clkreg_8bit_bus_enable;
-	unsigned int		clkreg_neg_edge_enable;
-	unsigned int		datalength_bits;
-	unsigned int		fifosize;
-	unsigned int		fifohalfsize;
-	unsigned int		data_cmd_enable;
-	unsigned int		datactrl_mask_ddrmode;
-	unsigned int		datactrl_mask_sdio;
-	bool			st_sdio;
-	bool			st_clkdiv;
-	bool			blksz_datactrl16;
-	bool			blksz_datactrl4;
-	u32			pwrreg_powerup;
-	u32			f_max;
-	bool			signal_direction;
-	bool			pwrreg_clkgate;
-	bool			busy_detect;
-	u32			busy_dpsm_flag;
-	u32			busy_detect_flag;
-	u32			busy_detect_mask;
-	bool			pwrreg_nopower;
-	bool			explicit_mclk_control;
-	bool			qcom_fifo;
-	bool			qcom_dml;
-	bool			reversed_irq_handling;
-	bool			mmcimask1;
-	u32			start_err;
-	u32			opendrain;
-};
-
 static struct variant_data variant_arm = {
 	.fifosize		= 16 * 4,
 	.fifohalfsize		= 8 * 4,
@@ -130,6 +59,7 @@ static struct variant_data variant_arm = {
 	.mmcimask1		= true,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_ROD,
+	.mmci_dma		= &dmaengine,
 };
 
 static struct variant_data variant_arm_extended_fifo = {
@@ -141,6 +71,7 @@ static struct variant_data variant_arm_extended_fifo = {
 	.mmcimask1		= true,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_ROD,
+	.mmci_dma		= &dmaengine,
 };
 
 static struct variant_data variant_arm_extended_fifo_hwfc = {
@@ -153,6 +84,7 @@ static struct variant_data variant_arm_extended_fifo_hwfc = {
 	.mmcimask1		= true,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_ROD,
+	.mmci_dma		= &dmaengine,
 };
 
 static struct variant_data variant_u300 = {
@@ -171,6 +103,7 @@ static struct variant_data variant_u300 = {
 	.mmcimask1		= true,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_OD,
+	.mmci_dma		= &dmaengine,
 };
 
 static struct variant_data variant_nomadik = {
@@ -190,6 +123,7 @@ static struct variant_data variant_nomadik = {
 	.mmcimask1		= true,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_OD,
+	.mmci_dma		= &dmaengine,
 };
 
 static struct variant_data variant_ux500 = {
@@ -215,6 +149,7 @@ static struct variant_data variant_ux500 = {
 	.mmcimask1		= true,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_OD,
+	.mmci_dma		= &dmaengine,
 };
 
 static struct variant_data variant_ux500v2 = {
@@ -242,6 +177,7 @@ static struct variant_data variant_ux500v2 = {
 	.mmcimask1		= true,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_OD,
+	.mmci_dma		= &dmaengine,
 };
 
 static struct variant_data variant_stm32 = {
@@ -259,6 +195,7 @@ static struct variant_data variant_stm32 = {
 	.f_max			= 48000000,
 	.pwrreg_clkgate		= true,
 	.pwrreg_nopower		= true,
+	.mmci_dma		= &dmaengine,
 };
 
 static struct variant_data variant_qcom = {
@@ -280,6 +217,7 @@ static struct variant_data variant_qcom = {
 	.mmcimask1		= true,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_ROD,
+	.mmci_dma		= &dmaengine,
 };
 
 /* Busy detection for the ST Micro variant */
@@ -355,7 +293,7 @@ static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr)
 /*
  * This must be called with host->lock held
  */
-static void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl)
+void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl)
 {
 	/* Keep busy mode in DPSM if enabled */
 	datactrl |= host->datactrl_reg & host->variant->busy_dpsm_flag;
@@ -480,281 +418,10 @@ static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data)
 	sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags);
 }
 
-/*
- * All the DMA operation mode stuff goes inside this ifdef.
- * This assumes that you have a generic DMA device interface,
- * no custom DMA interfaces are supported.
- */
-#ifdef CONFIG_DMA_ENGINE
-static void mmci_dma_setup(struct mmci_host *host)
-{
-	const char *rxname, *txname;
-	struct variant_data *variant = host->variant;
-
-	host->dma_rx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "rx");
-	host->dma_tx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "tx");
-
-	/* initialize pre request cookie */
-	host->next_data.cookie = 1;
-
-	/*
-	 * If only an RX channel is specified, the driver will
-	 * attempt to use it bidirectionally, however if it is
-	 * is specified but cannot be located, DMA will be disabled.
-	 */
-	if (host->dma_rx_channel && !host->dma_tx_channel)
-		host->dma_tx_channel = host->dma_rx_channel;
-
-	if (host->dma_rx_channel)
-		rxname = dma_chan_name(host->dma_rx_channel);
-	else
-		rxname = "none";
-
-	if (host->dma_tx_channel)
-		txname = dma_chan_name(host->dma_tx_channel);
-	else
-		txname = "none";
-
-	dev_info(mmc_dev(host->mmc), "DMA channels RX %s, TX %s\n",
-		 rxname, txname);
-
-	/*
-	 * Limit the maximum segment size in any SG entry according to
-	 * the parameters of the DMA engine device.
-	 */
-	if (host->dma_tx_channel) {
-		struct device *dev = host->dma_tx_channel->device->dev;
-		unsigned int max_seg_size = dma_get_max_seg_size(dev);
-
-		if (max_seg_size < host->mmc->max_seg_size)
-			host->mmc->max_seg_size = max_seg_size;
-	}
-	if (host->dma_rx_channel) {
-		struct device *dev = host->dma_rx_channel->device->dev;
-		unsigned int max_seg_size = dma_get_max_seg_size(dev);
-
-		if (max_seg_size < host->mmc->max_seg_size)
-			host->mmc->max_seg_size = max_seg_size;
-	}
-
-	if (variant->qcom_dml && host->dma_rx_channel && host->dma_tx_channel)
-		if (dml_hw_init(host, host->mmc->parent->of_node))
-			variant->qcom_dml = false;
-}
-
-/*
- * This is used in or so inline it
- * so it can be discarded.
- */
-static inline void mmci_dma_release(struct mmci_host *host)
-{
-	if (host->dma_rx_channel)
-		dma_release_channel(host->dma_rx_channel);
-	if (host->dma_tx_channel)
-		dma_release_channel(host->dma_tx_channel);
-	host->dma_rx_channel = host->dma_tx_channel = NULL;
-}
-
-static void mmci_dma_data_error(struct mmci_host *host)
-{
-	dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n");
-	dmaengine_terminate_all(host->dma_current);
-	host->dma_in_progress = false;
-	host->dma_current = NULL;
-	host->dma_desc_current = NULL;
-	host->data->host_cookie = 0;
-}
-
-static void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data)
-{
-	struct dma_chan *chan;
-
-	if (data->flags & MMC_DATA_READ)
-		chan = host->dma_rx_channel;
-	else
-		chan = host->dma_tx_channel;
-
-	dma_unmap_sg(chan->device->dev, data->sg, data->sg_len,
-		     mmc_get_dma_dir(data));
-}
-
-static void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data)
-{
-	u32 status;
-	int i;
-
-	/* Wait up to 1ms for the DMA to complete */
-	for (i = 0; ; i++) {
-		status = readl(host->base + MMCISTATUS);
-		if (!(status & MCI_RXDATAAVLBLMASK) || i >= 100)
-			break;
-		udelay(10);
-	}
-
-	/*
-	 * Check to see whether we still have some data left in the FIFO -
-	 * this catches DMA controllers which are unable to monitor the
-	 * DMALBREQ and DMALSREQ signals while allowing us to DMA to non-
-	 * contiguous buffers.  On TX, we'll get a FIFO underrun error.
-	 */
-	if (status & MCI_RXDATAAVLBLMASK) {
-		mmci_dma_data_error(host);
-		if (!data->error)
-			data->error = -EIO;
-	}
-
-	if (!data->host_cookie)
-		mmci_dma_unmap(host, data);
-
-	/*
-	 * Use of DMA with scatter-gather is impossible.
-	 * Give up with DMA and switch back to PIO mode.
-	 */
-	if (status & MCI_RXDATAAVLBLMASK) {
-		dev_err(mmc_dev(host->mmc), "buggy DMA detected. Taking evasive action.\n");
-		mmci_dma_release(host);
-	}
-
-	host->dma_in_progress = false;
-	host->dma_current = NULL;
-	host->dma_desc_current = NULL;
-}
-
-/* prepares DMA channel and DMA descriptor, returns non-zero on failure */
-static int __mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data,
-				struct dma_chan **dma_chan,
-				struct dma_async_tx_descriptor **dma_desc)
-{
-	struct variant_data *variant = host->variant;
-	struct dma_slave_config conf = {
-		.src_addr = host->phybase + MMCIFIFO,
-		.dst_addr = host->phybase + MMCIFIFO,
-		.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
-		.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
-		.src_maxburst = variant->fifohalfsize >> 2, /* # of words */
-		.dst_maxburst = variant->fifohalfsize >> 2, /* # of words */
-		.device_fc = false,
-	};
-	struct dma_chan *chan;
-	struct dma_device *device;
-	struct dma_async_tx_descriptor *desc;
-	int nr_sg;
-	unsigned long flags = DMA_CTRL_ACK;
-
-	if (data->flags & MMC_DATA_READ) {
-		conf.direction = DMA_DEV_TO_MEM;
-		chan = host->dma_rx_channel;
-	} else {
-		conf.direction = DMA_MEM_TO_DEV;
-		chan = host->dma_tx_channel;
-	}
-
-	/* If there's no DMA channel, fall back to PIO */
-	if (!chan)
-		return -EINVAL;
-
-	/* If less than or equal to the fifo size, don't bother with DMA */
-	if (data->blksz * data->blocks <= variant->fifosize)
-		return -EINVAL;
-
-	device = chan->device;
-	nr_sg = dma_map_sg(device->dev, data->sg, data->sg_len,
-			   mmc_get_dma_dir(data));
-	if (nr_sg == 0)
-		return -EINVAL;
-
-	if (host->variant->qcom_dml)
-		flags |= DMA_PREP_INTERRUPT;
-
-	dmaengine_slave_config(chan, &conf);
-	desc = dmaengine_prep_slave_sg(chan, data->sg, nr_sg,
-					    conf.direction, flags);
-	if (!desc)
-		goto unmap_exit;
-
-	*dma_chan = chan;
-	*dma_desc = desc;
-
-	return 0;
-
- unmap_exit:
-	dma_unmap_sg(device->dev, data->sg, data->sg_len,
-		     mmc_get_dma_dir(data));
-	return -ENOMEM;
-}
-
-static inline int mmci_dma_prep_data(struct mmci_host *host,
-				     struct mmc_data *data)
-{
-	/* Check if next job is already prepared. */
-	if (host->dma_current && host->dma_desc_current)
-		return 0;
-
-	/* No job were prepared thus do it now. */
-	return __mmci_dma_prep_data(host, data, &host->dma_current,
-				    &host->dma_desc_current);
-}
-
-static inline int mmci_dma_prep_next(struct mmci_host *host,
-				     struct mmc_data *data)
-{
-	struct mmci_host_next *nd = &host->next_data;
-	return __mmci_dma_prep_data(host, data, &nd->dma_chan, &nd->dma_desc);
-}
-
-static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
-{
-	int ret;
-	struct mmc_data *data = host->data;
-
-	ret = mmci_dma_prep_data(host, host->data);
-	if (ret)
-		return ret;
-
-	/* Okay, go for it. */
-	dev_vdbg(mmc_dev(host->mmc),
-		 "Submit MMCI DMA job, sglen %d blksz %04x blks %04x flags %08x\n",
-		 data->sg_len, data->blksz, data->blocks, data->flags);
-	host->dma_in_progress = true;
-	dmaengine_submit(host->dma_desc_current);
-	dma_async_issue_pending(host->dma_current);
-
-	if (host->variant->qcom_dml)
-		dml_start_xfer(host, data);
-
-	datactrl |= MCI_DPSM_DMAENABLE;
-
-	/* Trigger the DMA transfer */
-	mmci_write_datactrlreg(host, datactrl);
-
-	/*
-	 * Let the MMCI say when the data is ended and it's time
-	 * to fire next DMA request. When that happens, MMCI will
-	 * call mmci_data_end()
-	 */
-	writel(readl(host->base + MMCIMASK0) | MCI_DATAENDMASK,
-	       host->base + MMCIMASK0);
-	return 0;
-}
-
-static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data)
-{
-	struct mmci_host_next *next = &host->next_data;
-
-	WARN_ON(data->host_cookie && data->host_cookie != next->cookie);
-	WARN_ON(!data->host_cookie && (next->dma_desc || next->dma_chan));
-
-	host->dma_desc_current = next->dma_desc;
-	host->dma_current = next->dma_chan;
-	next->dma_desc = NULL;
-	next->dma_chan = NULL;
-}
-
 static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq)
 {
 	struct mmci_host *host = mmc_priv(mmc);
 	struct mmc_data *data = mrq->data;
-	struct mmci_host_next *nd = &host->next_data;
 
 	if (!data)
 		return;
@@ -764,8 +431,7 @@ static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq)
 	if (mmci_validate_data(host, data))
 		return;
 
-	if (!mmci_dma_prep_next(host, data))
-		data->host_cookie = ++nd->cookie < 0 ? 1 : nd->cookie;
+	mmci_dma_pre_req(host, data);
 }
 
 static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq,
@@ -777,67 +443,9 @@ static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq,
 	if (!data || !data->host_cookie)
 		return;
 
-	mmci_dma_unmap(host, data);
-
-	if (err) {
-		struct mmci_host_next *next = &host->next_data;
-		struct dma_chan *chan;
-		if (data->flags & MMC_DATA_READ)
-			chan = host->dma_rx_channel;
-		else
-			chan = host->dma_tx_channel;
-		dmaengine_terminate_all(chan);
-
-		if (host->dma_desc_current == next->dma_desc)
-			host->dma_desc_current = NULL;
-
-		if (host->dma_current == next->dma_chan) {
-			host->dma_in_progress = false;
-			host->dma_current = NULL;
-		}
-
-		next->dma_desc = NULL;
-		next->dma_chan = NULL;
-		data->host_cookie = 0;
-	}
-}
-
-#else
-/* Blank functions if the DMA engine is not available */
-static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data)
-{
-}
-static inline void mmci_dma_setup(struct mmci_host *host)
-{
-}
-
-static inline void mmci_dma_release(struct mmci_host *host)
-{
-}
-
-static inline void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data)
-{
-}
-
-static inline void mmci_dma_finalize(struct mmci_host *host,
-				     struct mmc_data *data)
-{
-}
-
-static inline void mmci_dma_data_error(struct mmci_host *host)
-{
-}
-
-static inline int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
-{
-	return -ENOSYS;
+	mmci_dma_post_req(host, data, err);
 }
 
-#define mmci_pre_request NULL
-#define mmci_post_request NULL
-
-#endif
-
 static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
 {
 	struct variant_data *variant = host->variant;
@@ -904,7 +512,7 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
 	 * Attempt to use DMA operation mode, if this
 	 * should fail, fall back to PIO mode
 	 */
-	if (!mmci_dma_start_data(host, datactrl))
+	if (!mmci_dma_start(host, datactrl))
 		return;
 
 	/* IRQ mode, map the SG list for CPU reading/writing */
@@ -979,10 +587,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
 		u32 remain, success;
 
 		/* Terminate the DMA transfer */
-		if (dma_inprogress(host)) {
-			mmci_dma_data_error(host);
-			mmci_dma_unmap(host, data);
-		}
+		mmci_dma_error(host);
 
 		/*
 		 * Calculate how far we are into the transfer.  Note that
@@ -1020,8 +625,8 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
 		dev_err(mmc_dev(host->mmc), "stray MCI_DATABLOCKEND interrupt\n");
 
 	if (status & MCI_DATAEND || data->error) {
-		if (dma_inprogress(host))
-			mmci_dma_finalize(host, data);
+		mmci_dma_finalize(host, data);
+
 		mmci_stop_data(host);
 
 		if (!data->error)
@@ -1128,10 +733,8 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
 	if ((!sbc && !cmd->data) || cmd->error) {
 		if (host->data) {
 			/* Terminate the DMA transfer */
-			if (dma_inprogress(host)) {
-				mmci_dma_data_error(host);
-				mmci_dma_unmap(host, host->data);
-			}
+			mmci_dma_error(host);
+
 			mmci_stop_data(host);
 		}
 		mmci_request_end(host, host->mrq);
@@ -1399,7 +1002,7 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 	host->mrq = mrq;
 
 	if (mrq->data)
-		mmci_get_next_data(host, mrq->data);
+		mmci_dma_get_next_data(host, mrq->data);
 
 	if (mrq->data && mrq->data->flags & MMC_DATA_READ)
 		mmci_start_data(host, mrq->data);
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index f91cdf7..a73bb98 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -195,13 +195,81 @@
 #define MMCI_PINCTRL_STATE_OPENDRAIN "opendrain"
 
 struct clk;
-struct variant_data;
 struct dma_chan;
 
-struct mmci_host_next {
-	struct dma_async_tx_descriptor	*dma_desc;
-	struct dma_chan			*dma_chan;
-	s32				cookie;
+/**
+ * struct variant_data - MMCI variant-specific quirks
+ * @clkreg: default value for MCICLOCK register
+ * @clkreg_enable: enable value for MMCICLOCK register
+ * @clkreg_8bit_bus_enable: enable value for 8 bit bus
+ * @clkreg_neg_edge_enable: enable value for inverted data/cmd output
+ * @datalength_bits: number of bits in the MMCIDATALENGTH register
+ * @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY
+ *	      is asserted (likewise for RX)
+ * @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY
+ *		  is asserted (likewise for RX)
+ * @data_cmd_enable: enable value for data commands.
+ * @st_sdio: enable ST specific SDIO logic
+ * @st_clkdiv: true if using a ST-specific clock divider algorithm
+ * @datactrl_mask_ddrmode: ddr mode mask in datactrl register.
+ * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl
+ *                    register
+ * @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl
+ *		     register
+ * @datactrl_mask_sdio: SDIO enable mask in datactrl register
+ * @pwrreg_powerup: power up value for MMCIPOWER register
+ * @f_max: maximum clk frequency supported by the controller.
+ * @signal_direction: input/out direction of bus signals can be indicated
+ * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock
+ * @busy_detect: true if the variant supports busy detection on DAT0.
+ * @busy_dpsm_flag: bitmask enabling busy detection in the DPSM
+ * @busy_detect_flag: bitmask identifying the bit in the MMCISTATUS register
+ *		      indicating that the card is busy
+ * @busy_detect_mask: bitmask identifying the bit in the MMCIMASK0 to mask for
+ *		      getting busy end detection interrupts
+ * @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply
+ * @explicit_mclk_control: enable explicit mclk control in driver.
+ * @qcom_fifo: enables qcom specific fifo pio read logic.
+ * @qcom_dml: enables qcom specific dma glue for dma transfers.
+ * @reversed_irq_handling: handle data irq before cmd irq.
+ * @mmcimask1: true if variant have a MMCIMASK1 register.
+ * @start_err: bitmask identifying the STARTBITERR bit inside MMCISTATUS
+ *	       register.
+ * @opendrain: bitmask identifying the OPENDRAIN bit inside MMCIPOWER register
+ * @mmci_dma: Pointer to platform-specific DMA callbacks.
+ */
+struct variant_data {
+	unsigned int		clkreg;
+	unsigned int		clkreg_enable;
+	unsigned int		clkreg_8bit_bus_enable;
+	unsigned int		clkreg_neg_edge_enable;
+	unsigned int		datalength_bits;
+	unsigned int		fifosize;
+	unsigned int		fifohalfsize;
+	unsigned int		data_cmd_enable;
+	unsigned int		datactrl_mask_ddrmode;
+	unsigned int		datactrl_mask_sdio;
+	bool			st_sdio;
+	bool			st_clkdiv;
+	bool			blksz_datactrl16;
+	bool			blksz_datactrl4;
+	u32			pwrreg_powerup;
+	u32			f_max;
+	bool			signal_direction;
+	bool			pwrreg_clkgate;
+	bool			busy_detect;
+	u32			busy_dpsm_flag;
+	u32			busy_detect_flag;
+	u32			busy_detect_mask;
+	bool			pwrreg_nopower;
+	bool			explicit_mclk_control;
+	bool			qcom_fifo;
+	bool			qcom_dml;
+	bool			reversed_irq_handling;
+	bool			mmcimask1;
+	u32			start_err;
+	u32			opendrain;
+	struct mmci_dma_ops	*mmci_dma;
 };
 
 struct mmci_host {
@@ -244,18 +312,9 @@ struct mmci_host {
 	unsigned int		size;
 	int (*get_rx_fifocnt)(struct mmci_host *h, u32 status, int remain);
 
-#ifdef CONFIG_DMA_ENGINE
-	/* DMA stuff */
-	struct dma_chan		*dma_current;
-	struct dma_chan		*dma_rx_channel;
-	struct dma_chan		*dma_tx_channel;
-	struct dma_async_tx_descriptor	*dma_desc_current;
-	struct mmci_host_next	next_data;
-	bool			dma_in_progress;
-
-#define dma_inprogress(host)	((host)->dma_in_progress)
-#else
-#define dma_inprogress(host)	(0)
-#endif
+	void			*dma_priv;
 };
 
+extern struct mmci_dma_ops dmaengine;
+
+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
new file mode 100644
index 0000000..98a542d
--- /dev/null
+++ b/drivers/mmc/host/mmci_dma.c
@@ -0,0 +1,450 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Ludovic.barre@st.com for STMicroelectronics.
+ */
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/scatterlist.h>
+
+#include "mmci.h"
+#include "mmci_dma.h"
+#include "mmci_qcom_dml.h"
+
+int mmci_dma_setup(struct mmci_host *host)
+{
+	struct mmci_dma_ops *mmci_dma = host->variant->mmci_dma;
+
+	if (mmci_dma && mmci_dma->setup)
+		return mmci_dma->setup(host);
+
+	return 0;
+}
+
+void mmci_dma_release(struct mmci_host *host)
+{
+	struct mmci_dma_ops *mmci_dma = host->variant->mmci_dma;
+
+	if (mmci_dma && mmci_dma->release)
+		mmci_dma->release(host);
+}
+
+void mmci_dma_pre_req(struct mmci_host *host, struct mmc_data *data)
+{
+	struct mmci_dma_ops *mmci_dma = host->variant->mmci_dma;
+
+	if (mmci_dma && mmci_dma->pre_req)
+		mmci_dma->pre_req(host, data);
+}
+
+int mmci_dma_start(struct mmci_host *host, unsigned int datactrl)
+{
+	struct mmci_dma_ops *mmci_dma = host->variant->mmci_dma;
+
+	if (mmci_dma && mmci_dma->start)
+		return mmci_dma->start(host, datactrl);
+
+	return -EINVAL;
+}
+
+void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data)
+{
+	struct mmci_dma_ops *mmci_dma = host->variant->mmci_dma;
+
+	if (mmci_dma && mmci_dma->finalize)
+		mmci_dma->finalize(host, data);
+}
+
+void mmci_dma_post_req(struct mmci_host *host,
+		       struct mmc_data *data, int err)
+{
+	struct mmci_dma_ops *mmci_dma = host->variant->mmci_dma;
+
+	if (mmci_dma && mmci_dma->post_req)
+		mmci_dma->post_req(host, data, err);
+}
+
+void mmci_dma_error(struct mmci_host *host)
+{
+	struct mmci_dma_ops *mmci_dma = host->variant->mmci_dma;
+
+	if (mmci_dma && mmci_dma->error)
+		mmci_dma->error(host);
+}
+
+void mmci_dma_get_next_data(struct mmci_host *host, struct mmc_data *data)
+{
+	struct mmci_dma_ops *mmci_dma = host->variant->mmci_dma;
+
+	if (mmci_dma && mmci_dma->get_next_data)
+		mmci_dma->get_next_data(host, data);
+}
+
+#ifdef CONFIG_DMA_ENGINE
+struct dmaengine_next {
+	struct dma_async_tx_descriptor *dma_desc;
+	struct dma_chan	*dma_chan;
+	s32 cookie;
+};
+
+struct dmaengine_priv {
+	struct dma_chan	*dma_current;
+	struct dma_chan	*dma_rx_channel;
+	struct dma_chan	*dma_tx_channel;
+	struct dma_async_tx_descriptor	*dma_desc_current;
+	struct dmaengine_next next_data;
+	bool dma_in_progress;
+};
+
+#define dma_inprogress(dmae) ((dmae)->dma_in_progress)
+
+static int dmaengine_setup(struct mmci_host *host)
+{
+	const char *rxname, *txname;
+	struct variant_data *variant = host->variant;
+	struct dmaengine_priv *dmae;
+
+	dmae = devm_kzalloc(mmc_dev(host->mmc), sizeof(*dmae), GFP_KERNEL);
+	if (!dmae)
+		return -ENOMEM;
+
+	host->dma_priv = dmae;
+
+	dmae->dma_rx_channel = dma_request_slave_channel(mmc_dev(host->mmc),
+							 "rx");
+	dmae->dma_tx_channel = dma_request_slave_channel(mmc_dev(host->mmc),
+							 "tx");
+
+	/* initialize pre request cookie */
+	dmae->next_data.cookie = 1;
+
+	/*
+	 * If only an RX channel is specified, the driver will
+	 * attempt to use it bidirectionally, however if it is
+	 * is specified but cannot be located, DMA will be disabled.
+	 */
+	if (dmae->dma_rx_channel && !dmae->dma_tx_channel)
+		dmae->dma_tx_channel = dmae->dma_rx_channel;
+
+	if (dmae->dma_rx_channel)
+		rxname = dma_chan_name(dmae->dma_rx_channel);
+	else
+		rxname = "none";
+
+	if (dmae->dma_tx_channel)
+		txname = dma_chan_name(dmae->dma_tx_channel);
+	else
+		txname = "none";
+
+	dev_info(mmc_dev(host->mmc), "DMA channels RX %s, TX %s\n",
+		 rxname, txname);
+
+	/*
+	 * Limit the maximum segment size in any SG entry according to
+	 * the parameters of the DMA engine device.
+	 */
+	if (dmae->dma_tx_channel) {
+		struct device *dev = dmae->dma_tx_channel->device->dev;
+		unsigned int max_seg_size = dma_get_max_seg_size(dev);
+
+		if (max_seg_size < host->mmc->max_seg_size)
+			host->mmc->max_seg_size = max_seg_size;
+	}
+	if (dmae->dma_rx_channel) {
+		struct device *dev = dmae->dma_rx_channel->device->dev;
+		unsigned int max_seg_size = dma_get_max_seg_size(dev);
+
+		if (max_seg_size < host->mmc->max_seg_size)
+			host->mmc->max_seg_size = max_seg_size;
+	}
+
+	if (variant->qcom_dml && dmae->dma_rx_channel && dmae->dma_tx_channel)
+		if (dml_hw_init(host, host->mmc->parent->of_node))
+			variant->qcom_dml = false;
+
+	return 0;
+}
+
+static inline void dmaengine_release(struct mmci_host *host)
+{
+	struct dmaengine_priv *dmae = host->dma_priv;
+
+	if (dmae->dma_rx_channel)
+		dma_release_channel(dmae->dma_rx_channel);
+	if (dmae->dma_tx_channel)
+		dma_release_channel(dmae->dma_tx_channel);
+
+	dmae->dma_rx_channel = dmae->dma_tx_channel = NULL;
+}
+
+static void dmaengine_unmap(struct mmci_host *host, struct mmc_data *data)
+{
+	struct dmaengine_priv *dmae = host->dma_priv;
+	struct dma_chan *chan;
+
+	if (data->flags & MMC_DATA_READ)
+		chan = dmae->dma_rx_channel;
+	else
+		chan = dmae->dma_tx_channel;
+
+	dma_unmap_sg(chan->device->dev, data->sg, data->sg_len,
+		     mmc_get_dma_dir(data));
+}
+
+static void dmaengine_error(struct mmci_host *host)
+{
+	struct dmaengine_priv *dmae = host->dma_priv;
+
+	if (!dma_inprogress(dmae))
+		return;
+
+	dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n");
+	dmaengine_terminate_all(dmae->dma_current);
+	dmae->dma_in_progress = false;
+	dmae->dma_current = NULL;
+	dmae->dma_desc_current = NULL;
+	host->data->host_cookie = 0;
+
+	dmaengine_unmap(host, host->data);
+}
+
+static void dmaengine_finalize(struct mmci_host *host, struct mmc_data *data)
+{
+	struct dmaengine_priv *dmae = host->dma_priv;
+	u32 status;
+	int i;
+
+	if (!dma_inprogress(dmae))
+		return;
+
+	/* Wait up to 1ms for the DMA to complete */
+	for (i = 0; ; i++) {
+		status = readl(host->base + MMCISTATUS);
+		if (!(status & MCI_RXDATAAVLBLMASK) || i >= 100)
+			break;
+		udelay(10);
+	}
+
+	/*
+	 * Check to see whether we still have some data left in the FIFO -
+	 * this catches DMA controllers which are unable to monitor the
+	 * DMALBREQ and DMALSREQ signals while allowing us to DMA to non-
+	 * contiguous buffers.  On TX, we'll get a FIFO underrun error.
+	 */
+	if (status & MCI_RXDATAAVLBLMASK) {
+		dmaengine_error(host);
+		if (!data->error)
+			data->error = -EIO;
+	}
+
+	if (!data->host_cookie)
+		dmaengine_unmap(host, data);
+
+	/*
+	 * Use of DMA with scatter-gather is impossible.
+	 * Give up with DMA and switch back to PIO mode.
+	 */
+	if (status & MCI_RXDATAAVLBLMASK) {
+		dev_err(mmc_dev(host->mmc),
+			"buggy DMA detected. Taking evasive action.\n");
+		dmaengine_release(host);
+	}
+
+	dmae->dma_in_progress = false;
+	dmae->dma_current = NULL;
+	dmae->dma_desc_current = NULL;
+}
+
+/* prepares DMA channel and DMA descriptor, returns non-zero on failure */
+static int __dmaengine_prep_data(struct mmci_host *host, struct mmc_data *data,
+				 struct dma_chan **dma_chan,
+				 struct dma_async_tx_descriptor **dma_desc)
+{
+	struct dmaengine_priv *dmae = host->dma_priv;
+	struct variant_data *variant = host->variant;
+	struct dma_slave_config conf = {
+		.src_addr = host->phybase + MMCIFIFO,
+		.dst_addr = host->phybase + MMCIFIFO,
+		.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+		.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+		.src_maxburst = variant->fifohalfsize >> 2, /* # of words */
+		.dst_maxburst = variant->fifohalfsize >> 2, /* # of words */
+		.device_fc = false,
+	};
+	struct dma_chan *chan;
+	struct dma_device *device;
+	struct dma_async_tx_descriptor *desc;
+	int nr_sg;
+	unsigned long flags = DMA_CTRL_ACK;
+
+	if (data->flags & MMC_DATA_READ) {
+		conf.direction = DMA_DEV_TO_MEM;
+		chan = dmae->dma_rx_channel;
+	} else {
+		conf.direction = DMA_MEM_TO_DEV;
+		chan = dmae->dma_tx_channel;
+	}
+
+	/* If there's no DMA channel, fall back to PIO */
+	if (!chan)
+		return -EINVAL;
+
+	/* If less than or equal to the fifo size, don't bother with DMA */
+	if (data->blksz * data->blocks <= variant->fifosize)
+		return -EINVAL;
+
+	device = chan->device;
+	nr_sg = dma_map_sg(device->dev, data->sg, data->sg_len,
+			   mmc_get_dma_dir(data));
+	if (nr_sg == 0)
+		return -EINVAL;
+
+	if (host->variant->qcom_dml)
+		flags |= DMA_PREP_INTERRUPT;
+
+	dmaengine_slave_config(chan, &conf);
+	desc = dmaengine_prep_slave_sg(chan, data->sg, nr_sg,
+				       conf.direction, flags);
+	if (!desc)
+		goto unmap_exit;
+
+	*dma_chan = chan;
+	*dma_desc = desc;
+
+	return 0;
+
+ unmap_exit:
+	dmaengine_unmap(host, data);
+	return -ENOMEM;
+}
+
+static inline int dmaengine_prep_data(struct mmci_host *host,
+				      struct mmc_data *data)
+{
+	struct dmaengine_priv *dmae = host->dma_priv;
+
+	/* Check if next job is already prepared. */
+	if (dmae->dma_current && dmae->dma_desc_current)
+		return 0;
+
+	/* No job were prepared thus do it now. */
+	return __dmaengine_prep_data(host, data, &dmae->dma_current,
+				     &dmae->dma_desc_current);
+}
+
+static inline int dmaengine_prep_next(struct mmci_host *host,
+				      struct mmc_data *data)
+{
+	struct dmaengine_priv *dmae = host->dma_priv;
+	struct dmaengine_next *nd = &dmae->next_data;
+
+	return __dmaengine_prep_data(host, data, &nd->dma_chan, &nd->dma_desc);
+}
+
+static int dmaengine_start(struct mmci_host *host, unsigned int datactrl)
+{
+	struct dmaengine_priv *dmae = host->dma_priv;
+	struct mmc_data *data = host->data;
+	int ret;
+
+	ret = dmaengine_prep_data(host, host->data);
+	if (ret)
+		return ret;
+
+	/* Okay, go for it. */
+	dev_vdbg(mmc_dev(host->mmc),
+		 "Submit MMCI DMA job, sglen %d blksz %04x blks %04x flags %08x\n",
+		 data->sg_len, data->blksz, data->blocks, data->flags);
+	dmae->dma_in_progress = true;
+	dmaengine_submit(dmae->dma_desc_current);
+	dma_async_issue_pending(dmae->dma_current);
+
+	if (host->variant->qcom_dml)
+		dml_start_xfer(host, data);
+
+	datactrl |= MCI_DPSM_DMAENABLE;
+
+	/* Trigger the DMA transfer */
+	mmci_write_datactrlreg(host, datactrl);
+
+	/*
+	 * Let the MMCI say when the data is ended and it's time
+	 * to fire next DMA request. When that happens, MMCI will
+	 * call mmci_data_end()
+	 */
+	writel(readl(host->base + MMCIMASK0) | MCI_DATAENDMASK,
+	       host->base + MMCIMASK0);
+	return 0;
+}
+
+static void dmaengine_get_next_data(struct mmci_host *host,
+				    struct mmc_data *data)
+{
+	struct dmaengine_priv *dmae = host->dma_priv;
+	struct dmaengine_next *next = &dmae->next_data;
+
+	WARN_ON(data->host_cookie && data->host_cookie != next->cookie);
+	WARN_ON(!data->host_cookie && (next->dma_desc || next->dma_chan));
+
+	dmae->dma_desc_current = next->dma_desc;
+	dmae->dma_current = next->dma_chan;
+	next->dma_desc = NULL;
+	next->dma_chan = NULL;
+}
+
+static void dmaengine_pre_req(struct mmci_host *host, struct mmc_data *data)
+{
+	struct dmaengine_priv *dmae = host->dma_priv;
+	struct dmaengine_next *nd = &dmae->next_data;
+
+	if (!dmaengine_prep_next(host, data))
+		data->host_cookie = ++nd->cookie < 0 ? 1 : nd->cookie;
+}
+
+static void dmaengine_post_req(struct mmci_host *host,
+			       struct mmc_data *data, int err)
+{
+	struct dmaengine_priv *dmae = host->dma_priv;
+
+	dmaengine_unmap(host, data);
+
+	if (err) {
+		struct dmaengine_next *next = &dmae->next_data;
+		struct dma_chan *chan;
+
+		if (data->flags & MMC_DATA_READ)
+			chan = dmae->dma_rx_channel;
+		else
+			chan = dmae->dma_tx_channel;
+		dmaengine_terminate_all(chan);
+
+		if (dmae->dma_desc_current == next->dma_desc)
+			dmae->dma_desc_current = NULL;
+
+		if (dmae->dma_current == next->dma_chan) {
+			dmae->dma_in_progress = false;
+			dmae->dma_current = NULL;
+		}
+
+		next->dma_desc = NULL;
+		next->dma_chan = NULL;
+		data->host_cookie = 0;
+	}
+}
+
+struct mmci_dma_ops dmaengine = {
+	.setup = dmaengine_setup,
+	.release = dmaengine_release,
+	.pre_req = dmaengine_pre_req,
+	.start = dmaengine_start,
+	.finalize = dmaengine_finalize,
+	.post_req = dmaengine_post_req,
+	.error = dmaengine_error,
+	.get_next_data = dmaengine_get_next_data,
+};
+#else
+struct mmci_dma_ops dmaengine = {};
+#endif
diff --git a/drivers/mmc/host/mmci_dma.h b/drivers/mmc/host/mmci_dma.h
new file mode 100644
index 0000000..33e4e8a
--- /dev/null
+++ b/drivers/mmc/host/mmci_dma.h
@@ -0,0 +1,31 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Ludovic.barre@st.com for STMicroelectronics.
+ */
+#ifndef __MMC_DMA_H__
+#define __MMC_DMA_H__
+
+struct mmci_dma_ops {
+	int (*setup)(struct mmci_host *host);
+	void (*release)(struct mmci_host *host);
+	void (*pre_req)(struct mmci_host *host, struct mmc_data *data);
+	int (*start)(struct mmci_host *host, unsigned int datactrl);
+	void (*finalize)(struct mmci_host *host, struct mmc_data *data);
+	void (*post_req)(struct mmci_host *host,
+			 struct mmc_data *data, int err);
+	void (*error)(struct mmci_host *host);
+	void (*get_next_data)(struct mmci_host *host, struct mmc_data *data);
+};
+
+int mmci_dma_setup(struct mmci_host *host);
+int mmci_dma_start(struct mmci_host *host, unsigned int datactrl);
+void mmci_dma_release(struct mmci_host *host);
+void mmci_dma_pre_req(struct mmci_host *host, struct mmc_data *data);
+void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data);
+void mmci_dma_post_req(struct mmci_host *host,
+		       struct mmc_data *data, int err);
+void mmci_dma_error(struct mmci_host *host);
+void mmci_dma_get_next_data(struct mmci_host *host, struct mmc_data *data);
+
+#endif /* __MMC_DMA_H__ */
-- 
2.7.4


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 02/19] mmc: mmci: merge qcom dml feature into mmci dma
  2018-06-12 13:14 [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
  2018-06-12 13:14 ` [PATCH 01/19] mmc: mmci: regroup and define dma operations Ludovic Barre
@ 2018-06-12 13:14 ` Ludovic Barre
  2018-07-05 15:26   ` Ulf Hansson
  2018-06-12 13:14 ` [PATCH 03/19] mmc: mmci: add datactrl block size variant property Ludovic Barre
                   ` (17 subsequent siblings)
  19 siblings, 1 reply; 41+ messages in thread
From: Ludovic Barre @ 2018-06-12 13:14 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	linux-arm-kernel, linux-kernel, devicetree, linux-mmc,
	Ludovic Barre

From: Ludovic Barre <ludovic.barre@st.com>

This patch integrates qcom dml feature into mmci_dma file.
Qualcomm Data Mover lite/local is already a variant of mmci dmaengine.

Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
---
 drivers/mmc/host/Makefile        |   1 -
 drivers/mmc/host/mmci.c          |   1 -
 drivers/mmc/host/mmci.h          |  35 ++++++++
 drivers/mmc/host/mmci_dma.c      | 135 ++++++++++++++++++++++++++++-
 drivers/mmc/host/mmci_qcom_dml.c | 177 ---------------------------------------
 drivers/mmc/host/mmci_qcom_dml.h |  31 -------
 6 files changed, 169 insertions(+), 211 deletions(-)
 delete mode 100644 drivers/mmc/host/mmci_qcom_dml.c
 delete mode 100644 drivers/mmc/host/mmci_qcom_dml.h

diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index daecaa98..608a020 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -5,7 +5,6 @@
 
 obj-$(CONFIG_MMC_ARMMMCI) += armmmci.o
 armmmci-y := mmci.o mmci_dma.o
-armmmci-$(CONFIG_MMC_QCOM_DML) += mmci_qcom_dml.o
 obj-$(CONFIG_MMC_PXA)		+= pxamci.o
 obj-$(CONFIG_MMC_MXC)		+= mxcmmc.o
 obj-$(CONFIG_MMC_MXS)		+= mxs-mmc.o
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 8868be0..7a15afd 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -43,7 +43,6 @@
 
 #include "mmci.h"
 #include "mmci_dma.h"
-#include "mmci_qcom_dml.h"
 
 #define DRIVER_NAME "mmci-pl18x"
 
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index a73bb98..f7cba35 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -194,6 +194,41 @@
 
 #define MMCI_PINCTRL_STATE_OPENDRAIN "opendrain"
 
+/* QCOM DML Registers */
+#define DML_CONFIG			0x00
+#define PRODUCER_CRCI_MSK		GENMASK(1, 0)
+#define PRODUCER_CRCI_DISABLE		0
+#define PRODUCER_CRCI_X_SEL		BIT(0)
+#define PRODUCER_CRCI_Y_SEL		BIT(1)
+#define CONSUMER_CRCI_MSK		GENMASK(3, 2)
+#define CONSUMER_CRCI_DISABLE		0
+#define CONSUMER_CRCI_X_SEL		BIT(2)
+#define CONSUMER_CRCI_Y_SEL		BIT(3)
+#define PRODUCER_TRANS_END_EN		BIT(4)
+#define BYPASS				BIT(16)
+#define DIRECT_MODE			BIT(17)
+#define INFINITE_CONS_TRANS		BIT(18)
+
+#define DML_SW_RESET			0x08
+#define DML_PRODUCER_START		0x0c
+#define DML_CONSUMER_START		0x10
+#define DML_PRODUCER_PIPE_LOGICAL_SIZE	0x14
+#define DML_CONSUMER_PIPE_LOGICAL_SIZE	0x18
+#define DML_PIPE_ID			0x1c
+#define PRODUCER_PIPE_ID_SHFT		0
+#define PRODUCER_PIPE_ID_MSK		GENMASK(4, 0)
+#define CONSUMER_PIPE_ID_SHFT		16
+#define CONSUMER_PIPE_ID_MSK		GENMASK(20, 16)
+
+#define DML_PRODUCER_BAM_BLOCK_SIZE	0x24
+#define DML_PRODUCER_BAM_TRANS_SIZE	0x28
+
+/* other definitions */
+#define PRODUCER_PIPE_LOGICAL_SIZE	4096
+#define CONSUMER_PIPE_LOGICAL_SIZE	4096
+
+#define DML_OFFSET			0x800
+
 struct clk;
 struct dma_chan;
 
diff --git a/drivers/mmc/host/mmci_dma.c b/drivers/mmc/host/mmci_dma.c
index 98a542d..dd7dae5 100644
--- a/drivers/mmc/host/mmci_dma.c
+++ b/drivers/mmc/host/mmci_dma.c
@@ -8,11 +8,11 @@
 #include <linux/dmaengine.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/card.h>
+#include <linux/of.h>
 #include <linux/scatterlist.h>
 
 #include "mmci.h"
 #include "mmci_dma.h"
-#include "mmci_qcom_dml.h"
 
 int mmci_dma_setup(struct mmci_host *host)
 {
@@ -101,6 +101,139 @@ struct dmaengine_priv {
 
 #define dma_inprogress(dmae) ((dmae)->dma_in_progress)
 
+#ifdef CONFIG_MMC_QCOM_DML
+void dml_start_xfer(struct mmci_host *host, struct mmc_data *data)
+{
+	u32 config;
+	void __iomem *base = host->base + DML_OFFSET;
+
+	if (data->flags & MMC_DATA_READ) {
+		/* Read operation: configure DML for producer operation */
+		/* Set producer CRCI-x and disable consumer CRCI */
+		config = readl_relaxed(base + DML_CONFIG);
+		config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_X_SEL;
+		config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_DISABLE;
+		writel_relaxed(config, base + DML_CONFIG);
+
+		/* Set the Producer BAM block size */
+		writel_relaxed(data->blksz, base + DML_PRODUCER_BAM_BLOCK_SIZE);
+
+		/* Set Producer BAM Transaction size */
+		writel_relaxed(data->blocks * data->blksz,
+			       base + DML_PRODUCER_BAM_TRANS_SIZE);
+		/* Set Producer Transaction End bit */
+		config = readl_relaxed(base + DML_CONFIG);
+		config |= PRODUCER_TRANS_END_EN;
+		writel_relaxed(config, base + DML_CONFIG);
+		/* Trigger producer */
+		writel_relaxed(1, base + DML_PRODUCER_START);
+	} else {
+		/* Write operation: configure DML for consumer operation */
+		/* Set consumer CRCI-x and disable producer CRCI*/
+		config = readl_relaxed(base + DML_CONFIG);
+		config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_X_SEL;
+		config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_DISABLE;
+		writel_relaxed(config, base + DML_CONFIG);
+		/* Clear Producer Transaction End bit */
+		config = readl_relaxed(base + DML_CONFIG);
+		config &= ~PRODUCER_TRANS_END_EN;
+		writel_relaxed(config, base + DML_CONFIG);
+		/* Trigger consumer */
+		writel_relaxed(1, base + DML_CONSUMER_START);
+	}
+
+	/* make sure the dml is configured before dma is triggered */
+	wmb();
+}
+
+static int of_get_dml_pipe_index(struct device_node *np, const char *name)
+{
+	int index;
+	struct of_phandle_args dma_spec;
+
+	index = of_property_match_string(np, "dma-names", name);
+
+	if (index < 0)
+		return -ENODEV;
+
+	if (of_parse_phandle_with_args(np, "dmas", "#dma-cells", index,
+				       &dma_spec))
+		return -ENODEV;
+
+	if (dma_spec.args_count)
+		return dma_spec.args[0];
+
+	return -ENODEV;
+}
+
+/* Initialize the dml hardware connected to SD Card controller */
+int dml_hw_init(struct mmci_host *host, struct device_node *np)
+{
+	u32 config;
+	void __iomem *base;
+	int consumer_id, producer_id;
+
+	consumer_id = of_get_dml_pipe_index(np, "tx");
+	producer_id = of_get_dml_pipe_index(np, "rx");
+
+	if (producer_id < 0 || consumer_id < 0)
+		return -ENODEV;
+
+	base = host->base + DML_OFFSET;
+
+	/* Reset the DML block */
+	writel_relaxed(1, base + DML_SW_RESET);
+
+	/* Disable the producer and consumer CRCI */
+	config = (PRODUCER_CRCI_DISABLE | CONSUMER_CRCI_DISABLE);
+	/*
+	 * Disable the bypass mode. Bypass mode will only be used
+	 * if data transfer is to happen in PIO mode and don't
+	 * want the BAM interface to connect with SDCC-DML.
+	 */
+	config &= ~BYPASS;
+	/*
+	 * Disable direct mode as we don't DML to MASTER the AHB bus.
+	 * BAM connected with DML should MASTER the AHB bus.
+	 */
+	config &= ~DIRECT_MODE;
+	/*
+	 * Disable infinite mode transfer as we won't be doing any
+	 * infinite size data transfers. All data transfer will be
+	 * of finite data size.
+	 */
+	config &= ~INFINITE_CONS_TRANS;
+	writel_relaxed(config, base + DML_CONFIG);
+
+	/*
+	 * Initialize the logical BAM pipe size for producer
+	 * and consumer.
+	 */
+	writel_relaxed(PRODUCER_PIPE_LOGICAL_SIZE,
+		       base + DML_PRODUCER_PIPE_LOGICAL_SIZE);
+	writel_relaxed(CONSUMER_PIPE_LOGICAL_SIZE,
+		       base + DML_CONSUMER_PIPE_LOGICAL_SIZE);
+
+	/* Initialize Producer/consumer pipe id */
+	writel_relaxed(producer_id | (consumer_id << CONSUMER_PIPE_ID_SHFT),
+		       base + DML_PIPE_ID);
+
+	/* Make sure dml initialization is finished */
+	mb();
+
+	return 0;
+}
+#else
+static inline int dml_hw_init(struct mmci_host *host, struct device_node *np)
+{
+	return -EINVAL;
+}
+
+static inline void dml_start_xfer(struct mmci_host *host, struct mmc_data *data)
+{
+}
+#endif /* CONFIG_MMC_QCOM_DML */
+
 static int dmaengine_setup(struct mmci_host *host)
 {
 	const char *rxname, *txname;
diff --git a/drivers/mmc/host/mmci_qcom_dml.c b/drivers/mmc/host/mmci_qcom_dml.c
deleted file mode 100644
index 00750c9..0000000
--- a/drivers/mmc/host/mmci_qcom_dml.c
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- *
- * Copyright (c) 2011, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that 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.
- *
- */
-#include <linux/of.h>
-#include <linux/of_dma.h>
-#include <linux/bitops.h>
-#include <linux/mmc/host.h>
-#include <linux/mmc/card.h>
-#include "mmci.h"
-
-/* Registers */
-#define DML_CONFIG			0x00
-#define PRODUCER_CRCI_MSK		GENMASK(1, 0)
-#define PRODUCER_CRCI_DISABLE		0
-#define PRODUCER_CRCI_X_SEL		BIT(0)
-#define PRODUCER_CRCI_Y_SEL		BIT(1)
-#define CONSUMER_CRCI_MSK		GENMASK(3, 2)
-#define CONSUMER_CRCI_DISABLE		0
-#define CONSUMER_CRCI_X_SEL		BIT(2)
-#define CONSUMER_CRCI_Y_SEL		BIT(3)
-#define PRODUCER_TRANS_END_EN		BIT(4)
-#define BYPASS				BIT(16)
-#define DIRECT_MODE			BIT(17)
-#define INFINITE_CONS_TRANS		BIT(18)
-
-#define DML_SW_RESET			0x08
-#define DML_PRODUCER_START		0x0c
-#define DML_CONSUMER_START		0x10
-#define DML_PRODUCER_PIPE_LOGICAL_SIZE	0x14
-#define DML_CONSUMER_PIPE_LOGICAL_SIZE	0x18
-#define DML_PIPE_ID			0x1c
-#define PRODUCER_PIPE_ID_SHFT		0
-#define PRODUCER_PIPE_ID_MSK		GENMASK(4, 0)
-#define CONSUMER_PIPE_ID_SHFT		16
-#define CONSUMER_PIPE_ID_MSK		GENMASK(20, 16)
-
-#define DML_PRODUCER_BAM_BLOCK_SIZE	0x24
-#define DML_PRODUCER_BAM_TRANS_SIZE	0x28
-
-/* other definitions */
-#define PRODUCER_PIPE_LOGICAL_SIZE	4096
-#define CONSUMER_PIPE_LOGICAL_SIZE	4096
-
-#define DML_OFFSET			0x800
-
-void dml_start_xfer(struct mmci_host *host, struct mmc_data *data)
-{
-	u32 config;
-	void __iomem *base = host->base + DML_OFFSET;
-
-	if (data->flags & MMC_DATA_READ) {
-		/* Read operation: configure DML for producer operation */
-		/* Set producer CRCI-x and disable consumer CRCI */
-		config = readl_relaxed(base + DML_CONFIG);
-		config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_X_SEL;
-		config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_DISABLE;
-		writel_relaxed(config, base + DML_CONFIG);
-
-		/* Set the Producer BAM block size */
-		writel_relaxed(data->blksz, base + DML_PRODUCER_BAM_BLOCK_SIZE);
-
-		/* Set Producer BAM Transaction size */
-		writel_relaxed(data->blocks * data->blksz,
-			       base + DML_PRODUCER_BAM_TRANS_SIZE);
-		/* Set Producer Transaction End bit */
-		config = readl_relaxed(base + DML_CONFIG);
-		config |= PRODUCER_TRANS_END_EN;
-		writel_relaxed(config, base + DML_CONFIG);
-		/* Trigger producer */
-		writel_relaxed(1, base + DML_PRODUCER_START);
-	} else {
-		/* Write operation: configure DML for consumer operation */
-		/* Set consumer CRCI-x and disable producer CRCI*/
-		config = readl_relaxed(base + DML_CONFIG);
-		config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_X_SEL;
-		config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_DISABLE;
-		writel_relaxed(config, base + DML_CONFIG);
-		/* Clear Producer Transaction End bit */
-		config = readl_relaxed(base + DML_CONFIG);
-		config &= ~PRODUCER_TRANS_END_EN;
-		writel_relaxed(config, base + DML_CONFIG);
-		/* Trigger consumer */
-		writel_relaxed(1, base + DML_CONSUMER_START);
-	}
-
-	/* make sure the dml is configured before dma is triggered */
-	wmb();
-}
-
-static int of_get_dml_pipe_index(struct device_node *np, const char *name)
-{
-	int index;
-	struct of_phandle_args	dma_spec;
-
-	index = of_property_match_string(np, "dma-names", name);
-
-	if (index < 0)
-		return -ENODEV;
-
-	if (of_parse_phandle_with_args(np, "dmas", "#dma-cells", index,
-				       &dma_spec))
-		return -ENODEV;
-
-	if (dma_spec.args_count)
-		return dma_spec.args[0];
-
-	return -ENODEV;
-}
-
-/* Initialize the dml hardware connected to SD Card controller */
-int dml_hw_init(struct mmci_host *host, struct device_node *np)
-{
-	u32 config;
-	void __iomem *base;
-	int consumer_id, producer_id;
-
-	consumer_id = of_get_dml_pipe_index(np, "tx");
-	producer_id = of_get_dml_pipe_index(np, "rx");
-
-	if (producer_id < 0 || consumer_id < 0)
-		return -ENODEV;
-
-	base = host->base + DML_OFFSET;
-
-	/* Reset the DML block */
-	writel_relaxed(1, base + DML_SW_RESET);
-
-	/* Disable the producer and consumer CRCI */
-	config = (PRODUCER_CRCI_DISABLE | CONSUMER_CRCI_DISABLE);
-	/*
-	 * Disable the bypass mode. Bypass mode will only be used
-	 * if data transfer is to happen in PIO mode and don't
-	 * want the BAM interface to connect with SDCC-DML.
-	 */
-	config &= ~BYPASS;
-	/*
-	 * Disable direct mode as we don't DML to MASTER the AHB bus.
-	 * BAM connected with DML should MASTER the AHB bus.
-	 */
-	config &= ~DIRECT_MODE;
-	/*
-	 * Disable infinite mode transfer as we won't be doing any
-	 * infinite size data transfers. All data transfer will be
-	 * of finite data size.
-	 */
-	config &= ~INFINITE_CONS_TRANS;
-	writel_relaxed(config, base + DML_CONFIG);
-
-	/*
-	 * Initialize the logical BAM pipe size for producer
-	 * and consumer.
-	 */
-	writel_relaxed(PRODUCER_PIPE_LOGICAL_SIZE,
-		       base + DML_PRODUCER_PIPE_LOGICAL_SIZE);
-	writel_relaxed(CONSUMER_PIPE_LOGICAL_SIZE,
-		       base + DML_CONSUMER_PIPE_LOGICAL_SIZE);
-
-	/* Initialize Producer/consumer pipe id */
-	writel_relaxed(producer_id | (consumer_id << CONSUMER_PIPE_ID_SHFT),
-		       base + DML_PIPE_ID);
-
-	/* Make sure dml initialization is finished */
-	mb();
-
-	return 0;
-}
diff --git a/drivers/mmc/host/mmci_qcom_dml.h b/drivers/mmc/host/mmci_qcom_dml.h
deleted file mode 100644
index 6e405d0..0000000
--- a/drivers/mmc/host/mmci_qcom_dml.h
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- *
- * Copyright (c) 2011, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that 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.
- *
- */
-#ifndef __MMC_QCOM_DML_H__
-#define __MMC_QCOM_DML_H__
-
-#ifdef CONFIG_MMC_QCOM_DML
-int dml_hw_init(struct mmci_host *host, struct device_node *np);
-void dml_start_xfer(struct mmci_host *host, struct mmc_data *data);
-#else
-static inline int dml_hw_init(struct mmci_host *host, struct device_node *np)
-{
-	return -ENOSYS;
-}
-static inline void dml_start_xfer(struct mmci_host *host, struct mmc_data *data)
-{
-}
-#endif /* CONFIG_MMC_QCOM_DML */
-
-#endif /* __MMC_QCOM_DML_H__ */
-- 
2.7.4


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 03/19] mmc: mmci: add datactrl block size variant property
  2018-06-12 13:14 [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
  2018-06-12 13:14 ` [PATCH 01/19] mmc: mmci: regroup and define dma operations Ludovic Barre
  2018-06-12 13:14 ` [PATCH 02/19] mmc: mmci: merge qcom dml feature into mmci dma Ludovic Barre
@ 2018-06-12 13:14 ` Ludovic Barre
  2018-06-12 13:14 ` [PATCH 04/19] mmc: mmci: expand startbiterr to irqmask and error check Ludovic Barre
                   ` (16 subsequent siblings)
  19 siblings, 0 replies; 41+ messages in thread
From: Ludovic Barre @ 2018-06-12 13:14 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	linux-arm-kernel, linux-kernel, devicetree, linux-mmc,
	Ludovic Barre

From: Ludovic Barre <ludovic.barre@st.com>

This patch allows to define a datactrl block size
by variant, requested by STM32 sdmmc variant.

Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
---
 drivers/mmc/host/mmci.c | 13 +++++++++++--
 drivers/mmc/host/mmci.h |  2 ++
 2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 7a15afd..9b25714 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -52,6 +52,7 @@ static struct variant_data variant_arm = {
 	.fifosize		= 16 * 4,
 	.fifohalfsize		= 8 * 4,
 	.datalength_bits	= 16,
+	.datactrl_blocksz	= 11,
 	.pwrreg_powerup		= MCI_PWR_UP,
 	.f_max			= 100000000,
 	.reversed_irq_handling	= true,
@@ -65,6 +66,7 @@ static struct variant_data variant_arm_extended_fifo = {
 	.fifosize		= 128 * 4,
 	.fifohalfsize		= 64 * 4,
 	.datalength_bits	= 16,
+	.datactrl_blocksz	= 11,
 	.pwrreg_powerup		= MCI_PWR_UP,
 	.f_max			= 100000000,
 	.mmcimask1		= true,
@@ -78,6 +80,7 @@ static struct variant_data variant_arm_extended_fifo_hwfc = {
 	.fifohalfsize		= 64 * 4,
 	.clkreg_enable		= MCI_ARM_HWFCEN,
 	.datalength_bits	= 16,
+	.datactrl_blocksz	= 11,
 	.pwrreg_powerup		= MCI_PWR_UP,
 	.f_max			= 100000000,
 	.mmcimask1		= true,
@@ -92,6 +95,7 @@ static struct variant_data variant_u300 = {
 	.clkreg_enable		= MCI_ST_U300_HWFCEN,
 	.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
 	.datalength_bits	= 16,
+	.datactrl_blocksz	= 11,
 	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,
 	.st_sdio			= true,
 	.pwrreg_powerup		= MCI_PWR_ON,
@@ -111,6 +115,7 @@ static struct variant_data variant_nomadik = {
 	.clkreg			= MCI_CLK_ENABLE,
 	.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
 	.datalength_bits	= 24,
+	.datactrl_blocksz	= 11,
 	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,
 	.st_sdio		= true,
 	.st_clkdiv		= true,
@@ -133,6 +138,7 @@ static struct variant_data variant_ux500 = {
 	.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
 	.clkreg_neg_edge_enable	= MCI_ST_UX500_NEG_EDGE,
 	.datalength_bits	= 24,
+	.datactrl_blocksz	= 11,
 	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,
 	.st_sdio		= true,
 	.st_clkdiv		= true,
@@ -160,6 +166,7 @@ static struct variant_data variant_ux500v2 = {
 	.clkreg_neg_edge_enable	= MCI_ST_UX500_NEG_EDGE,
 	.datactrl_mask_ddrmode	= MCI_DPSM_ST_DDRMODE,
 	.datalength_bits	= 24,
+	.datactrl_blocksz	= 11,
 	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,
 	.st_sdio		= true,
 	.st_clkdiv		= true,
@@ -187,6 +194,7 @@ static struct variant_data variant_stm32 = {
 	.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
 	.clkreg_neg_edge_enable	= MCI_ST_UX500_NEG_EDGE,
 	.datalength_bits	= 24,
+	.datactrl_blocksz	= 11,
 	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,
 	.st_sdio		= true,
 	.st_clkdiv		= true,
@@ -208,6 +216,7 @@ static struct variant_data variant_qcom = {
 	.data_cmd_enable	= MCI_CPSM_QCOM_DATCMD,
 	.blksz_datactrl4	= true,
 	.datalength_bits	= 24,
+	.datactrl_blocksz	= 11,
 	.pwrreg_powerup		= MCI_PWR_UP,
 	.f_max			= 208000000,
 	.explicit_mclk_control	= true,
@@ -1399,13 +1408,13 @@ static int mmci_probe(struct amba_device *dev,
 	/*
 	 * Block size can be up to 2048 bytes, but must be a power of two.
 	 */
-	mmc->max_blk_size = 1 << 11;
+	mmc->max_blk_size = 1 << variant->datactrl_blocksz;
 
 	/*
 	 * Limit the number of blocks transferred so that we don't overflow
 	 * the maximum request size.
 	 */
-	mmc->max_blk_count = mmc->max_req_size >> 11;
+	mmc->max_blk_count = mmc->max_req_size >> variant->datactrl_blocksz;
 
 	spin_lock_init(&host->lock);
 
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index f7cba35..27a2236 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -252,6 +252,7 @@ struct dma_chan;
  * @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl
  *		     register
  * @datactrl_mask_sdio: SDIO enable mask in datactrl register
+ * @datactrl_blksz: block size in power of two
  * @pwrreg_powerup: power up value for MMCIPOWER register
  * @f_max: maximum clk frequency supported by the controller.
  * @signal_direction: input/out direction of bus signals can be indicated
@@ -284,6 +285,7 @@ struct variant_data {
 	unsigned int		data_cmd_enable;
 	unsigned int		datactrl_mask_ddrmode;
 	unsigned int		datactrl_mask_sdio;
+	unsigned int		datactrl_blocksz;
 	bool			st_sdio;
 	bool			st_clkdiv;
 	bool			blksz_datactrl16;
-- 
2.7.4


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 04/19] mmc: mmci: expand startbiterr to irqmask and error check
  2018-06-12 13:14 [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
                   ` (2 preceding siblings ...)
  2018-06-12 13:14 ` [PATCH 03/19] mmc: mmci: add datactrl block size variant property Ludovic Barre
@ 2018-06-12 13:14 ` Ludovic Barre
  2018-06-12 13:14 ` [PATCH 05/19] mmc: mmci: allow to overwrite clock/power procedure to specific variant Ludovic Barre
                   ` (15 subsequent siblings)
  19 siblings, 0 replies; 41+ messages in thread
From: Ludovic Barre @ 2018-06-12 13:14 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	linux-arm-kernel, linux-kernel, devicetree, linux-mmc,
	Ludovic Barre

From: Ludovic Barre <ludovic.barre@st.com>

All variants don't pretend to have a startbiterr.
-While data error check, if status register return an error
(like  MCI_DATACRCFAIL) we must avoid to check MCI_STARTBITERR
(if not desired).
-expand start_err to MCI_IRQENABLE to avoid to set this bit by default.

Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
---
 drivers/mmc/host/mmci.c | 27 ++++++++++++++++-----------
 drivers/mmc/host/mmci.h |  6 +++---
 2 files changed, 19 insertions(+), 14 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 9b25714..ede95b7 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -584,14 +584,18 @@ static void
 mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
 	      unsigned int status)
 {
+	unsigned int status_err;
+
 	/* Make sure we have data to handle */
 	if (!data)
 		return;
 
 	/* First check for errors */
-	if (status & (MCI_DATACRCFAIL | MCI_DATATIMEOUT |
-		      host->variant->start_err |
-		      MCI_TXUNDERRUN | MCI_RXOVERRUN)) {
+	status_err = status & (host->variant->start_err |
+			       MCI_DATACRCFAIL | MCI_DATATIMEOUT |
+			       MCI_TXUNDERRUN | MCI_RXOVERRUN);
+
+	if (status_err) {
 		u32 remain, success;
 
 		/* Terminate the DMA transfer */
@@ -608,18 +612,18 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
 		success = data->blksz * data->blocks - remain;
 
 		dev_dbg(mmc_dev(host->mmc), "MCI ERROR IRQ, status 0x%08x at 0x%08x\n",
-			status, success);
-		if (status & MCI_DATACRCFAIL) {
+			status_err, success);
+		if (status_err & MCI_DATACRCFAIL) {
 			/* Last block was not successful */
 			success -= 1;
 			data->error = -EILSEQ;
-		} else if (status & MCI_DATATIMEOUT) {
+		} else if (status_err & MCI_DATATIMEOUT) {
 			data->error = -ETIMEDOUT;
-		} else if (status & MCI_STARTBITERR) {
+		} else if (status_err & MCI_STARTBITERR) {
 			data->error = -ECOMM;
-		} else if (status & MCI_TXUNDERRUN) {
+		} else if (status_err & MCI_TXUNDERRUN) {
 			data->error = -EIO;
-		} else if (status & MCI_RXOVERRUN) {
+		} else if (status_err & MCI_RXOVERRUN) {
 			if (success > host->variant->fifosize)
 				success -= host->variant->fifosize;
 			else
@@ -1470,7 +1474,7 @@ static int mmci_probe(struct amba_device *dev,
 			goto clk_disable;
 	}
 
-	writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+	writel(MCI_IRQENABLE | variant->start_err, host->base + MMCIMASK0);
 
 	amba_set_drvdata(dev, mmc);
 
@@ -1557,7 +1561,8 @@ static void mmci_restore(struct mmci_host *host)
 		writel(host->datactrl_reg, host->base + MMCIDATACTRL);
 		writel(host->pwr_reg, host->base + MMCIPOWER);
 	}
-	writel(MCI_IRQENABLE, host->base + MMCIMASK0);
+	writel(MCI_IRQENABLE | host->variant->start_err,
+	       host->base + MMCIMASK0);
 	mmci_reg_delay(host);
 
 	spin_unlock_irqrestore(&host->lock, flags);
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 27a2236..2ba9640 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -181,9 +181,9 @@
 #define MMCIFIFO		0x080 /* to 0x0bc */
 
 #define MCI_IRQENABLE	\
-	(MCI_CMDCRCFAILMASK|MCI_DATACRCFAILMASK|MCI_CMDTIMEOUTMASK|	\
-	MCI_DATATIMEOUTMASK|MCI_TXUNDERRUNMASK|MCI_RXOVERRUNMASK|	\
-	MCI_CMDRESPENDMASK|MCI_CMDSENTMASK|MCI_STARTBITERRMASK)
+	(MCI_CMDCRCFAILMASK | MCI_DATACRCFAILMASK | MCI_CMDTIMEOUTMASK | \
+	MCI_DATATIMEOUTMASK | MCI_TXUNDERRUNMASK | MCI_RXOVERRUNMASK |	\
+	MCI_CMDRESPENDMASK | MCI_CMDSENTMASK)
 
 /* These interrupts are directed to IRQ1 when two IRQ lines are available */
 #define MCI_IRQ1MASK \
-- 
2.7.4


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 05/19] mmc: mmci: allow to overwrite clock/power procedure to specific variant
  2018-06-12 13:14 [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
                   ` (3 preceding siblings ...)
  2018-06-12 13:14 ` [PATCH 04/19] mmc: mmci: expand startbiterr to irqmask and error check Ludovic Barre
@ 2018-06-12 13:14 ` Ludovic Barre
  2018-07-05 13:48   ` Ulf Hansson
  2018-06-12 13:14 ` [PATCH 06/19] mmc: mmci: add variant properties to define cpsm & cmdresp bits Ludovic Barre
                   ` (14 subsequent siblings)
  19 siblings, 1 reply; 41+ messages in thread
From: Ludovic Barre @ 2018-06-12 13:14 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	linux-arm-kernel, linux-kernel, devicetree, linux-mmc,
	Ludovic Barre

From: Ludovic Barre <ludovic.barre@st.com>

A specific variant could have different power or clock procedures.
This patch allows to overwrite the default mmci_set_clkreg and
mmci_set_pwrreg for a specific variant.

Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
---
 drivers/mmc/host/mmci.c | 96 +++++++++++++++++++++++++++++--------------------
 drivers/mmc/host/mmci.h |  7 ++++
 2 files changed, 64 insertions(+), 39 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index ede95b7..801c86b 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -374,6 +374,52 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
 	mmci_write_clkreg(host, clk);
 }
 
+static void mmci_set_pwrreg(struct mmci_host *host, unsigned char power_mode,
+			    unsigned int pwr)
+{
+	struct variant_data *variant = host->variant;
+	struct mmc_host *mmc = host->mmc;
+
+	switch (power_mode) {
+	case MMC_POWER_OFF:
+		if (!IS_ERR(mmc->supply.vmmc))
+			mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+
+		if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) {
+			regulator_disable(mmc->supply.vqmmc);
+			host->vqmmc_enabled = false;
+		}
+
+		break;
+	case MMC_POWER_UP:
+		if (!IS_ERR(mmc->supply.vmmc))
+			mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
+					      mmc->ios.vdd);
+
+		/*
+		 * The ST Micro variant doesn't have the PL180s MCI_PWR_UP
+		 * and instead uses MCI_PWR_ON so apply whatever value is
+		 * configured in the variant data.
+		 */
+		pwr |= variant->pwrreg_powerup;
+
+		break;
+	case MMC_POWER_ON:
+		if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) {
+			if (regulator_enable(mmc->supply.vqmmc) < 0)
+				dev_err(mmc_dev(mmc),
+					"failed to enable vqmmc regulator\n");
+			else
+				host->vqmmc_enabled = true;
+		}
+
+		pwr |= MCI_PWR_ON;
+		break;
+	}
+
+	mmci_write_pwrreg(host, pwr);
+}
+
 static void
 mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
 {
@@ -1031,7 +1077,7 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 {
 	struct mmci_host *host = mmc_priv(mmc);
 	struct variant_data *variant = host->variant;
-	u32 pwr = 0;
+	unsigned int pwr = 0;
 	unsigned long flags;
 	int ret;
 
@@ -1039,42 +1085,6 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 		host->plat->ios_handler(mmc_dev(mmc), ios))
 			dev_err(mmc_dev(mmc), "platform ios_handler failed\n");
 
-	switch (ios->power_mode) {
-	case MMC_POWER_OFF:
-		if (!IS_ERR(mmc->supply.vmmc))
-			mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
-
-		if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) {
-			regulator_disable(mmc->supply.vqmmc);
-			host->vqmmc_enabled = false;
-		}
-
-		break;
-	case MMC_POWER_UP:
-		if (!IS_ERR(mmc->supply.vmmc))
-			mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
-
-		/*
-		 * The ST Micro variant doesn't have the PL180s MCI_PWR_UP
-		 * and instead uses MCI_PWR_ON so apply whatever value is
-		 * configured in the variant data.
-		 */
-		pwr |= variant->pwrreg_powerup;
-
-		break;
-	case MMC_POWER_ON:
-		if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) {
-			ret = regulator_enable(mmc->supply.vqmmc);
-			if (ret < 0)
-				dev_err(mmc_dev(mmc),
-					"failed to enable vqmmc regulator\n");
-			else
-				host->vqmmc_enabled = true;
-		}
-
-		pwr |= MCI_PWR_ON;
-		break;
-	}
 
 	if (variant->signal_direction && ios->power_mode != MMC_POWER_OFF) {
 		/*
@@ -1126,8 +1136,16 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
 
 	spin_lock_irqsave(&host->lock, flags);
 
-	mmci_set_clkreg(host, ios->clock);
-	mmci_write_pwrreg(host, pwr);
+	if (variant->set_clkreg)
+		variant->set_clkreg(host, ios->clock);
+	else
+		mmci_set_clkreg(host, ios->clock);
+
+	if (variant->set_pwrreg)
+		variant->set_pwrreg(host, ios->power_mode, pwr);
+	else
+		mmci_set_pwrreg(host, ios->power_mode, pwr);
+
 	mmci_reg_delay(host);
 
 	spin_unlock_irqrestore(&host->lock, flags);
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 2ba9640..7265ca6 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -231,6 +231,7 @@
 
 struct clk;
 struct dma_chan;
+struct mmci_host;
 
 /**
  * struct variant_data - MMCI variant-specific quirks
@@ -273,6 +274,8 @@ struct dma_chan;
  *	       register.
  * @opendrain: bitmask identifying the OPENDRAIN bit inside MMCIPOWER register
  * @mmci_dma: Pointer to platform-specific DMA callbacks.
+ * @set_clk_ios: if clock procedure of variant is specific
+ * @set_pwr_ios: if power procedure of variant is specific
  */
 struct variant_data {
 	unsigned int		clkreg;
@@ -307,6 +310,9 @@ struct variant_data {
 	u32			start_err;
 	u32			opendrain;
 	struct mmci_dma_ops	*mmci_dma;
+	void (*set_clkreg)(struct mmci_host *host, unsigned int desired);
+	void (*set_pwrreg)(struct mmci_host *host, unsigned char power_mode,
+			   unsigned int pwr);
 };
 
 struct mmci_host {
@@ -328,6 +334,7 @@ struct mmci_host {
 	u32			pwr_reg;
 	u32			pwr_reg_add;
 	u32			clk_reg;
+	u32			clk_reg_add;
 	u32			datactrl_reg;
 	u32			busy_status;
 	u32			mask1_reg;
-- 
2.7.4


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 06/19] mmc: mmci: add variant properties to define cpsm & cmdresp bits
  2018-06-12 13:14 [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
                   ` (4 preceding siblings ...)
  2018-06-12 13:14 ` [PATCH 05/19] mmc: mmci: allow to overwrite clock/power procedure to specific variant Ludovic Barre
@ 2018-06-12 13:14 ` Ludovic Barre
  2018-07-05 14:20   ` Ulf Hansson
  2018-06-12 13:14 ` [PATCH 07/19] mmc: mmci: add variant property to define dpsm bit Ludovic Barre
                   ` (13 subsequent siblings)
  19 siblings, 1 reply; 41+ messages in thread
From: Ludovic Barre @ 2018-06-12 13:14 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	linux-arm-kernel, linux-kernel, devicetree, linux-mmc,
	Ludovic Barre

From: Ludovic Barre <ludovic.barre@st.com>

This patch adds command variant properties to define
cpsm enable bit and responses.
Needed to support the STM32 variant (shift of cpsm bit,
specific definition of commands response).

Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
---
 drivers/mmc/host/mmci.c | 47 +++++++++++++++++++++++++++++++++++++++++++----
 drivers/mmc/host/mmci.h |  8 ++++++++
 2 files changed, 51 insertions(+), 4 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 801c86b..52562fc 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -51,6 +51,10 @@ static unsigned int fmax = 515633;
 static struct variant_data variant_arm = {
 	.fifosize		= 16 * 4,
 	.fifohalfsize		= 8 * 4,
+	.cmdreg_cpsm_enable	= MCI_CPSM_ENABLE,
+	.cmdreg_lrsp_crc	= MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP,
+	.cmdreg_srsp_crc	= MCI_CPSM_RESPONSE,
+	.cmdreg_srsp		= MCI_CPSM_RESPONSE,
 	.datalength_bits	= 16,
 	.datactrl_blocksz	= 11,
 	.pwrreg_powerup		= MCI_PWR_UP,
@@ -65,6 +69,10 @@ static struct variant_data variant_arm = {
 static struct variant_data variant_arm_extended_fifo = {
 	.fifosize		= 128 * 4,
 	.fifohalfsize		= 64 * 4,
+	.cmdreg_cpsm_enable	= MCI_CPSM_ENABLE,
+	.cmdreg_lrsp_crc	= MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP,
+	.cmdreg_srsp_crc	= MCI_CPSM_RESPONSE,
+	.cmdreg_srsp		= MCI_CPSM_RESPONSE,
 	.datalength_bits	= 16,
 	.datactrl_blocksz	= 11,
 	.pwrreg_powerup		= MCI_PWR_UP,
@@ -79,6 +87,10 @@ static struct variant_data variant_arm_extended_fifo_hwfc = {
 	.fifosize		= 128 * 4,
 	.fifohalfsize		= 64 * 4,
 	.clkreg_enable		= MCI_ARM_HWFCEN,
+	.cmdreg_cpsm_enable	= MCI_CPSM_ENABLE,
+	.cmdreg_lrsp_crc	= MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP,
+	.cmdreg_srsp_crc	= MCI_CPSM_RESPONSE,
+	.cmdreg_srsp		= MCI_CPSM_RESPONSE,
 	.datalength_bits	= 16,
 	.datactrl_blocksz	= 11,
 	.pwrreg_powerup		= MCI_PWR_UP,
@@ -94,6 +106,10 @@ static struct variant_data variant_u300 = {
 	.fifohalfsize		= 8 * 4,
 	.clkreg_enable		= MCI_ST_U300_HWFCEN,
 	.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
+	.cmdreg_cpsm_enable	= MCI_CPSM_ENABLE,
+	.cmdreg_lrsp_crc	= MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP,
+	.cmdreg_srsp_crc	= MCI_CPSM_RESPONSE,
+	.cmdreg_srsp		= MCI_CPSM_RESPONSE,
 	.datalength_bits	= 16,
 	.datactrl_blocksz	= 11,
 	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,
@@ -114,6 +130,10 @@ static struct variant_data variant_nomadik = {
 	.fifohalfsize		= 8 * 4,
 	.clkreg			= MCI_CLK_ENABLE,
 	.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
+	.cmdreg_cpsm_enable	= MCI_CPSM_ENABLE,
+	.cmdreg_lrsp_crc	= MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP,
+	.cmdreg_srsp_crc	= MCI_CPSM_RESPONSE,
+	.cmdreg_srsp		= MCI_CPSM_RESPONSE,
 	.datalength_bits	= 24,
 	.datactrl_blocksz	= 11,
 	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,
@@ -137,6 +157,10 @@ static struct variant_data variant_ux500 = {
 	.clkreg_enable		= MCI_ST_UX500_HWFCEN,
 	.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
 	.clkreg_neg_edge_enable	= MCI_ST_UX500_NEG_EDGE,
+	.cmdreg_cpsm_enable	= MCI_CPSM_ENABLE,
+	.cmdreg_lrsp_crc	= MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP,
+	.cmdreg_srsp_crc	= MCI_CPSM_RESPONSE,
+	.cmdreg_srsp		= MCI_CPSM_RESPONSE,
 	.datalength_bits	= 24,
 	.datactrl_blocksz	= 11,
 	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,
@@ -164,6 +188,10 @@ static struct variant_data variant_ux500v2 = {
 	.clkreg_enable		= MCI_ST_UX500_HWFCEN,
 	.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
 	.clkreg_neg_edge_enable	= MCI_ST_UX500_NEG_EDGE,
+	.cmdreg_cpsm_enable	= MCI_CPSM_ENABLE,
+	.cmdreg_lrsp_crc	= MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP,
+	.cmdreg_srsp_crc	= MCI_CPSM_RESPONSE,
+	.cmdreg_srsp		= MCI_CPSM_RESPONSE,
 	.datactrl_mask_ddrmode	= MCI_DPSM_ST_DDRMODE,
 	.datalength_bits	= 24,
 	.datactrl_blocksz	= 11,
@@ -193,6 +221,10 @@ static struct variant_data variant_stm32 = {
 	.clkreg_enable		= MCI_ST_UX500_HWFCEN,
 	.clkreg_8bit_bus_enable = MCI_ST_8BIT_BUS,
 	.clkreg_neg_edge_enable	= MCI_ST_UX500_NEG_EDGE,
+	.cmdreg_cpsm_enable	= MCI_CPSM_ENABLE,
+	.cmdreg_lrsp_crc	= MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP,
+	.cmdreg_srsp_crc	= MCI_CPSM_RESPONSE,
+	.cmdreg_srsp		= MCI_CPSM_RESPONSE,
 	.datalength_bits	= 24,
 	.datactrl_blocksz	= 11,
 	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,
@@ -213,6 +245,10 @@ static struct variant_data variant_qcom = {
 				  MCI_QCOM_CLK_SELECT_IN_FBCLK,
 	.clkreg_8bit_bus_enable = MCI_QCOM_CLK_WIDEBUS_8,
 	.datactrl_mask_ddrmode	= MCI_QCOM_CLK_SELECT_IN_DDR_MODE,
+	.cmdreg_cpsm_enable	= MCI_CPSM_ENABLE,
+	.cmdreg_lrsp_crc	= MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP,
+	.cmdreg_srsp_crc	= MCI_CPSM_RESPONSE,
+	.cmdreg_srsp		= MCI_CPSM_RESPONSE,
 	.data_cmd_enable	= MCI_CPSM_QCOM_DATCMD,
 	.blksz_datactrl4	= true,
 	.datalength_bits	= 24,
@@ -603,16 +639,19 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)
 	dev_dbg(mmc_dev(host->mmc), "op %02x arg %08x flags %08x\n",
 	    cmd->opcode, cmd->arg, cmd->flags);
 
-	if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) {
+	if (readl(base + MMCICOMMAND) & host->variant->cmdreg_cpsm_enable) {
 		writel(0, base + MMCICOMMAND);
 		mmci_reg_delay(host);
 	}
 
-	c |= cmd->opcode | MCI_CPSM_ENABLE;
+	c |= cmd->opcode | host->variant->cmdreg_cpsm_enable;
 	if (cmd->flags & MMC_RSP_PRESENT) {
 		if (cmd->flags & MMC_RSP_136)
-			c |= MCI_CPSM_LONGRSP;
-		c |= MCI_CPSM_RESPONSE;
+			c |= host->variant->cmdreg_lrsp_crc;
+		else if (cmd->flags & MMC_RSP_CRC)
+			c |= host->variant->cmdreg_srsp_crc;
+		else
+			c |= host->variant->cmdreg_srsp;
 	}
 	if (/*interrupt*/0)
 		c |= MCI_CPSM_INTERRUPT;
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 7265ca6..e173305 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -239,6 +239,10 @@ struct mmci_host;
  * @clkreg_enable: enable value for MMCICLOCK register
  * @clkreg_8bit_bus_enable: enable value for 8 bit bus
  * @clkreg_neg_edge_enable: enable value for inverted data/cmd output
+ * @cmdreg_cpsm_enable: enable value for CPSM
+ * @cmdreg_lrsp_crc: enable value for long response with crc
+ * @cmdreg_srsp_crc: enable value for short response with crc
+ * @cmdreg_srsp: enable value for short response without crc
  * @datalength_bits: number of bits in the MMCIDATALENGTH register
  * @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY
  *	      is asserted (likewise for RX)
@@ -282,6 +286,10 @@ struct variant_data {
 	unsigned int		clkreg_enable;
 	unsigned int		clkreg_8bit_bus_enable;
 	unsigned int		clkreg_neg_edge_enable;
+	unsigned int		cmdreg_cpsm_enable;
+	unsigned int		cmdreg_lrsp_crc;
+	unsigned int		cmdreg_srsp_crc;
+	unsigned int		cmdreg_srsp;
 	unsigned int		datalength_bits;
 	unsigned int		fifosize;
 	unsigned int		fifohalfsize;
-- 
2.7.4


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 07/19] mmc: mmci: add variant property to define dpsm bit
  2018-06-12 13:14 [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
                   ` (5 preceding siblings ...)
  2018-06-12 13:14 ` [PATCH 06/19] mmc: mmci: add variant properties to define cpsm & cmdresp bits Ludovic Barre
@ 2018-06-12 13:14 ` Ludovic Barre
  2018-06-12 13:14 ` [PATCH 08/19] mmc: mmci: add variant property to define irq pio mask Ludovic Barre
                   ` (12 subsequent siblings)
  19 siblings, 0 replies; 41+ messages in thread
From: Ludovic Barre @ 2018-06-12 13:14 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	linux-arm-kernel, linux-kernel, devicetree, linux-mmc,
	Ludovic Barre

From: Ludovic Barre <ludovic.barre@st.com>

This patch adds datactrl variant property to define
dpsm enable bit. Needed to support the STM32 variant
(STM32 has no dpsm enable bit).

Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
---
 drivers/mmc/host/mmci.c | 15 ++++++++++++---
 drivers/mmc/host/mmci.h |  2 ++
 2 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 52562fc..1f44c61 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -57,6 +57,7 @@ static struct variant_data variant_arm = {
 	.cmdreg_srsp		= MCI_CPSM_RESPONSE,
 	.datalength_bits	= 16,
 	.datactrl_blocksz	= 11,
+	.datactrl_dpsm_enable	= MCI_DPSM_ENABLE,
 	.pwrreg_powerup		= MCI_PWR_UP,
 	.f_max			= 100000000,
 	.reversed_irq_handling	= true,
@@ -75,6 +76,7 @@ static struct variant_data variant_arm_extended_fifo = {
 	.cmdreg_srsp		= MCI_CPSM_RESPONSE,
 	.datalength_bits	= 16,
 	.datactrl_blocksz	= 11,
+	.datactrl_dpsm_enable	= MCI_DPSM_ENABLE,
 	.pwrreg_powerup		= MCI_PWR_UP,
 	.f_max			= 100000000,
 	.mmcimask1		= true,
@@ -93,6 +95,7 @@ static struct variant_data variant_arm_extended_fifo_hwfc = {
 	.cmdreg_srsp		= MCI_CPSM_RESPONSE,
 	.datalength_bits	= 16,
 	.datactrl_blocksz	= 11,
+	.datactrl_dpsm_enable	= MCI_DPSM_ENABLE,
 	.pwrreg_powerup		= MCI_PWR_UP,
 	.f_max			= 100000000,
 	.mmcimask1		= true,
@@ -112,6 +115,7 @@ static struct variant_data variant_u300 = {
 	.cmdreg_srsp		= MCI_CPSM_RESPONSE,
 	.datalength_bits	= 16,
 	.datactrl_blocksz	= 11,
+	.datactrl_dpsm_enable	= MCI_DPSM_ENABLE,
 	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,
 	.st_sdio			= true,
 	.pwrreg_powerup		= MCI_PWR_ON,
@@ -136,6 +140,7 @@ static struct variant_data variant_nomadik = {
 	.cmdreg_srsp		= MCI_CPSM_RESPONSE,
 	.datalength_bits	= 24,
 	.datactrl_blocksz	= 11,
+	.datactrl_dpsm_enable	= MCI_DPSM_ENABLE,
 	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,
 	.st_sdio		= true,
 	.st_clkdiv		= true,
@@ -163,6 +168,7 @@ static struct variant_data variant_ux500 = {
 	.cmdreg_srsp		= MCI_CPSM_RESPONSE,
 	.datalength_bits	= 24,
 	.datactrl_blocksz	= 11,
+	.datactrl_dpsm_enable	= MCI_DPSM_ENABLE,
 	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,
 	.st_sdio		= true,
 	.st_clkdiv		= true,
@@ -195,6 +201,7 @@ static struct variant_data variant_ux500v2 = {
 	.datactrl_mask_ddrmode	= MCI_DPSM_ST_DDRMODE,
 	.datalength_bits	= 24,
 	.datactrl_blocksz	= 11,
+	.datactrl_dpsm_enable	= MCI_DPSM_ENABLE,
 	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,
 	.st_sdio		= true,
 	.st_clkdiv		= true,
@@ -227,6 +234,7 @@ static struct variant_data variant_stm32 = {
 	.cmdreg_srsp		= MCI_CPSM_RESPONSE,
 	.datalength_bits	= 24,
 	.datactrl_blocksz	= 11,
+	.datactrl_dpsm_enable	= MCI_DPSM_ENABLE,
 	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,
 	.st_sdio		= true,
 	.st_clkdiv		= true,
@@ -253,6 +261,7 @@ static struct variant_data variant_qcom = {
 	.blksz_datactrl4	= true,
 	.datalength_bits	= 24,
 	.datactrl_blocksz	= 11,
+	.datactrl_dpsm_enable	= MCI_DPSM_ENABLE,
 	.pwrreg_powerup		= MCI_PWR_UP,
 	.f_max			= 208000000,
 	.explicit_mclk_control	= true,
@@ -564,11 +573,11 @@ static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
 	BUG_ON(1 << blksz_bits != data->blksz);
 
 	if (variant->blksz_datactrl16)
-		datactrl = MCI_DPSM_ENABLE | (data->blksz << 16);
+		datactrl = variant->datactrl_dpsm_enable | (data->blksz << 16);
 	else if (variant->blksz_datactrl4)
-		datactrl = MCI_DPSM_ENABLE | (data->blksz << 4);
+		datactrl = variant->datactrl_dpsm_enable | (data->blksz << 4);
 	else
-		datactrl = MCI_DPSM_ENABLE | blksz_bits << 4;
+		datactrl = variant->datactrl_dpsm_enable | blksz_bits << 4;
 
 	if (data->flags & MMC_DATA_READ)
 		datactrl |= MCI_DPSM_DIRECTION;
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index e173305..a2bf8bc 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -258,6 +258,7 @@ struct mmci_host;
  *		     register
  * @datactrl_mask_sdio: SDIO enable mask in datactrl register
  * @datactrl_blksz: block size in power of two
+ * @datactrl_dpsm_enable: enable value for DPSM
  * @pwrreg_powerup: power up value for MMCIPOWER register
  * @f_max: maximum clk frequency supported by the controller.
  * @signal_direction: input/out direction of bus signals can be indicated
@@ -297,6 +298,7 @@ struct variant_data {
 	unsigned int		datactrl_mask_ddrmode;
 	unsigned int		datactrl_mask_sdio;
 	unsigned int		datactrl_blocksz;
+	unsigned int		datactrl_dpsm_enable;
 	bool			st_sdio;
 	bool			st_clkdiv;
 	bool			blksz_datactrl16;
-- 
2.7.4


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 08/19] mmc: mmci: add variant property to define irq pio mask
  2018-06-12 13:14 [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
                   ` (6 preceding siblings ...)
  2018-06-12 13:14 ` [PATCH 07/19] mmc: mmci: add variant property to define dpsm bit Ludovic Barre
@ 2018-06-12 13:14 ` Ludovic Barre
  2018-06-12 13:14 ` [PATCH 09/19] mmc: mmci: add variant property to write datactrl before command Ludovic Barre
                   ` (11 subsequent siblings)
  19 siblings, 0 replies; 41+ messages in thread
From: Ludovic Barre @ 2018-06-12 13:14 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	linux-arm-kernel, linux-kernel, devicetree, linux-mmc,
	Ludovic Barre

From: Ludovic Barre <ludovic.barre@st.com>

This patch allows to define specific pio mask for variants.
Needed to support the STM32 sdmmc variant which has some bits
with different meaning (bits: 21,20,13,12,9)

Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
---
 drivers/mmc/host/mmci.c | 13 +++++++++++--
 drivers/mmc/host/mmci.h |  5 ++++-
 2 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 1f44c61..5fd3ffb6 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -62,6 +62,7 @@ static struct variant_data variant_arm = {
 	.f_max			= 100000000,
 	.reversed_irq_handling	= true,
 	.mmcimask1		= true,
+	.irq_pio_mask		= MCI_IRQ_PIO_MASK,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_ROD,
 	.mmci_dma		= &dmaengine,
@@ -80,6 +81,7 @@ static struct variant_data variant_arm_extended_fifo = {
 	.pwrreg_powerup		= MCI_PWR_UP,
 	.f_max			= 100000000,
 	.mmcimask1		= true,
+	.irq_pio_mask		= MCI_IRQ_PIO_MASK,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_ROD,
 	.mmci_dma		= &dmaengine,
@@ -99,6 +101,7 @@ static struct variant_data variant_arm_extended_fifo_hwfc = {
 	.pwrreg_powerup		= MCI_PWR_UP,
 	.f_max			= 100000000,
 	.mmcimask1		= true,
+	.irq_pio_mask		= MCI_IRQ_PIO_MASK,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_ROD,
 	.mmci_dma		= &dmaengine,
@@ -124,6 +127,7 @@ static struct variant_data variant_u300 = {
 	.pwrreg_clkgate		= true,
 	.pwrreg_nopower		= true,
 	.mmcimask1		= true,
+	.irq_pio_mask		= MCI_IRQ_PIO_MASK,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_OD,
 	.mmci_dma		= &dmaengine,
@@ -150,6 +154,7 @@ static struct variant_data variant_nomadik = {
 	.pwrreg_clkgate		= true,
 	.pwrreg_nopower		= true,
 	.mmcimask1		= true,
+	.irq_pio_mask		= MCI_IRQ_PIO_MASK,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_OD,
 	.mmci_dma		= &dmaengine,
@@ -182,6 +187,7 @@ static struct variant_data variant_ux500 = {
 	.busy_detect_mask	= MCI_ST_BUSYENDMASK,
 	.pwrreg_nopower		= true,
 	.mmcimask1		= true,
+	.irq_pio_mask		= MCI_IRQ_PIO_MASK,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_OD,
 	.mmci_dma		= &dmaengine,
@@ -216,6 +222,7 @@ static struct variant_data variant_ux500v2 = {
 	.busy_detect_mask	= MCI_ST_BUSYENDMASK,
 	.pwrreg_nopower		= true,
 	.mmcimask1		= true,
+	.irq_pio_mask		= MCI_IRQ_PIO_MASK,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_OD,
 	.mmci_dma		= &dmaengine,
@@ -232,6 +239,7 @@ static struct variant_data variant_stm32 = {
 	.cmdreg_lrsp_crc	= MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP,
 	.cmdreg_srsp_crc	= MCI_CPSM_RESPONSE,
 	.cmdreg_srsp		= MCI_CPSM_RESPONSE,
+	.irq_pio_mask		= MCI_IRQ_PIO_MASK,
 	.datalength_bits	= 24,
 	.datactrl_blocksz	= 11,
 	.datactrl_dpsm_enable	= MCI_DPSM_ENABLE,
@@ -268,6 +276,7 @@ static struct variant_data variant_qcom = {
 	.qcom_fifo		= true,
 	.qcom_dml		= true,
 	.mmcimask1		= true,
+	.irq_pio_mask		= MCI_IRQ_PIO_MASK,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_ROD,
 	.mmci_dma		= &dmaengine,
@@ -486,7 +495,7 @@ static void mmci_set_mask1(struct mmci_host *host, unsigned int mask)
 	if (host->singleirq) {
 		unsigned int mask0 = readl(base + MMCIMASK0);
 
-		mask0 &= ~MCI_IRQ1MASK;
+		mask0 &= ~variant->irq_pio_mask;
 		mask0 |= mask;
 
 		writel(mask0, base + MMCIMASK0);
@@ -1046,7 +1055,7 @@ static irqreturn_t mmci_irq(int irq, void *dev_id)
 			if (status & host->mask1_reg)
 				mmci_pio_irq(irq, dev_id);
 
-			status &= ~MCI_IRQ1MASK;
+			status &= ~host->variant->irq_pio_mask;
 		}
 
 		/*
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index a2bf8bc..b91b130 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -186,7 +186,7 @@
 	MCI_CMDRESPENDMASK | MCI_CMDSENTMASK)
 
 /* These interrupts are directed to IRQ1 when two IRQ lines are available */
-#define MCI_IRQ1MASK \
+#define MCI_IRQ_PIO_MASK \
 	(MCI_RXFIFOHALFFULLMASK | MCI_RXDATAAVLBLMASK | \
 	 MCI_TXFIFOHALFEMPTYMASK)
 
@@ -275,6 +275,8 @@ struct mmci_host;
  * @qcom_dml: enables qcom specific dma glue for dma transfers.
  * @reversed_irq_handling: handle data irq before cmd irq.
  * @mmcimask1: true if variant have a MMCIMASK1 register.
+ * @irq_pio_mask: bitmask used to manage interrupt pio transfert in mmcimask
+ *		  register
  * @start_err: bitmask identifying the STARTBITERR bit inside MMCISTATUS
  *	       register.
  * @opendrain: bitmask identifying the OPENDRAIN bit inside MMCIPOWER register
@@ -317,6 +319,7 @@ struct variant_data {
 	bool			qcom_dml;
 	bool			reversed_irq_handling;
 	bool			mmcimask1;
+	unsigned int		irq_pio_mask;
 	u32			start_err;
 	u32			opendrain;
 	struct mmci_dma_ops	*mmci_dma;
-- 
2.7.4


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 09/19] mmc: mmci: add variant property to write datactrl before command
  2018-06-12 13:14 [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
                   ` (7 preceding siblings ...)
  2018-06-12 13:14 ` [PATCH 08/19] mmc: mmci: add variant property to define irq pio mask Ludovic Barre
@ 2018-06-12 13:14 ` Ludovic Barre
  2018-06-12 13:14 ` [PATCH 10/19] mmc: mmci: add variant property to allow remain data Ludovic Barre
                   ` (10 subsequent siblings)
  19 siblings, 0 replies; 41+ messages in thread
From: Ludovic Barre @ 2018-06-12 13:14 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	linux-arm-kernel, linux-kernel, devicetree, linux-mmc,
	Ludovic Barre

From: Ludovic Barre <ludovic.barre@st.com>

This patch adds a boolean property to allow to write datactrl
before to send command, whatever the command type (read or write).
Needed to support the STM32 sdmmc variant.

Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
---
 drivers/mmc/host/mmci.c | 6 ++++--
 drivers/mmc/host/mmci.h | 2 ++
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 5fd3ffb6..83c3572 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -855,7 +855,8 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
 		mmci_request_end(host, host->mrq);
 	} else if (sbc) {
 		mmci_start_command(host, host->mrq->cmd, 0);
-	} else if (!(cmd->data->flags & MMC_DATA_READ)) {
+	} else if (!host->variant->datactrl_first &&
+		   !(cmd->data->flags & MMC_DATA_READ)) {
 		mmci_start_data(host, cmd->data);
 	}
 }
@@ -1119,7 +1120,8 @@ static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 	if (mrq->data)
 		mmci_dma_get_next_data(host, mrq->data);
 
-	if (mrq->data && mrq->data->flags & MMC_DATA_READ)
+	if (mrq->data &&
+	    (host->variant->datactrl_first || mrq->data->flags & MMC_DATA_READ))
 		mmci_start_data(host, mrq->data);
 
 	if (mrq->sbc)
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index b91b130..5091025 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -259,6 +259,7 @@ struct mmci_host;
  * @datactrl_mask_sdio: SDIO enable mask in datactrl register
  * @datactrl_blksz: block size in power of two
  * @datactrl_dpsm_enable: enable value for DPSM
+ * @datactrl_first: true if data must be setup before send command
  * @pwrreg_powerup: power up value for MMCIPOWER register
  * @f_max: maximum clk frequency supported by the controller.
  * @signal_direction: input/out direction of bus signals can be indicated
@@ -301,6 +302,7 @@ struct variant_data {
 	unsigned int		datactrl_mask_sdio;
 	unsigned int		datactrl_blocksz;
 	unsigned int		datactrl_dpsm_enable;
+	bool			datactrl_first;
 	bool			st_sdio;
 	bool			st_clkdiv;
 	bool			blksz_datactrl16;
-- 
2.7.4


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 10/19] mmc: mmci: add variant property to allow remain data
  2018-06-12 13:14 [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
                   ` (8 preceding siblings ...)
  2018-06-12 13:14 ` [PATCH 09/19] mmc: mmci: add variant property to write datactrl before command Ludovic Barre
@ 2018-06-12 13:14 ` Ludovic Barre
  2018-07-05 13:55   ` Ulf Hansson
  2018-06-12 13:14 ` [PATCH 11/19] mmc: mmci: add variant property to check specific data constraint Ludovic Barre
                   ` (9 subsequent siblings)
  19 siblings, 1 reply; 41+ messages in thread
From: Ludovic Barre @ 2018-06-12 13:14 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	linux-arm-kernel, linux-kernel, devicetree, linux-mmc,
	Ludovic Barre

From: Ludovic Barre <ludovic.barre@st.com>

This patch adds a boolean property to read remaining data.
Needed to support the STM32 sdmmc variant. MMCIDATACNT
register should be read only after the data transfer is complete.
When reading after an error event the read data count value may be
different from the real number of data bytes transferred.

Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
---
 drivers/mmc/host/mmci.c | 17 +++++++++++++++--
 drivers/mmc/host/mmci.h |  3 +++
 2 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 83c3572..abddad7 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -58,6 +58,7 @@ static struct variant_data variant_arm = {
 	.datalength_bits	= 16,
 	.datactrl_blocksz	= 11,
 	.datactrl_dpsm_enable	= MCI_DPSM_ENABLE,
+	.datacnt_remain		= true,
 	.pwrreg_powerup		= MCI_PWR_UP,
 	.f_max			= 100000000,
 	.reversed_irq_handling	= true,
@@ -78,6 +79,7 @@ static struct variant_data variant_arm_extended_fifo = {
 	.datalength_bits	= 16,
 	.datactrl_blocksz	= 11,
 	.datactrl_dpsm_enable	= MCI_DPSM_ENABLE,
+	.datacnt_remain		= true,
 	.pwrreg_powerup		= MCI_PWR_UP,
 	.f_max			= 100000000,
 	.mmcimask1		= true,
@@ -98,6 +100,7 @@ static struct variant_data variant_arm_extended_fifo_hwfc = {
 	.datalength_bits	= 16,
 	.datactrl_blocksz	= 11,
 	.datactrl_dpsm_enable	= MCI_DPSM_ENABLE,
+	.datacnt_remain		= true,
 	.pwrreg_powerup		= MCI_PWR_UP,
 	.f_max			= 100000000,
 	.mmcimask1		= true,
@@ -120,6 +123,7 @@ static struct variant_data variant_u300 = {
 	.datactrl_blocksz	= 11,
 	.datactrl_dpsm_enable	= MCI_DPSM_ENABLE,
 	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,
+	.datacnt_remain		= true,
 	.st_sdio			= true,
 	.pwrreg_powerup		= MCI_PWR_ON,
 	.f_max			= 100000000,
@@ -146,6 +150,7 @@ static struct variant_data variant_nomadik = {
 	.datactrl_blocksz	= 11,
 	.datactrl_dpsm_enable	= MCI_DPSM_ENABLE,
 	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,
+	.datacnt_remain		= true,
 	.st_sdio		= true,
 	.st_clkdiv		= true,
 	.pwrreg_powerup		= MCI_PWR_ON,
@@ -175,6 +180,7 @@ static struct variant_data variant_ux500 = {
 	.datactrl_blocksz	= 11,
 	.datactrl_dpsm_enable	= MCI_DPSM_ENABLE,
 	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,
+	.datacnt_remain		= true,
 	.st_sdio		= true,
 	.st_clkdiv		= true,
 	.pwrreg_powerup		= MCI_PWR_ON,
@@ -209,6 +215,7 @@ static struct variant_data variant_ux500v2 = {
 	.datactrl_blocksz	= 11,
 	.datactrl_dpsm_enable	= MCI_DPSM_ENABLE,
 	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,
+	.datacnt_remain		= true,
 	.st_sdio		= true,
 	.st_clkdiv		= true,
 	.blksz_datactrl16	= true,
@@ -244,6 +251,7 @@ static struct variant_data variant_stm32 = {
 	.datactrl_blocksz	= 11,
 	.datactrl_dpsm_enable	= MCI_DPSM_ENABLE,
 	.datactrl_mask_sdio	= MCI_DPSM_ST_SDIOEN,
+	.datacnt_remain		= true,
 	.st_sdio		= true,
 	.st_clkdiv		= true,
 	.pwrreg_powerup		= MCI_PWR_ON,
@@ -270,6 +278,7 @@ static struct variant_data variant_qcom = {
 	.datalength_bits	= 24,
 	.datactrl_blocksz	= 11,
 	.datactrl_dpsm_enable	= MCI_DPSM_ENABLE,
+	.datacnt_remain		= true,
 	.pwrreg_powerup		= MCI_PWR_UP,
 	.f_max			= 208000000,
 	.explicit_mclk_control	= true,
@@ -711,8 +720,12 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
 		 * can be as much as a FIFO-worth of data ahead.  This
 		 * matters for FIFO overruns only.
 		 */
-		remain = readl(host->base + MMCIDATACNT);
-		success = data->blksz * data->blocks - remain;
+		if (host->variant->datacnt_remain) {
+			remain = readl(host->base + MMCIDATACNT);
+			success = data->blksz * data->blocks - remain;
+		} else {
+			success = 0;
+		}
 
 		dev_dbg(mmc_dev(host->mmc), "MCI ERROR IRQ, status 0x%08x at 0x%08x\n",
 			status_err, success);
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 5091025..12ee2e6 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -260,6 +260,8 @@ struct mmci_host;
  * @datactrl_blksz: block size in power of two
  * @datactrl_dpsm_enable: enable value for DPSM
  * @datactrl_first: true if data must be setup before send command
+ * @datacnt_remain: true if you could read datacnt register
+ *                  to define remain data
  * @pwrreg_powerup: power up value for MMCIPOWER register
  * @f_max: maximum clk frequency supported by the controller.
  * @signal_direction: input/out direction of bus signals can be indicated
@@ -303,6 +305,7 @@ struct variant_data {
 	unsigned int		datactrl_blocksz;
 	unsigned int		datactrl_dpsm_enable;
 	bool			datactrl_first;
+	bool			datacnt_remain;
 	bool			st_sdio;
 	bool			st_clkdiv;
 	bool			blksz_datactrl16;
-- 
2.7.4


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 11/19] mmc: mmci: add variant property to check specific data constraint
  2018-06-12 13:14 [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
                   ` (9 preceding siblings ...)
  2018-06-12 13:14 ` [PATCH 10/19] mmc: mmci: add variant property to allow remain data Ludovic Barre
@ 2018-06-12 13:14 ` Ludovic Barre
  2018-06-12 13:14 ` [PATCH 12/19] mmc: mmci: add variant property to request a reset Ludovic Barre
                   ` (8 subsequent siblings)
  19 siblings, 0 replies; 41+ messages in thread
From: Ludovic Barre @ 2018-06-12 13:14 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	linux-arm-kernel, linux-kernel, devicetree, linux-mmc,
	Ludovic Barre

From: Ludovic Barre <ludovic.barre@st.com>

This patch adds a function pointer to check specific data constraint
follow hardware variant. Needed to support the STM32 sdmmc variant
which has alignment constraint.

Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
---
 drivers/mmc/host/mmci.c | 5 +++++
 drivers/mmc/host/mmci.h | 2 ++
 2 files changed, 7 insertions(+)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index abddad7..9c00804 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -312,6 +312,8 @@ static int mmci_card_busy(struct mmc_host *mmc)
 static int mmci_validate_data(struct mmci_host *host,
 			      struct mmc_data *data)
 {
+	struct variant_data *variant = host->variant;
+
 	if (!data)
 		return 0;
 
@@ -321,6 +323,9 @@ static int mmci_validate_data(struct mmci_host *host,
 		return -EINVAL;
 	}
 
+	if (variant->validate_data)
+		return variant->validate_data(host, data);
+
 	return 0;
 }
 
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 12ee2e6..30d3f53 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -284,6 +284,7 @@ struct mmci_host;
  *	       register.
  * @opendrain: bitmask identifying the OPENDRAIN bit inside MMCIPOWER register
  * @mmci_dma: Pointer to platform-specific DMA callbacks.
+ * @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
  */
@@ -328,6 +329,7 @@ struct variant_data {
 	u32			start_err;
 	u32			opendrain;
 	struct mmci_dma_ops	*mmci_dma;
+	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,
 			   unsigned int pwr);
-- 
2.7.4


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 12/19] mmc: mmci: add variant property to request a reset
  2018-06-12 13:14 [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
                   ` (10 preceding siblings ...)
  2018-06-12 13:14 ` [PATCH 11/19] mmc: mmci: add variant property to check specific data constraint Ludovic Barre
@ 2018-06-12 13:14 ` Ludovic Barre
  2018-06-25 21:23   ` Rob Herring
  2018-06-12 13:14 ` [PATCH 13/19] mmc: mmci: send stop cmd if a data command fail Ludovic Barre
                   ` (7 subsequent siblings)
  19 siblings, 1 reply; 41+ messages in thread
From: Ludovic Barre @ 2018-06-12 13:14 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	linux-arm-kernel, linux-kernel, devicetree, linux-mmc,
	Ludovic Barre

From: Ludovic Barre <ludovic.barre@st.com>

Some variants could require a reset.
STM32 sdmmc variant needs to reset hardware block
during the power cycle procedure (for re-initialization)

Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
---
 Documentation/devicetree/bindings/mmc/mmci.txt | 2 ++
 drivers/mmc/host/mmci.c                        | 9 +++++++++
 drivers/mmc/host/mmci.h                        | 4 ++++
 3 files changed, 15 insertions(+)

diff --git a/Documentation/devicetree/bindings/mmc/mmci.txt b/Documentation/devicetree/bindings/mmc/mmci.txt
index 03796cf..e952707 100644
--- a/Documentation/devicetree/bindings/mmc/mmci.txt
+++ b/Documentation/devicetree/bindings/mmc/mmci.txt
@@ -11,6 +11,8 @@ Required properties:
 - compatible             : contains "arm,pl18x", "arm,primecell".
 - vmmc-supply            : phandle to the regulator device tree node, mentioned
                            as the VCC/VDD supply in the eMMC/SD specs.
+depend of variant:
+- resets                 : phandle to internal reset line.
 
 Optional properties:
 - arm,primecell-periphid : contains the PrimeCell Peripheral ID, it overrides
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 9c00804..87724e1 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -37,6 +37,7 @@
 #include <linux/pm_runtime.h>
 #include <linux/types.h>
 #include <linux/pinctrl/consumer.h>
+#include <linux/reset.h>
 
 #include <asm/div64.h>
 #include <asm/io.h>
@@ -1445,6 +1446,14 @@ static int mmci_probe(struct amba_device *dev,
 
 	dev_dbg(mmc_dev(mmc), "clocking block at %u Hz\n", mmc->f_max);
 
+	if (variant->reset) {
+		host->rst = devm_reset_control_get_exclusive(&dev->dev, NULL);
+		if (IS_ERR(host->rst)) {
+			ret = PTR_ERR(host->rst);
+			goto clk_disable;
+		}
+	}
+
 	/* Get regulators and the supported OCR mask */
 	ret = mmc_regulator_get_supply(mmc);
 	if (ret)
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 30d3f53..2d7e901 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -284,6 +284,7 @@ struct mmci_host;
  *	       register.
  * @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.
  * @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
@@ -329,6 +330,7 @@ struct variant_data {
 	u32			start_err;
 	u32			opendrain;
 	struct mmci_dma_ops	*mmci_dma;
+	bool			reset;
 	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,
@@ -345,6 +347,8 @@ struct mmci_host {
 	struct clk		*clk;
 	bool			singleirq;
 
+	struct reset_control	*rst;
+
 	spinlock_t		lock;
 
 	unsigned int		mclk;
-- 
2.7.4


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 13/19] mmc: mmci: send stop cmd if a data command fail
  2018-06-12 13:14 [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
                   ` (11 preceding siblings ...)
  2018-06-12 13:14 ` [PATCH 12/19] mmc: mmci: add variant property to request a reset Ludovic Barre
@ 2018-06-12 13:14 ` Ludovic Barre
  2018-07-04 13:37   ` Ulf Hansson
  2018-06-12 13:14 ` [PATCH 14/19] mmc: mmci: add clock divider for stm32 sdmmc Ludovic Barre
                   ` (6 subsequent siblings)
  19 siblings, 1 reply; 41+ messages in thread
From: Ludovic Barre @ 2018-06-12 13:14 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	linux-arm-kernel, linux-kernel, devicetree, linux-mmc,
	Ludovic Barre

From: Ludovic Barre <ludovic.barre@st.com>

The mmc framework follows the requirement of SD_Specification:
the STOP_TRANSMISSION is sent on multiple write/read commands
and the stop command (alone), not needed on other ADTC commands.

But, some variants require a stop command "STOP_TRANSMISION" to clear
the DPSM "Data Path State Machine" if an error happens on command or data
step. If it's not done the next data command will freeze hardware block.
Needed to support the STM32 sdmmc variant.

Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
---
 drivers/mmc/host/mmci.c | 36 +++++++++++++++++++++++++++++++-----
 drivers/mmc/host/mmci.h |  3 +++
 2 files changed, 34 insertions(+), 5 deletions(-)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 87724e1..9af7db8 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -24,6 +24,7 @@
 #include <linux/mmc/pm.h>
 #include <linux/mmc/host.h>
 #include <linux/mmc/card.h>
+#include <linux/mmc/mmc.h>
 #include <linux/mmc/slot-gpio.h>
 #include <linux/amba/bus.h>
 #include <linux/clk.h>
@@ -522,11 +523,28 @@ static void mmci_set_mask1(struct mmci_host *host, unsigned int mask)
 	host->mask1_reg = mask;
 }
 
-static void mmci_stop_data(struct mmci_host *host)
+static int mmci_stop_data(struct mmci_host *host)
 {
+	struct mmc_command *stop = &host->stop_abort;
+	struct mmc_data *data = host->data;
+	unsigned int cmd = 0;
+
 	mmci_write_datactrlreg(host, 0);
 	mmci_set_mask1(host, 0);
 	host->data = NULL;
+
+	if (host->variant->cmdreg_stop) {
+		cmd |= host->variant->cmdreg_stop;
+		if (!data->stop) {
+			memset(stop, 0, sizeof(struct mmc_command));
+			stop->opcode = MMC_STOP_TRANSMISSION;
+			stop->arg = 0;
+			stop->flags = MMC_RSP_R1B | MMC_CMD_AC;
+			data->stop = stop;
+		}
+	}
+
+	return cmd;
 }
 
 static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data)
@@ -703,6 +721,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
 	      unsigned int status)
 {
 	unsigned int status_err;
+	unsigned int cmd_reg = 0;
 
 	/* Make sure we have data to handle */
 	if (!data)
@@ -761,7 +780,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
 	if (status & MCI_DATAEND || data->error) {
 		mmci_dma_finalize(host, data);
 
-		mmci_stop_data(host);
+		cmd_reg = mmci_stop_data(host);
 
 		if (!data->error)
 			/* The error clause is handled above, success! */
@@ -770,7 +789,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
 		if (!data->stop || host->mrq->sbc) {
 			mmci_request_end(host, data->mrq);
 		} else {
-			mmci_start_command(host, data->stop, 0);
+			mmci_start_command(host, data->stop, cmd_reg);
 		}
 	}
 }
@@ -780,6 +799,8 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
 	     unsigned int status)
 {
 	void __iomem *base = host->base;
+	struct mmc_data *data = host->data;
+	unsigned int cmd_reg = 0;
 	bool sbc;
 
 	if (!cmd)
@@ -865,11 +886,16 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
 	}
 
 	if ((!sbc && !cmd->data) || cmd->error) {
-		if (host->data) {
+		if (data) {
 			/* Terminate the DMA transfer */
 			mmci_dma_error(host);
 
-			mmci_stop_data(host);
+			cmd_reg = mmci_stop_data(host);
+
+			if (data->stop) {
+				mmci_start_command(host, data->stop, cmd_reg);
+				return;
+			}
 		}
 		mmci_request_end(host, host->mrq);
 	} else if (sbc) {
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 2d7e901..4b4cd1d 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -243,6 +243,7 @@ struct mmci_host;
  * @cmdreg_lrsp_crc: enable value for long response with crc
  * @cmdreg_srsp_crc: enable value for short response with crc
  * @cmdreg_srsp: enable value for short response without crc
+ * @cmdreg_stop: enable value for stop and abort transmission
  * @datalength_bits: number of bits in the MMCIDATALENGTH register
  * @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY
  *	      is asserted (likewise for RX)
@@ -298,6 +299,7 @@ struct variant_data {
 	unsigned int		cmdreg_lrsp_crc;
 	unsigned int		cmdreg_srsp_crc;
 	unsigned int		cmdreg_srsp;
+	unsigned int		cmdreg_stop;
 	unsigned int		datalength_bits;
 	unsigned int		fifosize;
 	unsigned int		fifohalfsize;
@@ -343,6 +345,7 @@ struct mmci_host {
 	struct mmc_request	*mrq;
 	struct mmc_command	*cmd;
 	struct mmc_data		*data;
+	struct mmc_command	stop_abort;
 	struct mmc_host		*mmc;
 	struct clk		*clk;
 	bool			singleirq;
-- 
2.7.4


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 14/19] mmc: mmci: add clock divider for stm32 sdmmc
  2018-06-12 13:14 [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
                   ` (12 preceding siblings ...)
  2018-06-12 13:14 ` [PATCH 13/19] mmc: mmci: send stop cmd if a data command fail Ludovic Barre
@ 2018-06-12 13:14 ` Ludovic Barre
  2018-06-12 13:14 ` [PATCH 15/19] mmc: mmci: add stm32 sdmmc registers Ludovic Barre
                   ` (5 subsequent siblings)
  19 siblings, 0 replies; 41+ messages in thread
From: Ludovic Barre @ 2018-06-12 13:14 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	linux-arm-kernel, linux-kernel, devicetree, linux-mmc,
	Ludovic Barre

From: Ludovic Barre <ludovic.barre@st.com>

The STM32 sdmmc variant has a different clock divider.

Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
---
 drivers/mmc/host/mmci.c | 2 ++
 drivers/mmc/host/mmci.h | 2 ++
 2 files changed, 4 insertions(+)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 9af7db8..e24fd1e 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -1451,6 +1451,8 @@ static int mmci_probe(struct amba_device *dev,
 	 */
 	if (variant->st_clkdiv)
 		mmc->f_min = DIV_ROUND_UP(host->mclk, 257);
+	else if (variant->stm32_clkdiv)
+		mmc->f_min = DIV_ROUND_UP(host->mclk, 2046);
 	else if (variant->explicit_mclk_control)
 		mmc->f_min = clk_round_rate(host->clk, 100000);
 	else
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 4b4cd1d..227927e 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -252,6 +252,7 @@ struct mmci_host;
  * @data_cmd_enable: enable value for data commands.
  * @st_sdio: enable ST specific SDIO logic
  * @st_clkdiv: true if using a ST-specific clock divider algorithm
+ * @stm32_clkdiv: true if using a STM32-specific clock divider algorithm
  * @datactrl_mask_ddrmode: ddr mode mask in datactrl register.
  * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl
  *                    register
@@ -312,6 +313,7 @@ struct variant_data {
 	bool			datacnt_remain;
 	bool			st_sdio;
 	bool			st_clkdiv;
+	bool			stm32_clkdiv;
 	bool			blksz_datactrl16;
 	bool			blksz_datactrl4;
 	u32			pwrreg_powerup;
-- 
2.7.4


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 15/19] mmc: mmci: add stm32 sdmmc registers
  2018-06-12 13:14 [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
                   ` (13 preceding siblings ...)
  2018-06-12 13:14 ` [PATCH 14/19] mmc: mmci: add clock divider for stm32 sdmmc Ludovic Barre
@ 2018-06-12 13:14 ` Ludovic Barre
  2018-06-12 13:14 ` [PATCH 16/19] mmc: mmci: add DT bindings for STM32 sdmmc Ludovic Barre
                   ` (4 subsequent siblings)
  19 siblings, 0 replies; 41+ messages in thread
From: Ludovic Barre @ 2018-06-12 13:14 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	linux-arm-kernel, linux-kernel, devicetree, linux-mmc,
	Ludovic Barre

From: Ludovic Barre <ludovic.barre@st.com>

This patch adds stm32 sdmmc specific registers.

Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
---
 drivers/mmc/host/mmci.h | 56 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 56 insertions(+)

diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index 227927e..c2ad724 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -23,6 +23,14 @@
 #define MCI_ST_DATA31DIREN	(1 << 5)
 #define MCI_ST_FBCLKEN		(1 << 7)
 #define MCI_ST_DATA74DIREN	(1 << 8)
+/*
+ * The STM32 sdmmc does not have PWR_UP/OD/ROD
+ * and uses the power register for
+ */
+#define MCI_STM32_PWR_CYC	0x02
+#define MCI_STM32_VSWITCH	BIT(2)
+#define MCI_STM32_VSWITCHEN	BIT(3)
+#define MCI_STM32_DIRPOL	BIT(4)
 
 #define MMCICLOCK		0x004
 #define MCI_CLK_ENABLE		(1 << 8)
@@ -50,6 +58,19 @@
 #define MCI_QCOM_CLK_SELECT_IN_FBCLK	BIT(15)
 #define MCI_QCOM_CLK_SELECT_IN_DDR_MODE	(BIT(14) | BIT(15))
 
+/* Modified on STM32 sdmmc */
+#define MCI_STM32_CLK_CLKDIV_MSK	GENMASK(9, 0)
+#define MCI_STM32_CLK_WIDEBUS_4		BIT(14)
+#define MCI_STM32_CLK_WIDEBUS_8		BIT(15)
+#define MCI_STM32_CLK_NEGEDGE		BIT(16)
+#define MCI_STM32_CLK_HWFCEN		BIT(17)
+#define MCI_STM32_CLK_DDR		BIT(18)
+#define MCI_STM32_CLK_BUSSPEED		BIT(19)
+#define MCI_STM32_CLK_SEL_MSK		GENMASK(21, 20)
+#define MCI_STM32_CLK_SELCK		(0 << 20)
+#define MCI_STM32_CLK_SELCKIN		(1 << 20)
+#define MCI_STM32_CLK_SELFBCK		(2 << 20)
+
 #define MMCIARGUMENT		0x008
 
 /* The command register controls the Command Path State Machine (CPSM) */
@@ -72,6 +93,15 @@
 #define MCI_CPSM_QCOM_CCSDISABLE	BIT(15)
 #define MCI_CPSM_QCOM_AUTO_CMD19	BIT(16)
 #define MCI_CPSM_QCOM_AUTO_CMD21	BIT(21)
+/* Command register in STM32 sdmmc versions */
+#define MCI_CPSM_STM32_CMDTRANS		BIT(6)
+#define MCI_CPSM_STM32_CMDSTOP		BIT(7)
+#define MCI_CPSM_STM32_WAITRESP_MASK	GENMASK(9, 8)
+#define MCI_CPSM_STM32_NORSP		(0 << 8)
+#define MCI_CPSM_STM32_SRSP_CRC		(1 << 8)
+#define MCI_CPSM_STM32_SRSP		(2 << 8)
+#define MCI_CPSM_STM32_LRSP_CRC		(3 << 8)
+#define MCI_CPSM_STM32_ENABLE		BIT(12)
 
 #define MMCIRESPCMD		0x010
 #define MMCIRESPONSE0		0x014
@@ -130,6 +160,8 @@
 #define MCI_ST_SDIOIT		(1 << 22)
 #define MCI_ST_CEATAEND		(1 << 23)
 #define MCI_ST_CARDBUSY		(1 << 24)
+/* Extended status bits for the STM32 variants */
+#define MCI_STM32_BUSYD0	BIT(20)
 
 #define MMCICLEAR		0x038
 #define MCI_CMDCRCFAILCLR	(1 << 0)
@@ -175,11 +207,32 @@
 #define MCI_ST_SDIOITMASK	(1 << 22)
 #define MCI_ST_CEATAENDMASK	(1 << 23)
 #define MCI_ST_BUSYENDMASK	(1 << 24)
+/* Extended status bits for the STM32 variants */
+#define MCI_STM32_BUSYD0ENDMASK	BIT(21)
 
 #define MMCIMASK1		0x040
 #define MMCIFIFOCNT		0x048
 #define MMCIFIFO		0x080 /* to 0x0bc */
 
+/* STM32 sdmmc registers for IDMA (Internal DMA) */
+#define MMCI_STM32_IDMACTRLR	0x050
+#define MMCI_STM32_IDMAEN	BIT(0)
+#define MMCI_STM32_IDMALLIEN	BIT(1)
+
+#define MMCI_STM32_IDMABSIZER		0x054
+#define MMCI_STM32_IDMABNDT_SHIFT	5
+#define MMCI_STM32_IDMABNDT_MASK	GENMASK(12, 5)
+
+#define MMCI_STM32_IDMABASE0R	0x058
+
+#define MMCI_STM32_IDMALAR	0x64
+#define MMCI_STM32_IDMALA_MASK	GENMASK(13, 0)
+#define MMCI_STM32_ABR		BIT(29)
+#define MMCI_STM32_ULS		BIT(30)
+#define MMCI_STM32_ULA		BIT(31)
+
+#define MMCI_STM32_IDMABAR	0x68
+
 #define MCI_IRQENABLE	\
 	(MCI_CMDCRCFAILMASK | MCI_DATACRCFAILMASK | MCI_CMDTIMEOUTMASK | \
 	MCI_DATATIMEOUTMASK | MCI_TXUNDERRUNMASK | MCI_RXOVERRUNMASK |	\
@@ -190,6 +243,9 @@
 	(MCI_RXFIFOHALFFULLMASK | MCI_RXDATAAVLBLMASK | \
 	 MCI_TXFIFOHALFEMPTYMASK)
 
+#define MCI_IRQ_PIO_STM32_MASK \
+	(MCI_RXFIFOHALFFULLMASK | MCI_TXFIFOHALFEMPTYMASK)
+
 #define NR_SG		128
 
 #define MMCI_PINCTRL_STATE_OPENDRAIN "opendrain"
-- 
2.7.4


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 16/19] mmc: mmci: add DT bindings for STM32 sdmmc
  2018-06-12 13:14 [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
                   ` (14 preceding siblings ...)
  2018-06-12 13:14 ` [PATCH 15/19] mmc: mmci: add stm32 sdmmc registers Ludovic Barre
@ 2018-06-12 13:14 ` Ludovic Barre
  2018-06-25 21:47   ` Rob Herring
  2018-06-12 13:14 ` [PATCH 17/19] mmc: mmci: add stm32 sdmmc idma support Ludovic Barre
                   ` (3 subsequent siblings)
  19 siblings, 1 reply; 41+ messages in thread
From: Ludovic Barre @ 2018-06-12 13:14 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	linux-arm-kernel, linux-kernel, devicetree, linux-mmc,
	Ludovic Barre

From: Ludovic Barre <ludovic.barre@st.com>

STM32 sdmmc variant has support to:
-Indicate signal directions (only one property
for d0dir, d123dir, cmd_dir)
-Select command and data phase relation.
-Select "clock in" from an external driver.

Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
---
 Documentation/devicetree/bindings/mmc/mmci.txt | 9 +++++++++
 drivers/mmc/host/mmci.c                        | 6 ++++++
 2 files changed, 15 insertions(+)

diff --git a/Documentation/devicetree/bindings/mmc/mmci.txt b/Documentation/devicetree/bindings/mmc/mmci.txt
index e952707..d678933 100644
--- a/Documentation/devicetree/bindings/mmc/mmci.txt
+++ b/Documentation/devicetree/bindings/mmc/mmci.txt
@@ -19,6 +19,7 @@ Optional properties:
                            the ID provided by the HW
 - vqmmc-supply           : phandle to the regulator device tree node, mentioned
                            as the VCCQ/VDD_IO supply in the eMMC/SD specs.
+specific for ux500 variant:
 - st,sig-dir-dat0        : bus signal direction pin used for DAT[0].
 - st,sig-dir-dat2        : bus signal direction pin used for DAT[2].
 - st,sig-dir-dat31       : bus signal direction pin used for DAT[3] and DAT[1].
@@ -26,6 +27,14 @@ Optional properties:
 - st,sig-dir-cmd         : cmd signal direction pin used for CMD.
 - st,sig-pin-fbclk       : feedback clock signal pin used.
 
+specific for sdmmc variant:
+- st,sig-dir             : signal direction polarity used for cmd, dat0 dat123.
+- st,neg-edge            : data & command phase relation, generated on
+                           sd clock falling edge.
+- st,use-ckin            : use ckin pin from an external driver to sample
+                           the receive data (example: with voltage
+			   switch transceiver).
+
 Deprecated properties:
 - mmc-cap-mmc-highspeed  : indicates whether MMC is high speed capable.
 - mmc-cap-sd-highspeed   : indicates whether SD is high speed capable.
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index e24fd1e..86aef4f 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -1328,6 +1328,12 @@ static int mmci_of_parse(struct device_node *np, struct mmc_host *mmc)
 		host->pwr_reg_add |= MCI_ST_CMDDIREN;
 	if (of_get_property(np, "st,sig-pin-fbclk", NULL))
 		host->pwr_reg_add |= MCI_ST_FBCLKEN;
+	if (of_get_property(np, "st,sig-dir", NULL))
+		host->pwr_reg_add |= MCI_STM32_DIRPOL;
+	if (of_get_property(np, "st,neg-edge", NULL))
+		host->clk_reg_add |= MCI_STM32_CLK_NEGEDGE;
+	if (of_get_property(np, "st,use-ckin ", NULL))
+		host->clk_reg_add |= MCI_STM32_CLK_SELCKIN;
 
 	if (of_get_property(np, "mmc-cap-mmc-highspeed", NULL))
 		mmc->caps |= MMC_CAP_MMC_HIGHSPEED;
-- 
2.7.4


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 17/19] mmc: mmci: add stm32 sdmmc idma support
  2018-06-12 13:14 [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
                   ` (15 preceding siblings ...)
  2018-06-12 13:14 ` [PATCH 16/19] mmc: mmci: add DT bindings for STM32 sdmmc Ludovic Barre
@ 2018-06-12 13:14 ` Ludovic Barre
  2018-06-12 13:14 ` [PATCH 18/19] mmc: mmci: add specific clk/pwr procedure for stm32 sdmmc Ludovic Barre
                   ` (2 subsequent siblings)
  19 siblings, 0 replies; 41+ messages in thread
From: Ludovic Barre @ 2018-06-12 13:14 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	linux-arm-kernel, linux-kernel, devicetree, linux-mmc,
	Ludovic Barre

From: Ludovic Barre <ludovic.barre@st.com>

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 <ludovic.barre@st.com>
---
 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


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 18/19] mmc: mmci: add specific clk/pwr procedure for stm32 sdmmc
  2018-06-12 13:14 [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
                   ` (16 preceding siblings ...)
  2018-06-12 13:14 ` [PATCH 17/19] mmc: mmci: add stm32 sdmmc idma support Ludovic Barre
@ 2018-06-12 13:14 ` Ludovic Barre
  2018-07-05 14:49   ` Ulf Hansson
  2018-06-12 13:14 ` [PATCH 19/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
  2018-06-29 13:51 ` [PATCH 00/19] " Ludovic BARRE
  19 siblings, 1 reply; 41+ messages in thread
From: Ludovic Barre @ 2018-06-12 13:14 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	linux-arm-kernel, linux-kernel, devicetree, linux-mmc,
	Ludovic Barre

From: Ludovic Barre <ludovic.barre@st.com>

This patch adds specific clock and power ios for stm32 sdmmc variant.
power ios: stm32 dedicated procedure must be done to perform power
off/on procedures. To power off, the sdmmc must be reset and set
to power cycle state before to disabling vqmmc. This drives low
SDMMC_D[7:0], SDMMC_CMD and SDMMC_CK to prevent the Card from
being supplied through the signal lines.
To power on, set the SDMMC in power-off SDMMC_D[7:0], SDMMC_CMD
and SDMMC_CK are driven high. Then we can set the SDMMC to
Power-on state.

clock ios: specific bits behavior:
-clock divider card_clk = mclk / (2 * clkdiv)
-ddr activation
-wide bus 1/4/8bits
-bus speed
-receive clock selection (in_ck/ck_in/fb_ck)

Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
---
 drivers/mmc/host/mmci.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 112 insertions(+)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index 86aef4f..af27a0a 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -50,6 +50,10 @@
 
 static unsigned int fmax = 515633;
 
+static void mmci_sdmmc_set_pwrreg(struct mmci_host *host,
+				  unsigned char power_mode, unsigned int pwr);
+static void mmci_sdmmc_set_clkreg(struct mmci_host *host, unsigned int desired);
+
 static struct variant_data variant_arm = {
 	.fifosize		= 16 * 4,
 	.fifohalfsize		= 8 * 4,
@@ -490,6 +494,114 @@ static void mmci_set_pwrreg(struct mmci_host *host, unsigned char power_mode,
 	mmci_write_pwrreg(host, pwr);
 }
 
+static void mmci_sdmmc_set_clkreg(struct mmci_host *host, unsigned int desired)
+{
+	unsigned int clk = 0, ddr = 0;
+
+	if (host->mmc->ios.timing == MMC_TIMING_MMC_DDR52 ||
+	    host->mmc->ios.timing == MMC_TIMING_UHS_DDR50)
+		ddr = MCI_STM32_CLK_DDR;
+
+	/*
+	 * cclk = mclk / (2 * clkdiv)
+	 * clkdiv 0 => bypass
+	 * in ddr mode bypass is not possible
+	 */
+	if (desired) {
+		if (desired >= host->mclk && !ddr) {
+			host->cclk = host->mclk;
+		} else {
+			clk = DIV_ROUND_UP(host->mclk, 2 * desired);
+			if (clk > MCI_STM32_CLK_CLKDIV_MSK)
+				clk = MCI_STM32_CLK_CLKDIV_MSK;
+			host->cclk = host->mclk / (2 * clk);
+		}
+	}
+
+	if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4)
+		clk |= MCI_STM32_CLK_WIDEBUS_4;
+	if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8)
+		clk |= MCI_STM32_CLK_WIDEBUS_8;
+
+	clk |= MCI_STM32_CLK_HWFCEN;
+	clk |= host->clk_reg_add;
+	clk |= ddr;
+
+	/*
+	 * SDMMC_FBCK is selected when an external Delay Block is needed
+	 * with SDR104.
+	 */
+	if (host->mmc->ios.timing >= MMC_TIMING_UHS_SDR50) {
+		clk |= MCI_STM32_CLK_BUSSPEED;
+		if (host->mmc->ios.timing == MMC_TIMING_UHS_SDR104) {
+			clk &= ~MCI_STM32_CLK_SEL_MSK;
+			clk |= MCI_STM32_CLK_SELFBCK;
+		}
+	}
+
+	mmci_write_clkreg(host, clk);
+}
+
+static void mmci_sdmmc_set_pwrreg(struct mmci_host *host,
+				  unsigned char power_mode, unsigned int pwr)
+{
+	struct mmc_host *mmc = host->mmc;
+
+	pwr |= host->pwr_reg_add;
+
+	switch (power_mode) {
+	case MMC_POWER_OFF:
+		if (!IS_ERR(mmc->supply.vmmc))
+			mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
+
+		/* Only a reset could disable sdmmc */
+		reset_control_assert(host->rst);
+		udelay(2);
+		reset_control_deassert(host->rst);
+
+		/* default mask (probe) must be activated */
+		writel(MCI_IRQENABLE | host->variant->start_err,
+		       host->base + MMCIMASK0);
+
+		/*
+		 * Set the SDMMC in Power-cycle state before to disabling vqmmc.
+		 * This will make that the SDMMC_D[7:0], SDMMC_CMD and SDMMC_CK
+		 * are driven low, to prevent the Card from being supplied
+		 * through the signal lines.
+		 */
+		mmci_write_pwrreg(host, MCI_STM32_PWR_CYC | pwr);
+
+		if (!IS_ERR(host->mmc->supply.vqmmc) && host->vqmmc_enabled) {
+			regulator_disable(host->mmc->supply.vqmmc);
+			host->vqmmc_enabled = false;
+		}
+		break;
+	case MMC_POWER_UP:
+		if (!IS_ERR(mmc->supply.vmmc))
+			mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
+					      mmc->ios.vdd);
+		break;
+	case MMC_POWER_ON:
+		if (!IS_ERR(host->mmc->supply.vqmmc) && !host->vqmmc_enabled) {
+			if (regulator_enable(host->mmc->supply.vqmmc) < 0)
+				dev_err(mmc_dev(host->mmc),
+					"failed to enable vqmmc regulator\n");
+			else
+				host->vqmmc_enabled = true;
+		}
+
+		/*
+		 * After a power-cycle state, we must set the SDMMC in
+		 * Power-off. The SDMMC_D[7:0], SDMMC_CMD and SDMMC_CK are
+		 * driven high. Then we can set the SDMMC to Power-on state
+		 */
+		mmci_write_pwrreg(host, MCI_PWR_OFF | pwr);
+		mdelay(1);
+		mmci_write_pwrreg(host, MCI_PWR_ON | pwr);
+		break;
+	}
+}
+
 static void
 mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
 {
-- 
2.7.4


^ permalink raw reply	[flat|nested] 41+ messages in thread

* [PATCH 19/19] mmc: mmci: add stm32 sdmmc variant
  2018-06-12 13:14 [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
                   ` (17 preceding siblings ...)
  2018-06-12 13:14 ` [PATCH 18/19] mmc: mmci: add specific clk/pwr procedure for stm32 sdmmc Ludovic Barre
@ 2018-06-12 13:14 ` Ludovic Barre
  2018-06-29 13:51 ` [PATCH 00/19] " Ludovic BARRE
  19 siblings, 0 replies; 41+ messages in thread
From: Ludovic Barre @ 2018-06-12 13:14 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	linux-arm-kernel, linux-kernel, devicetree, linux-mmc,
	Ludovic Barre

From: Ludovic Barre <ludovic.barre@st.com>

This patch adds a stm32 sdmmc variant, rev 1.1.
Introduces a new Manufacturer id "0x53, ascii 'S' to define
new stm32 sdmmc familly with clean range of amba
revision/configurations bits (corresponding to sdmmc_ver
register with major/minor fields).

Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
---
 drivers/mmc/host/mmci.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)

diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index af27a0a..bed671d 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -267,6 +267,29 @@ static struct variant_data variant_stm32 = {
 	.mmci_dma		= &dmaengine,
 };
 
+static struct variant_data variant_stm32_sdmmc = {
+	.fifosize		= 16 * 4,
+	.fifohalfsize		= 8 * 4,
+	.f_max			= 208000000,
+	.stm32_clkdiv		= true,
+	.reset			= true,
+	.cmdreg_cpsm_enable	= MCI_CPSM_STM32_ENABLE,
+	.cmdreg_lrsp_crc	= MCI_CPSM_STM32_LRSP_CRC,
+	.cmdreg_srsp_crc	= MCI_CPSM_STM32_SRSP_CRC,
+	.cmdreg_srsp		= MCI_CPSM_STM32_SRSP,
+	.cmdreg_stop		= MCI_CPSM_STM32_CMDSTOP,
+	.data_cmd_enable	= MCI_CPSM_STM32_CMDTRANS,
+	.irq_pio_mask		= MCI_IRQ_PIO_STM32_MASK,
+	.datactrl_first		= true,
+	.datalength_bits	= 25,
+	.datactrl_blocksz	= 14,
+	.mmci_dma		= &sdmmc_idma,
+	.stm32_idmabsize_mask	= GENMASK(12, 5),
+	.validate_data		= sdmmc_idma_validate_data,
+	.set_clkreg		= mmci_sdmmc_set_clkreg,
+	.set_pwrreg		= mmci_sdmmc_set_pwrreg,
+};
+
 static struct variant_data variant_qcom = {
 	.fifosize		= 16 * 4,
 	.fifohalfsize		= 8 * 4,
@@ -1907,6 +1930,11 @@ static const struct amba_id mmci_ids[] = {
 		.mask   = 0x00ffffff,
 		.data	= &variant_stm32,
 	},
+	{
+		.id     = 0x10153180,
+		.mask	= 0xf0ffffff,
+		.data	= &variant_stm32_sdmmc,
+	},
 	/* Qualcomm variants */
 	{
 		.id     = 0x00051180,
-- 
2.7.4


^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 12/19] mmc: mmci: add variant property to request a reset
  2018-06-12 13:14 ` [PATCH 12/19] mmc: mmci: add variant property to request a reset Ludovic Barre
@ 2018-06-25 21:23   ` Rob Herring
  0 siblings, 0 replies; 41+ messages in thread
From: Rob Herring @ 2018-06-25 21:23 UTC (permalink / raw)
  To: Ludovic Barre
  Cc: Ulf Hansson, Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	linux-arm-kernel, linux-kernel, devicetree, linux-mmc

On Tue, Jun 12, 2018 at 03:14:33PM +0200, Ludovic Barre wrote:
> From: Ludovic Barre <ludovic.barre@st.com>
> 
> Some variants could require a reset.
> STM32 sdmmc variant needs to reset hardware block
> during the power cycle procedure (for re-initialization)
> 
> Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
> ---
>  Documentation/devicetree/bindings/mmc/mmci.txt | 2 ++
>  drivers/mmc/host/mmci.c                        | 9 +++++++++
>  drivers/mmc/host/mmci.h                        | 4 ++++
>  3 files changed, 15 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/mmc/mmci.txt b/Documentation/devicetree/bindings/mmc/mmci.txt
> index 03796cf..e952707 100644
> --- a/Documentation/devicetree/bindings/mmc/mmci.txt
> +++ b/Documentation/devicetree/bindings/mmc/mmci.txt
> @@ -11,6 +11,8 @@ Required properties:
>  - compatible             : contains "arm,pl18x", "arm,primecell".
>  - vmmc-supply            : phandle to the regulator device tree node, mentioned
>                             as the VCC/VDD supply in the eMMC/SD specs.
> +depend of variant:

Just make it an optional property.

> +- resets                 : phandle to internal reset line.
>  
>  Optional properties:
>  - arm,primecell-periphid : contains the PrimeCell Peripheral ID, it overrides

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 16/19] mmc: mmci: add DT bindings for STM32 sdmmc
  2018-06-12 13:14 ` [PATCH 16/19] mmc: mmci: add DT bindings for STM32 sdmmc Ludovic Barre
@ 2018-06-25 21:47   ` Rob Herring
  0 siblings, 0 replies; 41+ messages in thread
From: Rob Herring @ 2018-06-25 21:47 UTC (permalink / raw)
  To: Ludovic Barre
  Cc: Ulf Hansson, Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	linux-arm-kernel, linux-kernel, devicetree, linux-mmc

On Tue, Jun 12, 2018 at 03:14:37PM +0200, Ludovic Barre wrote:
> From: Ludovic Barre <ludovic.barre@st.com>
> 
> STM32 sdmmc variant has support to:
> -Indicate signal directions (only one property
> for d0dir, d123dir, cmd_dir)
> -Select command and data phase relation.
> -Select "clock in" from an external driver.
> 
> Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
> ---
>  Documentation/devicetree/bindings/mmc/mmci.txt | 9 +++++++++

Acked-by: Rob Herring <robh@kernel.org>

>  drivers/mmc/host/mmci.c                        | 6 ++++++
>  2 files changed, 15 insertions(+)

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant
  2018-06-12 13:14 [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
                   ` (18 preceding siblings ...)
  2018-06-12 13:14 ` [PATCH 19/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
@ 2018-06-29 13:51 ` Ludovic BARRE
  2018-06-29 15:18   ` Ulf Hansson
  19 siblings, 1 reply; 41+ messages in thread
From: Ludovic BARRE @ 2018-06-29 13:51 UTC (permalink / raw)
  To: Ulf Hansson, Rob Herring
  Cc: Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	linux-arm-kernel, linux-kernel, devicetree, linux-mmc

hi Ulf

I know that you very busy on other task,
but did you have time to look my serie.
do you have first feedback ?

BR
Ludo

On 06/12/2018 03:14 PM, Ludovic Barre wrote:
> From: Ludovic Barre <ludovic.barre@st.com>
> 
> This patch series adapts mmci driver to add support for stm32
> sdmmc variant. stm32h7 SoC integrates the first revision of
> stm32 sdmmc.
> 
> This series is composed of 3 parts:
> -Prepare mmci driver to manage dma interfaces by adding property.
>   New mmci dma API is defined according to the legacy needs.
> -Adapt mmci driver to dedicated constraints of stm32 sdmmc variant,
>   defined under some specific properties.
> -Add stm32 sdmmc variant. As Internal DMA way satisfies data
> transfer, the mmci driver hasn't been modified for pio_read/write.
> Specific adds-ons to stm32 sdmmc:
>   + registers
>   + clk/power functions
>   + idma interface
> 
> Ludovic Barre (19):
>    mmc: mmci: regroup and define dma operations
>    mmc: mmci: merge qcom dml feature into mmci dma
>    mmc: mmci: add datactrl block size variant property
>    mmc: mmci: expand startbiterr to irqmask and error check
>    mmc: mmci: allow to overwrite clock/power procedure to specific
>      variant
>    mmc: mmci: add variant properties to define cpsm & cmdresp bits
>    mmc: mmci: add variant property to define dpsm bit
>    mmc: mmci: add variant property to define irq pio mask
>    mmc: mmci: add variant property to write datactrl before command
>    mmc: mmci: add variant property to allow remain data
>    mmc: mmci: add variant property to check specific data constraint
>    mmc: mmci: add variant property to request a reset
>    mmc: mmci: send stop cmd if a data command fail
>    mmc: mmci: add clock divider for stm32 sdmmc
>    mmc: mmci: add stm32 sdmmc registers
>    mmc: mmci: add DT bindings for STM32 sdmmc
>    mmc: mmci: add stm32 sdmmc idma support
>    mmc: mmci: add specific clk/pwr procedure for stm32 sdmmc
>    mmc: mmci: add stm32 sdmmc variant
> 
>   Documentation/devicetree/bindings/mmc/mmci.txt |  11 +
>   drivers/mmc/host/Makefile                      |   3 +-
>   drivers/mmc/host/mmci.c                        | 846 +++++++++++--------------
>   drivers/mmc/host/mmci.h                        | 237 ++++++-
>   drivers/mmc/host/mmci_dma.c                    | 780 +++++++++++++++++++++++
>   drivers/mmc/host/mmci_dma.h                    |  33 +
>   drivers/mmc/host/mmci_qcom_dml.c               | 177 ------
>   drivers/mmc/host/mmci_qcom_dml.h               |  31 -
>   8 files changed, 1410 insertions(+), 708 deletions(-)
>   create mode 100644 drivers/mmc/host/mmci_dma.c
>   create mode 100644 drivers/mmc/host/mmci_dma.h
>   delete mode 100644 drivers/mmc/host/mmci_qcom_dml.c
>   delete mode 100644 drivers/mmc/host/mmci_qcom_dml.h
> 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant
  2018-06-29 13:51 ` [PATCH 00/19] " Ludovic BARRE
@ 2018-06-29 15:18   ` Ulf Hansson
  0 siblings, 0 replies; 41+ messages in thread
From: Ulf Hansson @ 2018-06-29 15:18 UTC (permalink / raw)
  To: Ludovic BARRE
  Cc: Rob Herring, Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	Linux ARM, Linux Kernel Mailing List, devicetree, linux-mmc

On 29 June 2018 at 15:51, Ludovic BARRE <ludovic.barre@st.com> wrote:
> hi Ulf
>
> I know that you very busy on other task,
> but did you have time to look my serie.
> do you have first feedback ?

Apologize for the delay. I am going to catch up on mmc next week.

At first glance, this looks reasonable.

Kind regards
Uffe

>
> BR
> Ludo
>
>
> On 06/12/2018 03:14 PM, Ludovic Barre wrote:
>>
>> From: Ludovic Barre <ludovic.barre@st.com>
>>
>> This patch series adapts mmci driver to add support for stm32
>> sdmmc variant. stm32h7 SoC integrates the first revision of
>> stm32 sdmmc.
>>
>> This series is composed of 3 parts:
>> -Prepare mmci driver to manage dma interfaces by adding property.
>>   New mmci dma API is defined according to the legacy needs.
>> -Adapt mmci driver to dedicated constraints of stm32 sdmmc variant,
>>   defined under some specific properties.
>> -Add stm32 sdmmc variant. As Internal DMA way satisfies data
>> transfer, the mmci driver hasn't been modified for pio_read/write.
>> Specific adds-ons to stm32 sdmmc:
>>   + registers
>>   + clk/power functions
>>   + idma interface
>>
>> Ludovic Barre (19):
>>    mmc: mmci: regroup and define dma operations
>>    mmc: mmci: merge qcom dml feature into mmci dma
>>    mmc: mmci: add datactrl block size variant property
>>    mmc: mmci: expand startbiterr to irqmask and error check
>>    mmc: mmci: allow to overwrite clock/power procedure to specific
>>      variant
>>    mmc: mmci: add variant properties to define cpsm & cmdresp bits
>>    mmc: mmci: add variant property to define dpsm bit
>>    mmc: mmci: add variant property to define irq pio mask
>>    mmc: mmci: add variant property to write datactrl before command
>>    mmc: mmci: add variant property to allow remain data
>>    mmc: mmci: add variant property to check specific data constraint
>>    mmc: mmci: add variant property to request a reset
>>    mmc: mmci: send stop cmd if a data command fail
>>    mmc: mmci: add clock divider for stm32 sdmmc
>>    mmc: mmci: add stm32 sdmmc registers
>>    mmc: mmci: add DT bindings for STM32 sdmmc
>>    mmc: mmci: add stm32 sdmmc idma support
>>    mmc: mmci: add specific clk/pwr procedure for stm32 sdmmc
>>    mmc: mmci: add stm32 sdmmc variant
>>
>>   Documentation/devicetree/bindings/mmc/mmci.txt |  11 +
>>   drivers/mmc/host/Makefile                      |   3 +-
>>   drivers/mmc/host/mmci.c                        | 846
>> +++++++++++--------------
>>   drivers/mmc/host/mmci.h                        | 237 ++++++-
>>   drivers/mmc/host/mmci_dma.c                    | 780
>> +++++++++++++++++++++++
>>   drivers/mmc/host/mmci_dma.h                    |  33 +
>>   drivers/mmc/host/mmci_qcom_dml.c               | 177 ------
>>   drivers/mmc/host/mmci_qcom_dml.h               |  31 -
>>   8 files changed, 1410 insertions(+), 708 deletions(-)
>>   create mode 100644 drivers/mmc/host/mmci_dma.c
>>   create mode 100644 drivers/mmc/host/mmci_dma.h
>>   delete mode 100644 drivers/mmc/host/mmci_qcom_dml.c
>>   delete mode 100644 drivers/mmc/host/mmci_qcom_dml.h
>>
>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 13/19] mmc: mmci: send stop cmd if a data command fail
  2018-06-12 13:14 ` [PATCH 13/19] mmc: mmci: send stop cmd if a data command fail Ludovic Barre
@ 2018-07-04 13:37   ` Ulf Hansson
  2018-07-11  8:57     ` Ludovic BARRE
  0 siblings, 1 reply; 41+ messages in thread
From: Ulf Hansson @ 2018-07-04 13:37 UTC (permalink / raw)
  To: Ludovic Barre
  Cc: Rob Herring, Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	Linux ARM, Linux Kernel Mailing List, devicetree, linux-mmc

On 12 June 2018 at 15:14, Ludovic Barre <ludovic.Barre@st.com> wrote:
> From: Ludovic Barre <ludovic.barre@st.com>
>
> The mmc framework follows the requirement of SD_Specification:
> the STOP_TRANSMISSION is sent on multiple write/read commands
> and the stop command (alone), not needed on other ADTC commands.
>
> But, some variants require a stop command "STOP_TRANSMISION" to clear
> the DPSM "Data Path State Machine" if an error happens on command or data
> step. If it's not done the next data command will freeze hardware block.
> Needed to support the STM32 sdmmc variant.
>
> Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
> ---
>  drivers/mmc/host/mmci.c | 36 +++++++++++++++++++++++++++++++-----
>  drivers/mmc/host/mmci.h |  3 +++
>  2 files changed, 34 insertions(+), 5 deletions(-)
>
> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
> index 87724e1..9af7db8 100644
> --- a/drivers/mmc/host/mmci.c
> +++ b/drivers/mmc/host/mmci.c
> @@ -24,6 +24,7 @@
>  #include <linux/mmc/pm.h>
>  #include <linux/mmc/host.h>
>  #include <linux/mmc/card.h>
> +#include <linux/mmc/mmc.h>
>  #include <linux/mmc/slot-gpio.h>
>  #include <linux/amba/bus.h>
>  #include <linux/clk.h>
> @@ -522,11 +523,28 @@ static void mmci_set_mask1(struct mmci_host *host, unsigned int mask)
>         host->mask1_reg = mask;
>  }
>
> -static void mmci_stop_data(struct mmci_host *host)
> +static int mmci_stop_data(struct mmci_host *host)

Let's leave this as void function and instead add a check in
mmci_start_command(), to see if the command is a
MMC_STOP_TRANSMISSION. If so, then add host->variant->cmdreg_stop to
the register that is written in it.

And actually, I think this change alone should be a separate patch
coming before $subject patch in the series, as it addresses the first
problem of two.

>  {
> +       struct mmc_command *stop = &host->stop_abort;
> +       struct mmc_data *data = host->data;
> +       unsigned int cmd = 0;
> +
>         mmci_write_datactrlreg(host, 0);
>         mmci_set_mask1(host, 0);
>         host->data = NULL;
> +
> +       if (host->variant->cmdreg_stop) {
> +               cmd |= host->variant->cmdreg_stop;
> +               if (!data->stop) {
> +                       memset(stop, 0, sizeof(struct mmc_command));
> +                       stop->opcode = MMC_STOP_TRANSMISSION;
> +                       stop->arg = 0;
> +                       stop->flags = MMC_RSP_R1B | MMC_CMD_AC;
> +                       data->stop = stop;

I don't understand why you want the host->stop_abort being present in
the mmci host struct? Can we get rid of that?

Anyway, re-using the data->stop pointer seems reasonable.

> +               }
> +       }
> +
> +       return cmd;
>  }
>
>  static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data)
> @@ -703,6 +721,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
>               unsigned int status)
>  {
>         unsigned int status_err;
> +       unsigned int cmd_reg = 0;
>
>         /* Make sure we have data to handle */
>         if (!data)
> @@ -761,7 +780,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
>         if (status & MCI_DATAEND || data->error) {
>                 mmci_dma_finalize(host, data);
>
> -               mmci_stop_data(host);
> +               cmd_reg = mmci_stop_data(host);
>
>                 if (!data->error)
>                         /* The error clause is handled above, success! */
> @@ -770,7 +789,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
>                 if (!data->stop || host->mrq->sbc) {
>                         mmci_request_end(host, data->mrq);
>                 } else {
> -                       mmci_start_command(host, data->stop, 0);
> +                       mmci_start_command(host, data->stop, cmd_reg);
>                 }
>         }
>  }
> @@ -780,6 +799,8 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
>              unsigned int status)
>  {
>         void __iomem *base = host->base;
> +       struct mmc_data *data = host->data;
> +       unsigned int cmd_reg = 0;
>         bool sbc;
>
>         if (!cmd)
> @@ -865,11 +886,16 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
>         }
>
>         if ((!sbc && !cmd->data) || cmd->error) {
> -               if (host->data) {
> +               if (data) {
>                         /* Terminate the DMA transfer */
>                         mmci_dma_error(host);
>
> -                       mmci_stop_data(host);
> +                       cmd_reg = mmci_stop_data(host);
> +
> +                       if (data->stop) {

This means a change in behavior for !host->variant->cmdreg_stop
variants, because the mmc core may have assigned data->stop. Did you
test this path for any of the old variants?

The change as such anyway seems like a good idea. If we had data, we
may play safe and send a stop command to let the card to try to close
down the transfer. On the other hand, we don't want this to come as
side-effect, but rather it deserves is own standalone patch.

I guess there are two options going forward.
1) Split the patch and see if this works for other variants first and
the add your changes on top for the new ST variant.
2) Make this change only to affect the new ST variant.

I am fine with both.

> +                               mmci_start_command(host, data->stop, cmd_reg);
> +                               return;
> +                       }
>                 }
>                 mmci_request_end(host, host->mrq);
>         } else if (sbc) {
> diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
> index 2d7e901..4b4cd1d 100644
> --- a/drivers/mmc/host/mmci.h
> +++ b/drivers/mmc/host/mmci.h
> @@ -243,6 +243,7 @@ struct mmci_host;
>   * @cmdreg_lrsp_crc: enable value for long response with crc
>   * @cmdreg_srsp_crc: enable value for short response with crc
>   * @cmdreg_srsp: enable value for short response without crc
> + * @cmdreg_stop: enable value for stop and abort transmission
>   * @datalength_bits: number of bits in the MMCIDATALENGTH register
>   * @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY
>   *           is asserted (likewise for RX)
> @@ -298,6 +299,7 @@ struct variant_data {
>         unsigned int            cmdreg_lrsp_crc;
>         unsigned int            cmdreg_srsp_crc;
>         unsigned int            cmdreg_srsp;
> +       unsigned int            cmdreg_stop;
>         unsigned int            datalength_bits;
>         unsigned int            fifosize;
>         unsigned int            fifohalfsize;
> @@ -343,6 +345,7 @@ struct mmci_host {
>         struct mmc_request      *mrq;
>         struct mmc_command      *cmd;
>         struct mmc_data         *data;
> +       struct mmc_command      stop_abort;
>         struct mmc_host         *mmc;
>         struct clk              *clk;
>         bool                    singleirq;
> --
> 2.7.4
>

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 05/19] mmc: mmci: allow to overwrite clock/power procedure to specific variant
  2018-06-12 13:14 ` [PATCH 05/19] mmc: mmci: allow to overwrite clock/power procedure to specific variant Ludovic Barre
@ 2018-07-05 13:48   ` Ulf Hansson
  2018-07-11 12:19     ` Ludovic BARRE
  0 siblings, 1 reply; 41+ messages in thread
From: Ulf Hansson @ 2018-07-05 13:48 UTC (permalink / raw)
  To: Ludovic Barre
  Cc: Rob Herring, Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	Linux ARM, Linux Kernel Mailing List, devicetree, linux-mmc

On 12 June 2018 at 15:14, Ludovic Barre <ludovic.Barre@st.com> wrote:
> From: Ludovic Barre <ludovic.barre@st.com>
>
> A specific variant could have different power or clock procedures.
> This patch allows to overwrite the default mmci_set_clkreg and
> mmci_set_pwrreg for a specific variant.
>
> Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
> ---
>  drivers/mmc/host/mmci.c | 96 +++++++++++++++++++++++++++++--------------------
>  drivers/mmc/host/mmci.h |  7 ++++
>  2 files changed, 64 insertions(+), 39 deletions(-)
>
> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
> index ede95b7..801c86b 100644
> --- a/drivers/mmc/host/mmci.c
> +++ b/drivers/mmc/host/mmci.c
> @@ -374,6 +374,52 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
>         mmci_write_clkreg(host, clk);
>  }
>
> +static void mmci_set_pwrreg(struct mmci_host *host, unsigned char power_mode,
> +                           unsigned int pwr)
> +{
> +       struct variant_data *variant = host->variant;
> +       struct mmc_host *mmc = host->mmc;
> +
> +       switch (power_mode) {
> +       case MMC_POWER_OFF:
> +               if (!IS_ERR(mmc->supply.vmmc))
> +                       mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
> +
> +               if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) {
> +                       regulator_disable(mmc->supply.vqmmc);
> +                       host->vqmmc_enabled = false;
> +               }
> +
> +               break;
> +       case MMC_POWER_UP:
> +               if (!IS_ERR(mmc->supply.vmmc))
> +                       mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
> +                                             mmc->ios.vdd);
> +
> +               /*
> +                * The ST Micro variant doesn't have the PL180s MCI_PWR_UP
> +                * and instead uses MCI_PWR_ON so apply whatever value is
> +                * configured in the variant data.
> +                */
> +               pwr |= variant->pwrreg_powerup;
> +
> +               break;
> +       case MMC_POWER_ON:
> +               if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) {
> +                       if (regulator_enable(mmc->supply.vqmmc) < 0)
> +                               dev_err(mmc_dev(mmc),
> +                                       "failed to enable vqmmc regulator\n");
> +                       else
> +                               host->vqmmc_enabled = true;
> +               }
> +
> +               pwr |= MCI_PWR_ON;
> +               break;
> +       }
> +
> +       mmci_write_pwrreg(host, pwr);
> +}
> +
>  static void
>  mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
>  {
> @@ -1031,7 +1077,7 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
>  {
>         struct mmci_host *host = mmc_priv(mmc);
>         struct variant_data *variant = host->variant;
> -       u32 pwr = 0;
> +       unsigned int pwr = 0;

?

>         unsigned long flags;
>         int ret;
>
> @@ -1039,42 +1085,6 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
>                 host->plat->ios_handler(mmc_dev(mmc), ios))
>                         dev_err(mmc_dev(mmc), "platform ios_handler failed\n");
>
> -       switch (ios->power_mode) {
> -       case MMC_POWER_OFF:
> -               if (!IS_ERR(mmc->supply.vmmc))
> -                       mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
> -
> -               if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) {
> -                       regulator_disable(mmc->supply.vqmmc);
> -                       host->vqmmc_enabled = false;
> -               }
> -
> -               break;
> -       case MMC_POWER_UP:
> -               if (!IS_ERR(mmc->supply.vmmc))
> -                       mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
> -
> -               /*
> -                * The ST Micro variant doesn't have the PL180s MCI_PWR_UP
> -                * and instead uses MCI_PWR_ON so apply whatever value is
> -                * configured in the variant data.
> -                */
> -               pwr |= variant->pwrreg_powerup;
> -
> -               break;
> -       case MMC_POWER_ON:
> -               if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) {
> -                       ret = regulator_enable(mmc->supply.vqmmc);
> -                       if (ret < 0)
> -                               dev_err(mmc_dev(mmc),
> -                                       "failed to enable vqmmc regulator\n");
> -                       else
> -                               host->vqmmc_enabled = true;
> -               }
> -
> -               pwr |= MCI_PWR_ON;
> -               break;
> -       }

This above looks like pure re-factoring. Please make the above change
a separate patch.

>
>         if (variant->signal_direction && ios->power_mode != MMC_POWER_OFF) {
>                 /*
> @@ -1126,8 +1136,16 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
>
>         spin_lock_irqsave(&host->lock, flags);
>
> -       mmci_set_clkreg(host, ios->clock);
> -       mmci_write_pwrreg(host, pwr);
> +       if (variant->set_clkreg)
> +               variant->set_clkreg(host, ios->clock);
> +       else
> +               mmci_set_clkreg(host, ios->clock);

This means a change in behavior, which I don't think is what you want.

1) The spin lock will be held while doing the regulator operations.
2) The clock register will be written to before the regulator
operations have been done. Not sure if that works fine!?

An overall comment, I think we should create a mmci_host_ops structure
and put the needed callbacks in there (kept to a minimum of course),
rather than putting them as a part of the variant struct. More
importantly, also the legacy mmci variants should get a mmci_host_ops
structure assigned during probe.

The point is, I think it makes the code above (and future wise) more
flexible. It should also allow us to better share functions between
the variants. In principle, I expect that we end up with a bunch of
"library" mmci functions that can be invoked from variant's
mmci_host_ops (if not assigned directly).

> +
> +       if (variant->set_pwrreg)
> +               variant->set_pwrreg(host, ios->power_mode, pwr);
> +       else
> +               mmci_set_pwrreg(host, ios->power_mode, pwr);
> +
>         mmci_reg_delay(host);
>
>         spin_unlock_irqrestore(&host->lock, flags);
> diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
> index 2ba9640..7265ca6 100644
> --- a/drivers/mmc/host/mmci.h
> +++ b/drivers/mmc/host/mmci.h
> @@ -231,6 +231,7 @@
>
>  struct clk;
>  struct dma_chan;
> +struct mmci_host;
>
>  /**
>   * struct variant_data - MMCI variant-specific quirks
> @@ -273,6 +274,8 @@ struct dma_chan;
>   *            register.
>   * @opendrain: bitmask identifying the OPENDRAIN bit inside MMCIPOWER register
>   * @mmci_dma: Pointer to platform-specific DMA callbacks.
> + * @set_clk_ios: if clock procedure of variant is specific
> + * @set_pwr_ios: if power procedure of variant is specific
>   */
>  struct variant_data {
>         unsigned int            clkreg;
> @@ -307,6 +310,9 @@ struct variant_data {
>         u32                     start_err;
>         u32                     opendrain;
>         struct mmci_dma_ops     *mmci_dma;
> +       void (*set_clkreg)(struct mmci_host *host, unsigned int desired);
> +       void (*set_pwrreg)(struct mmci_host *host, unsigned char power_mode,
> +                          unsigned int pwr);
>  };
>
>  struct mmci_host {
> @@ -328,6 +334,7 @@ struct mmci_host {
>         u32                     pwr_reg;
>         u32                     pwr_reg_add;
>         u32                     clk_reg;
> +       u32                     clk_reg_add;

What's this? Some leftover I guess?

>         u32                     datactrl_reg;
>         u32                     busy_status;
>         u32                     mask1_reg;
> --
> 2.7.4
>

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 10/19] mmc: mmci: add variant property to allow remain data
  2018-06-12 13:14 ` [PATCH 10/19] mmc: mmci: add variant property to allow remain data Ludovic Barre
@ 2018-07-05 13:55   ` Ulf Hansson
  0 siblings, 0 replies; 41+ messages in thread
From: Ulf Hansson @ 2018-07-05 13:55 UTC (permalink / raw)
  To: Ludovic Barre
  Cc: Rob Herring, Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	Linux ARM, Linux Kernel Mailing List, devicetree, linux-mmc

On 12 June 2018 at 15:14, Ludovic Barre <ludovic.Barre@st.com> wrote:
> From: Ludovic Barre <ludovic.barre@st.com>
>
> This patch adds a boolean property to read remaining data.
> Needed to support the STM32 sdmmc variant. MMCIDATACNT
> register should be read only after the data transfer is complete.
> When reading after an error event the read data count value may be
> different from the real number of data bytes transferred.
>
> Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
> ---
>  drivers/mmc/host/mmci.c | 17 +++++++++++++++--
>  drivers/mmc/host/mmci.h |  3 +++
>  2 files changed, 18 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
> index 83c3572..abddad7 100644
> --- a/drivers/mmc/host/mmci.c
> +++ b/drivers/mmc/host/mmci.c
> @@ -58,6 +58,7 @@ static struct variant_data variant_arm = {
>         .datalength_bits        = 16,
>         .datactrl_blocksz       = 11,
>         .datactrl_dpsm_enable   = MCI_DPSM_ENABLE,
> +       .datacnt_remain         = true,

According to the description in the changelog, it sounds like
MMCIDATACNT is broken (or useless for the STM32 variant). I suggest to
rename the new variant to something along those lines instead.

Like "datacnt_broken", then you only need to set it to true for the
STM32 variant and legacy variants can remain as is.

>         .pwrreg_powerup         = MCI_PWR_UP,
>         .f_max                  = 100000000,
>         .reversed_irq_handling  = true,
> @@ -78,6 +79,7 @@ static struct variant_data variant_arm_extended_fifo = {
>         .datalength_bits        = 16,
>         .datactrl_blocksz       = 11,
>         .datactrl_dpsm_enable   = MCI_DPSM_ENABLE,
> +       .datacnt_remain         = true,
>         .pwrreg_powerup         = MCI_PWR_UP,
>         .f_max                  = 100000000,
>         .mmcimask1              = true,
> @@ -98,6 +100,7 @@ static struct variant_data variant_arm_extended_fifo_hwfc = {
>         .datalength_bits        = 16,
>         .datactrl_blocksz       = 11,
>         .datactrl_dpsm_enable   = MCI_DPSM_ENABLE,
> +       .datacnt_remain         = true,
>         .pwrreg_powerup         = MCI_PWR_UP,
>         .f_max                  = 100000000,
>         .mmcimask1              = true,
> @@ -120,6 +123,7 @@ static struct variant_data variant_u300 = {
>         .datactrl_blocksz       = 11,
>         .datactrl_dpsm_enable   = MCI_DPSM_ENABLE,
>         .datactrl_mask_sdio     = MCI_DPSM_ST_SDIOEN,
> +       .datacnt_remain         = true,
>         .st_sdio                        = true,
>         .pwrreg_powerup         = MCI_PWR_ON,
>         .f_max                  = 100000000,
> @@ -146,6 +150,7 @@ static struct variant_data variant_nomadik = {
>         .datactrl_blocksz       = 11,
>         .datactrl_dpsm_enable   = MCI_DPSM_ENABLE,
>         .datactrl_mask_sdio     = MCI_DPSM_ST_SDIOEN,
> +       .datacnt_remain         = true,
>         .st_sdio                = true,
>         .st_clkdiv              = true,
>         .pwrreg_powerup         = MCI_PWR_ON,
> @@ -175,6 +180,7 @@ static struct variant_data variant_ux500 = {
>         .datactrl_blocksz       = 11,
>         .datactrl_dpsm_enable   = MCI_DPSM_ENABLE,
>         .datactrl_mask_sdio     = MCI_DPSM_ST_SDIOEN,
> +       .datacnt_remain         = true,
>         .st_sdio                = true,
>         .st_clkdiv              = true,
>         .pwrreg_powerup         = MCI_PWR_ON,
> @@ -209,6 +215,7 @@ static struct variant_data variant_ux500v2 = {
>         .datactrl_blocksz       = 11,
>         .datactrl_dpsm_enable   = MCI_DPSM_ENABLE,
>         .datactrl_mask_sdio     = MCI_DPSM_ST_SDIOEN,
> +       .datacnt_remain         = true,
>         .st_sdio                = true,
>         .st_clkdiv              = true,
>         .blksz_datactrl16       = true,
> @@ -244,6 +251,7 @@ static struct variant_data variant_stm32 = {
>         .datactrl_blocksz       = 11,
>         .datactrl_dpsm_enable   = MCI_DPSM_ENABLE,
>         .datactrl_mask_sdio     = MCI_DPSM_ST_SDIOEN,
> +       .datacnt_remain         = true,
>         .st_sdio                = true,
>         .st_clkdiv              = true,
>         .pwrreg_powerup         = MCI_PWR_ON,
> @@ -270,6 +278,7 @@ static struct variant_data variant_qcom = {
>         .datalength_bits        = 24,
>         .datactrl_blocksz       = 11,
>         .datactrl_dpsm_enable   = MCI_DPSM_ENABLE,
> +       .datacnt_remain         = true,
>         .pwrreg_powerup         = MCI_PWR_UP,
>         .f_max                  = 208000000,
>         .explicit_mclk_control  = true,
> @@ -711,8 +720,12 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
>                  * can be as much as a FIFO-worth of data ahead.  This
>                  * matters for FIFO overruns only.
>                  */
> -               remain = readl(host->base + MMCIDATACNT);
> -               success = data->blksz * data->blocks - remain;
> +               if (host->variant->datacnt_remain) {
> +                       remain = readl(host->base + MMCIDATACNT);
> +                       success = data->blksz * data->blocks - remain;
> +               } else {
> +                       success = 0;
> +               }
>
>                 dev_dbg(mmc_dev(host->mmc), "MCI ERROR IRQ, status 0x%08x at 0x%08x\n",
>                         status_err, success);
> diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
> index 5091025..12ee2e6 100644
> --- a/drivers/mmc/host/mmci.h
> +++ b/drivers/mmc/host/mmci.h
> @@ -260,6 +260,8 @@ struct mmci_host;
>   * @datactrl_blksz: block size in power of two
>   * @datactrl_dpsm_enable: enable value for DPSM
>   * @datactrl_first: true if data must be setup before send command
> + * @datacnt_remain: true if you could read datacnt register
> + *                  to define remain data
>   * @pwrreg_powerup: power up value for MMCIPOWER register
>   * @f_max: maximum clk frequency supported by the controller.
>   * @signal_direction: input/out direction of bus signals can be indicated
> @@ -303,6 +305,7 @@ struct variant_data {
>         unsigned int            datactrl_blocksz;
>         unsigned int            datactrl_dpsm_enable;
>         bool                    datactrl_first;
> +       bool                    datacnt_remain;
>         bool                    st_sdio;
>         bool                    st_clkdiv;
>         bool                    blksz_datactrl16;
> --
> 2.7.4
>

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 06/19] mmc: mmci: add variant properties to define cpsm & cmdresp bits
  2018-06-12 13:14 ` [PATCH 06/19] mmc: mmci: add variant properties to define cpsm & cmdresp bits Ludovic Barre
@ 2018-07-05 14:20   ` Ulf Hansson
  0 siblings, 0 replies; 41+ messages in thread
From: Ulf Hansson @ 2018-07-05 14:20 UTC (permalink / raw)
  To: Ludovic Barre
  Cc: Rob Herring, Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	Linux ARM, Linux Kernel Mailing List, devicetree, linux-mmc

On 12 June 2018 at 15:14, Ludovic Barre <ludovic.Barre@st.com> wrote:
> From: Ludovic Barre <ludovic.barre@st.com>
>
> This patch adds command variant properties to define
> cpsm enable bit and responses.
> Needed to support the STM32 variant (shift of cpsm bit,
> specific definition of commands response).
>
> Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
> ---
>  drivers/mmc/host/mmci.c | 47 +++++++++++++++++++++++++++++++++++++++++++----
>  drivers/mmc/host/mmci.h |  8 ++++++++
>  2 files changed, 51 insertions(+), 4 deletions(-)
>
> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
> index 801c86b..52562fc 100644
> --- a/drivers/mmc/host/mmci.c
> +++ b/drivers/mmc/host/mmci.c
> @@ -51,6 +51,10 @@ static unsigned int fmax = 515633;
>  static struct variant_data variant_arm = {
>         .fifosize               = 16 * 4,
>         .fifohalfsize           = 8 * 4,
> +       .cmdreg_cpsm_enable     = MCI_CPSM_ENABLE,
> +       .cmdreg_lrsp_crc        = MCI_CPSM_RESPONSE | MCI_CPSM_LONGRSP,
> +       .cmdreg_srsp_crc        = MCI_CPSM_RESPONSE,
> +       .cmdreg_srsp            = MCI_CPSM_RESPONSE,

Do these really needs to be a superset of each other?

Maybe it becomes easier for the STM32 variant later though...

[...]

> @@ -603,16 +639,19 @@ mmci_start_command(struct mmci_host *host, struct mmc_command *cmd, u32 c)

While at it, would you mind folding in a cleanup patch removing the
"u32 c" as in-parameter? It's currently always set to "0" by the
caller, so it's not needed.

>         dev_dbg(mmc_dev(host->mmc), "op %02x arg %08x flags %08x\n",
>             cmd->opcode, cmd->arg, cmd->flags);
>
> -       if (readl(base + MMCICOMMAND) & MCI_CPSM_ENABLE) {
> +       if (readl(base + MMCICOMMAND) & host->variant->cmdreg_cpsm_enable) {
>                 writel(0, base + MMCICOMMAND);
>                 mmci_reg_delay(host);
>         }
>
> -       c |= cmd->opcode | MCI_CPSM_ENABLE;
> +       c |= cmd->opcode | host->variant->cmdreg_cpsm_enable;
>         if (cmd->flags & MMC_RSP_PRESENT) {
>                 if (cmd->flags & MMC_RSP_136)
> -                       c |= MCI_CPSM_LONGRSP;
> -               c |= MCI_CPSM_RESPONSE;
> +                       c |= host->variant->cmdreg_lrsp_crc;

This looks weird. Probably because of the naming of the variant data.

Perhaps rename to "cmdreg_lrsp", thus skipping the "_crc" suffix?

> +               else if (cmd->flags & MMC_RSP_CRC)
> +                       c |= host->variant->cmdreg_srsp_crc;

Why is here an else? We can have both MMC_RSP_136 and MMC_RSP_CRC bits
set, right!?

> +               else
> +                       c |= host->variant->cmdreg_srsp;

What do you think about using a switch-case, perhaps with fall-through
- and then adding those bits that are needed for each response
bit+variant instead? Could that make this more readable?

>         }
>         if (/*interrupt*/0)
>                 c |= MCI_CPSM_INTERRUPT;
> diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
> index 7265ca6..e173305 100644
> --- a/drivers/mmc/host/mmci.h
> +++ b/drivers/mmc/host/mmci.h
> @@ -239,6 +239,10 @@ struct mmci_host;
>   * @clkreg_enable: enable value for MMCICLOCK register
>   * @clkreg_8bit_bus_enable: enable value for 8 bit bus
>   * @clkreg_neg_edge_enable: enable value for inverted data/cmd output
> + * @cmdreg_cpsm_enable: enable value for CPSM
> + * @cmdreg_lrsp_crc: enable value for long response with crc
> + * @cmdreg_srsp_crc: enable value for short response with crc
> + * @cmdreg_srsp: enable value for short response without crc
>   * @datalength_bits: number of bits in the MMCIDATALENGTH register
>   * @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY
>   *           is asserted (likewise for RX)
> @@ -282,6 +286,10 @@ struct variant_data {
>         unsigned int            clkreg_enable;
>         unsigned int            clkreg_8bit_bus_enable;
>         unsigned int            clkreg_neg_edge_enable;
> +       unsigned int            cmdreg_cpsm_enable;
> +       unsigned int            cmdreg_lrsp_crc;
> +       unsigned int            cmdreg_srsp_crc;
> +       unsigned int            cmdreg_srsp;
>         unsigned int            datalength_bits;
>         unsigned int            fifosize;
>         unsigned int            fifohalfsize;
> --
> 2.7.4
>

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 18/19] mmc: mmci: add specific clk/pwr procedure for stm32 sdmmc
  2018-06-12 13:14 ` [PATCH 18/19] mmc: mmci: add specific clk/pwr procedure for stm32 sdmmc Ludovic Barre
@ 2018-07-05 14:49   ` Ulf Hansson
  0 siblings, 0 replies; 41+ messages in thread
From: Ulf Hansson @ 2018-07-05 14:49 UTC (permalink / raw)
  To: Ludovic Barre
  Cc: Rob Herring, Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	Linux ARM, Linux Kernel Mailing List, devicetree, linux-mmc

On 12 June 2018 at 15:14, Ludovic Barre <ludovic.Barre@st.com> wrote:
> From: Ludovic Barre <ludovic.barre@st.com>
>
> This patch adds specific clock and power ios for stm32 sdmmc variant.
> power ios: stm32 dedicated procedure must be done to perform power
> off/on procedures. To power off, the sdmmc must be reset and set
> to power cycle state before to disabling vqmmc. This drives low
> SDMMC_D[7:0], SDMMC_CMD and SDMMC_CK to prevent the Card from
> being supplied through the signal lines.
> To power on, set the SDMMC in power-off SDMMC_D[7:0], SDMMC_CMD
> and SDMMC_CK are driven high. Then we can set the SDMMC to
> Power-on state.
>
> clock ios: specific bits behavior:
> -clock divider card_clk = mclk / (2 * clkdiv)
> -ddr activation
> -wide bus 1/4/8bits
> -bus speed
> -receive clock selection (in_ck/ck_in/fb_ck)
>
> Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
> ---
>  drivers/mmc/host/mmci.c | 112 ++++++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 112 insertions(+)
>
> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
> index 86aef4f..af27a0a 100644
> --- a/drivers/mmc/host/mmci.c
> +++ b/drivers/mmc/host/mmci.c
> @@ -50,6 +50,10 @@
>
>  static unsigned int fmax = 515633;
>
> +static void mmci_sdmmc_set_pwrreg(struct mmci_host *host,
> +                                 unsigned char power_mode, unsigned int pwr);
> +static void mmci_sdmmc_set_clkreg(struct mmci_host *host, unsigned int desired);
> +
>  static struct variant_data variant_arm = {
>         .fifosize               = 16 * 4,
>         .fifohalfsize           = 8 * 4,
> @@ -490,6 +494,114 @@ static void mmci_set_pwrreg(struct mmci_host *host, unsigned char power_mode,
>         mmci_write_pwrreg(host, pwr);
>  }
>
> +static void mmci_sdmmc_set_clkreg(struct mmci_host *host, unsigned int desired)
> +{
> +       unsigned int clk = 0, ddr = 0;
> +
> +       if (host->mmc->ios.timing == MMC_TIMING_MMC_DDR52 ||
> +           host->mmc->ios.timing == MMC_TIMING_UHS_DDR50)
> +               ddr = MCI_STM32_CLK_DDR;
> +
> +       /*
> +        * cclk = mclk / (2 * clkdiv)
> +        * clkdiv 0 => bypass
> +        * in ddr mode bypass is not possible
> +        */
> +       if (desired) {
> +               if (desired >= host->mclk && !ddr) {
> +                       host->cclk = host->mclk;
> +               } else {
> +                       clk = DIV_ROUND_UP(host->mclk, 2 * desired);
> +                       if (clk > MCI_STM32_CLK_CLKDIV_MSK)
> +                               clk = MCI_STM32_CLK_CLKDIV_MSK;
> +                       host->cclk = host->mclk / (2 * clk);
> +               }
> +       }
> +
> +       if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_4)
> +               clk |= MCI_STM32_CLK_WIDEBUS_4;
> +       if (host->mmc->ios.bus_width == MMC_BUS_WIDTH_8)
> +               clk |= MCI_STM32_CLK_WIDEBUS_8;
> +
> +       clk |= MCI_STM32_CLK_HWFCEN;
> +       clk |= host->clk_reg_add;
> +       clk |= ddr;
> +
> +       /*
> +        * SDMMC_FBCK is selected when an external Delay Block is needed
> +        * with SDR104.
> +        */
> +       if (host->mmc->ios.timing >= MMC_TIMING_UHS_SDR50) {
> +               clk |= MCI_STM32_CLK_BUSSPEED;
> +               if (host->mmc->ios.timing == MMC_TIMING_UHS_SDR104) {
> +                       clk &= ~MCI_STM32_CLK_SEL_MSK;
> +                       clk |= MCI_STM32_CLK_SELFBCK;
> +               }
> +       }
> +
> +       mmci_write_clkreg(host, clk);
> +}
> +
> +static void mmci_sdmmc_set_pwrreg(struct mmci_host *host,
> +                                 unsigned char power_mode, unsigned int pwr)
> +{
> +       struct mmc_host *mmc = host->mmc;
> +
> +       pwr |= host->pwr_reg_add;
> +
> +       switch (power_mode) {
> +       case MMC_POWER_OFF:
> +               if (!IS_ERR(mmc->supply.vmmc))
> +                       mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
> +
> +               /* Only a reset could disable sdmmc */
> +               reset_control_assert(host->rst);
> +               udelay(2);
> +               reset_control_deassert(host->rst);

Could you please elaborate on what goes on here. Do you need to
reset-the controller when powering off the card?

> +
> +               /* default mask (probe) must be activated */
> +               writel(MCI_IRQENABLE | host->variant->start_err,
> +                      host->base + MMCIMASK0);

The above seems like it could be made generic. There is no reason to
keep IRQs enabled when the card is powered off.

> +
> +               /*
> +                * Set the SDMMC in Power-cycle state before to disabling vqmmc.
> +                * This will make that the SDMMC_D[7:0], SDMMC_CMD and SDMMC_CK
> +                * are driven low, to prevent the Card from being supplied
> +                * through the signal lines.
> +                */
> +               mmci_write_pwrreg(host, MCI_STM32_PWR_CYC | pwr);
> +
> +               if (!IS_ERR(host->mmc->supply.vqmmc) && host->vqmmc_enabled) {
> +                       regulator_disable(host->mmc->supply.vqmmc);
> +                       host->vqmmc_enabled = false;
> +               }
> +               break;
> +       case MMC_POWER_UP:
> +               if (!IS_ERR(mmc->supply.vmmc))
> +                       mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
> +                                             mmc->ios.vdd);
> +               break;
> +       case MMC_POWER_ON:
> +               if (!IS_ERR(host->mmc->supply.vqmmc) && !host->vqmmc_enabled) {
> +                       if (regulator_enable(host->mmc->supply.vqmmc) < 0)
> +                               dev_err(mmc_dev(host->mmc),
> +                                       "failed to enable vqmmc regulator\n");
> +                       else
> +                               host->vqmmc_enabled = true;
> +               }
> +
> +               /*
> +                * After a power-cycle state, we must set the SDMMC in
> +                * Power-off. The SDMMC_D[7:0], SDMMC_CMD and SDMMC_CK are
> +                * driven high. Then we can set the SDMMC to Power-on state
> +                */
> +               mmci_write_pwrreg(host, MCI_PWR_OFF | pwr);
> +               mdelay(1);
> +               mmci_write_pwrreg(host, MCI_PWR_ON | pwr);

This looks odd. MMC_POWER_ON is the last power phase. For every single
additional ios change (timing, clock, etc), you will hit this piece of
code. In other words, there will be a mdelay(1) each time, which is
probably not needed.

> +               break;
> +       }
> +}
> +
>  static void
>  mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
>  {
> --
> 2.7.4
>

Overall I am wondering if mmci_sdmmc_set_pwrreg() can be made generic
inside the common mmci ->set_ios() functions instead.

For example, the reset can be optional and checked with
if(!IS_ERR(host->rst)) - and the others can probably be conditional
based on a variant data.

What do you think?

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 01/19] mmc: mmci: regroup and define dma operations
  2018-06-12 13:14 ` [PATCH 01/19] mmc: mmci: regroup and define dma operations Ludovic Barre
@ 2018-07-05 15:17   ` Ulf Hansson
  2018-07-11  9:41     ` Ludovic BARRE
  0 siblings, 1 reply; 41+ messages in thread
From: Ulf Hansson @ 2018-07-05 15:17 UTC (permalink / raw)
  To: Ludovic Barre
  Cc: Rob Herring, Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	Linux ARM, Linux Kernel Mailing List, devicetree, linux-mmc

On 12 June 2018 at 15:14, Ludovic Barre <ludovic.Barre@st.com> wrote:
> From: Ludovic Barre <ludovic.barre@st.com>
>
> Prepare mmci driver to manage dma interface by new property.
> This patch defines and regroups dma operations for mmci drivers.
> mmci_dma_XX prototypes are added to call member of mmci_dma_ops
> if not null. Based on legacy need, a mmci dma interface has been
> defined with:
> -mmci_dma_setup
> -mmci_dma_release
> -mmci_dma_pre_req
> -mmci_dma_start
> -mmci_dma_finalize
> -mmci_dma_post_req
> -mmci_dma_error
> -mmci_dma_get_next_data

As I suggested for one of the other patches, I would rather turn core
mmci functions into library functions, which can be either invoked
from variant callbacks or assigned directly to them.

In other words, I would leave the functions that you move in this
patch to stay in mmci.c. Although some needs to be re-factored and we
also needs to make some of them available to be called from another
file, hence the functions needs to be shared via mmci.h rather than
being declared static.

Let me take a concrete example on how I would move forward, hopefully
that explains it a bit better. Please see below.

[...]

> -/*
> - * All the DMA operation mode stuff goes inside this ifdef.
> - * This assumes that you have a generic DMA device interface,
> - * no custom DMA interfaces are supported.
> - */
> -#ifdef CONFIG_DMA_ENGINE
> -static void mmci_dma_setup(struct mmci_host *host)
> -{
> -       const char *rxname, *txname;
> -       struct variant_data *variant = host->variant;
> -
> -       host->dma_rx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "rx");
> -       host->dma_tx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "tx");
> -
> -       /* initialize pre request cookie */
> -       host->next_data.cookie = 1;
> -
> -       /*
> -        * If only an RX channel is specified, the driver will
> -        * attempt to use it bidirectionally, however if it is
> -        * is specified but cannot be located, DMA will be disabled.
> -        */
> -       if (host->dma_rx_channel && !host->dma_tx_channel)
> -               host->dma_tx_channel = host->dma_rx_channel;
> -
> -       if (host->dma_rx_channel)
> -               rxname = dma_chan_name(host->dma_rx_channel);
> -       else
> -               rxname = "none";
> -
> -       if (host->dma_tx_channel)
> -               txname = dma_chan_name(host->dma_tx_channel);
> -       else
> -               txname = "none";
> -
> -       dev_info(mmc_dev(host->mmc), "DMA channels RX %s, TX %s\n",
> -                rxname, txname);
> -
> -       /*
> -        * Limit the maximum segment size in any SG entry according to
> -        * the parameters of the DMA engine device.
> -        */
> -       if (host->dma_tx_channel) {
> -               struct device *dev = host->dma_tx_channel->device->dev;
> -               unsigned int max_seg_size = dma_get_max_seg_size(dev);
> -
> -               if (max_seg_size < host->mmc->max_seg_size)
> -                       host->mmc->max_seg_size = max_seg_size;
> -       }
> -       if (host->dma_rx_channel) {
> -               struct device *dev = host->dma_rx_channel->device->dev;
> -               unsigned int max_seg_size = dma_get_max_seg_size(dev);
> -
> -               if (max_seg_size < host->mmc->max_seg_size)
> -                       host->mmc->max_seg_size = max_seg_size;
> -       }

Everything above shall be left as generic library function,
mmci_dma_setup() and I would share it via mmci.h and thus change it
from being static.

> -
> -       if (variant->qcom_dml && host->dma_rx_channel && host->dma_tx_channel)
> -               if (dml_hw_init(host, host->mmc->parent->of_node))
> -                       variant->qcom_dml = false;

This piece of code, should be made specific to the qcom variant and
managed though a "mmci_host_ops" callback. The corresponding code in
that callback would then start by invoking mmci_dma_setup(), before it
continues with the qcom specific operations.

For legacy variants, the corresponding callback would be set directly
to mmci_dma_setup() and called through the callback from mmci.c when
needed. There is no need to have a separate file for DMA for the
legacy variants, I think.

[...]

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 02/19] mmc: mmci: merge qcom dml feature into mmci dma
  2018-06-12 13:14 ` [PATCH 02/19] mmc: mmci: merge qcom dml feature into mmci dma Ludovic Barre
@ 2018-07-05 15:26   ` Ulf Hansson
  2018-07-11 15:19     ` Ludovic BARRE
  0 siblings, 1 reply; 41+ messages in thread
From: Ulf Hansson @ 2018-07-05 15:26 UTC (permalink / raw)
  To: Ludovic Barre
  Cc: Rob Herring, Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	Linux ARM, Linux Kernel Mailing List, devicetree, linux-mmc

On 12 June 2018 at 15:14, Ludovic Barre <ludovic.Barre@st.com> wrote:
> From: Ludovic Barre <ludovic.barre@st.com>
>
> This patch integrates qcom dml feature into mmci_dma file.
> Qualcomm Data Mover lite/local is already a variant of mmci dmaengine.
>
> Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
> ---
>  drivers/mmc/host/Makefile        |   1 -
>  drivers/mmc/host/mmci.c          |   1 -
>  drivers/mmc/host/mmci.h          |  35 ++++++++
>  drivers/mmc/host/mmci_dma.c      | 135 ++++++++++++++++++++++++++++-
>  drivers/mmc/host/mmci_qcom_dml.c | 177 ---------------------------------------
>  drivers/mmc/host/mmci_qcom_dml.h |  31 -------
>  6 files changed, 169 insertions(+), 211 deletions(-)
>  delete mode 100644 drivers/mmc/host/mmci_qcom_dml.c
>  delete mode 100644 drivers/mmc/host/mmci_qcom_dml.h

No, this is not the way to go. Instead I I think there are two options.

1) Keep mmci_qcom_dml.c|h and thus add new files for the stm32 dma variant.

2) Start by renaming mmci_qcom_dml.* to mmc_dma.* and then in the next
step add the code for stm32 dma into the renamed files.

I guess if there is some overlap in functionality, 2) may be best as
it could easier avoid open coding. However, I am fine with whatever
option and I expect that you knows what is best.

Kind regards
Uffe

>
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index daecaa98..608a020 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -5,7 +5,6 @@
>
>  obj-$(CONFIG_MMC_ARMMMCI) += armmmci.o
>  armmmci-y := mmci.o mmci_dma.o
> -armmmci-$(CONFIG_MMC_QCOM_DML) += mmci_qcom_dml.o
>  obj-$(CONFIG_MMC_PXA)          += pxamci.o
>  obj-$(CONFIG_MMC_MXC)          += mxcmmc.o
>  obj-$(CONFIG_MMC_MXS)          += mxs-mmc.o
> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
> index 8868be0..7a15afd 100644
> --- a/drivers/mmc/host/mmci.c
> +++ b/drivers/mmc/host/mmci.c
> @@ -43,7 +43,6 @@
>
>  #include "mmci.h"
>  #include "mmci_dma.h"
> -#include "mmci_qcom_dml.h"
>
>  #define DRIVER_NAME "mmci-pl18x"
>
> diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
> index a73bb98..f7cba35 100644
> --- a/drivers/mmc/host/mmci.h
> +++ b/drivers/mmc/host/mmci.h
> @@ -194,6 +194,41 @@
>
>  #define MMCI_PINCTRL_STATE_OPENDRAIN "opendrain"
>
> +/* QCOM DML Registers */
> +#define DML_CONFIG                     0x00
> +#define PRODUCER_CRCI_MSK              GENMASK(1, 0)
> +#define PRODUCER_CRCI_DISABLE          0
> +#define PRODUCER_CRCI_X_SEL            BIT(0)
> +#define PRODUCER_CRCI_Y_SEL            BIT(1)
> +#define CONSUMER_CRCI_MSK              GENMASK(3, 2)
> +#define CONSUMER_CRCI_DISABLE          0
> +#define CONSUMER_CRCI_X_SEL            BIT(2)
> +#define CONSUMER_CRCI_Y_SEL            BIT(3)
> +#define PRODUCER_TRANS_END_EN          BIT(4)
> +#define BYPASS                         BIT(16)
> +#define DIRECT_MODE                    BIT(17)
> +#define INFINITE_CONS_TRANS            BIT(18)
> +
> +#define DML_SW_RESET                   0x08
> +#define DML_PRODUCER_START             0x0c
> +#define DML_CONSUMER_START             0x10
> +#define DML_PRODUCER_PIPE_LOGICAL_SIZE 0x14
> +#define DML_CONSUMER_PIPE_LOGICAL_SIZE 0x18
> +#define DML_PIPE_ID                    0x1c
> +#define PRODUCER_PIPE_ID_SHFT          0
> +#define PRODUCER_PIPE_ID_MSK           GENMASK(4, 0)
> +#define CONSUMER_PIPE_ID_SHFT          16
> +#define CONSUMER_PIPE_ID_MSK           GENMASK(20, 16)
> +
> +#define DML_PRODUCER_BAM_BLOCK_SIZE    0x24
> +#define DML_PRODUCER_BAM_TRANS_SIZE    0x28
> +
> +/* other definitions */
> +#define PRODUCER_PIPE_LOGICAL_SIZE     4096
> +#define CONSUMER_PIPE_LOGICAL_SIZE     4096
> +
> +#define DML_OFFSET                     0x800
> +
>  struct clk;
>  struct dma_chan;
>
> diff --git a/drivers/mmc/host/mmci_dma.c b/drivers/mmc/host/mmci_dma.c
> index 98a542d..dd7dae5 100644
> --- a/drivers/mmc/host/mmci_dma.c
> +++ b/drivers/mmc/host/mmci_dma.c
> @@ -8,11 +8,11 @@
>  #include <linux/dmaengine.h>
>  #include <linux/mmc/host.h>
>  #include <linux/mmc/card.h>
> +#include <linux/of.h>
>  #include <linux/scatterlist.h>
>
>  #include "mmci.h"
>  #include "mmci_dma.h"
> -#include "mmci_qcom_dml.h"
>
>  int mmci_dma_setup(struct mmci_host *host)
>  {
> @@ -101,6 +101,139 @@ struct dmaengine_priv {
>
>  #define dma_inprogress(dmae) ((dmae)->dma_in_progress)
>
> +#ifdef CONFIG_MMC_QCOM_DML
> +void dml_start_xfer(struct mmci_host *host, struct mmc_data *data)
> +{
> +       u32 config;
> +       void __iomem *base = host->base + DML_OFFSET;
> +
> +       if (data->flags & MMC_DATA_READ) {
> +               /* Read operation: configure DML for producer operation */
> +               /* Set producer CRCI-x and disable consumer CRCI */
> +               config = readl_relaxed(base + DML_CONFIG);
> +               config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_X_SEL;
> +               config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_DISABLE;
> +               writel_relaxed(config, base + DML_CONFIG);
> +
> +               /* Set the Producer BAM block size */
> +               writel_relaxed(data->blksz, base + DML_PRODUCER_BAM_BLOCK_SIZE);
> +
> +               /* Set Producer BAM Transaction size */
> +               writel_relaxed(data->blocks * data->blksz,
> +                              base + DML_PRODUCER_BAM_TRANS_SIZE);
> +               /* Set Producer Transaction End bit */
> +               config = readl_relaxed(base + DML_CONFIG);
> +               config |= PRODUCER_TRANS_END_EN;
> +               writel_relaxed(config, base + DML_CONFIG);
> +               /* Trigger producer */
> +               writel_relaxed(1, base + DML_PRODUCER_START);
> +       } else {
> +               /* Write operation: configure DML for consumer operation */
> +               /* Set consumer CRCI-x and disable producer CRCI*/
> +               config = readl_relaxed(base + DML_CONFIG);
> +               config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_X_SEL;
> +               config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_DISABLE;
> +               writel_relaxed(config, base + DML_CONFIG);
> +               /* Clear Producer Transaction End bit */
> +               config = readl_relaxed(base + DML_CONFIG);
> +               config &= ~PRODUCER_TRANS_END_EN;
> +               writel_relaxed(config, base + DML_CONFIG);
> +               /* Trigger consumer */
> +               writel_relaxed(1, base + DML_CONSUMER_START);
> +       }
> +
> +       /* make sure the dml is configured before dma is triggered */
> +       wmb();
> +}
> +
> +static int of_get_dml_pipe_index(struct device_node *np, const char *name)
> +{
> +       int index;
> +       struct of_phandle_args dma_spec;
> +
> +       index = of_property_match_string(np, "dma-names", name);
> +
> +       if (index < 0)
> +               return -ENODEV;
> +
> +       if (of_parse_phandle_with_args(np, "dmas", "#dma-cells", index,
> +                                      &dma_spec))
> +               return -ENODEV;
> +
> +       if (dma_spec.args_count)
> +               return dma_spec.args[0];
> +
> +       return -ENODEV;
> +}
> +
> +/* Initialize the dml hardware connected to SD Card controller */
> +int dml_hw_init(struct mmci_host *host, struct device_node *np)
> +{
> +       u32 config;
> +       void __iomem *base;
> +       int consumer_id, producer_id;
> +
> +       consumer_id = of_get_dml_pipe_index(np, "tx");
> +       producer_id = of_get_dml_pipe_index(np, "rx");
> +
> +       if (producer_id < 0 || consumer_id < 0)
> +               return -ENODEV;
> +
> +       base = host->base + DML_OFFSET;
> +
> +       /* Reset the DML block */
> +       writel_relaxed(1, base + DML_SW_RESET);
> +
> +       /* Disable the producer and consumer CRCI */
> +       config = (PRODUCER_CRCI_DISABLE | CONSUMER_CRCI_DISABLE);
> +       /*
> +        * Disable the bypass mode. Bypass mode will only be used
> +        * if data transfer is to happen in PIO mode and don't
> +        * want the BAM interface to connect with SDCC-DML.
> +        */
> +       config &= ~BYPASS;
> +       /*
> +        * Disable direct mode as we don't DML to MASTER the AHB bus.
> +        * BAM connected with DML should MASTER the AHB bus.
> +        */
> +       config &= ~DIRECT_MODE;
> +       /*
> +        * Disable infinite mode transfer as we won't be doing any
> +        * infinite size data transfers. All data transfer will be
> +        * of finite data size.
> +        */
> +       config &= ~INFINITE_CONS_TRANS;
> +       writel_relaxed(config, base + DML_CONFIG);
> +
> +       /*
> +        * Initialize the logical BAM pipe size for producer
> +        * and consumer.
> +        */
> +       writel_relaxed(PRODUCER_PIPE_LOGICAL_SIZE,
> +                      base + DML_PRODUCER_PIPE_LOGICAL_SIZE);
> +       writel_relaxed(CONSUMER_PIPE_LOGICAL_SIZE,
> +                      base + DML_CONSUMER_PIPE_LOGICAL_SIZE);
> +
> +       /* Initialize Producer/consumer pipe id */
> +       writel_relaxed(producer_id | (consumer_id << CONSUMER_PIPE_ID_SHFT),
> +                      base + DML_PIPE_ID);
> +
> +       /* Make sure dml initialization is finished */
> +       mb();
> +
> +       return 0;
> +}
> +#else
> +static inline int dml_hw_init(struct mmci_host *host, struct device_node *np)
> +{
> +       return -EINVAL;
> +}
> +
> +static inline void dml_start_xfer(struct mmci_host *host, struct mmc_data *data)
> +{
> +}
> +#endif /* CONFIG_MMC_QCOM_DML */
> +
>  static int dmaengine_setup(struct mmci_host *host)
>  {
>         const char *rxname, *txname;
> diff --git a/drivers/mmc/host/mmci_qcom_dml.c b/drivers/mmc/host/mmci_qcom_dml.c
> deleted file mode 100644
> index 00750c9..0000000
> --- a/drivers/mmc/host/mmci_qcom_dml.c
> +++ /dev/null
> @@ -1,177 +0,0 @@
> -/*
> - *
> - * Copyright (c) 2011, The Linux Foundation. All rights reserved.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License version 2 and
> - * only version 2 as published by the Free Software Foundation.
> - *
> - * This program is distributed in the hope that 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.
> - *
> - */
> -#include <linux/of.h>
> -#include <linux/of_dma.h>
> -#include <linux/bitops.h>
> -#include <linux/mmc/host.h>
> -#include <linux/mmc/card.h>
> -#include "mmci.h"
> -
> -/* Registers */
> -#define DML_CONFIG                     0x00
> -#define PRODUCER_CRCI_MSK              GENMASK(1, 0)
> -#define PRODUCER_CRCI_DISABLE          0
> -#define PRODUCER_CRCI_X_SEL            BIT(0)
> -#define PRODUCER_CRCI_Y_SEL            BIT(1)
> -#define CONSUMER_CRCI_MSK              GENMASK(3, 2)
> -#define CONSUMER_CRCI_DISABLE          0
> -#define CONSUMER_CRCI_X_SEL            BIT(2)
> -#define CONSUMER_CRCI_Y_SEL            BIT(3)
> -#define PRODUCER_TRANS_END_EN          BIT(4)
> -#define BYPASS                         BIT(16)
> -#define DIRECT_MODE                    BIT(17)
> -#define INFINITE_CONS_TRANS            BIT(18)
> -
> -#define DML_SW_RESET                   0x08
> -#define DML_PRODUCER_START             0x0c
> -#define DML_CONSUMER_START             0x10
> -#define DML_PRODUCER_PIPE_LOGICAL_SIZE 0x14
> -#define DML_CONSUMER_PIPE_LOGICAL_SIZE 0x18
> -#define DML_PIPE_ID                    0x1c
> -#define PRODUCER_PIPE_ID_SHFT          0
> -#define PRODUCER_PIPE_ID_MSK           GENMASK(4, 0)
> -#define CONSUMER_PIPE_ID_SHFT          16
> -#define CONSUMER_PIPE_ID_MSK           GENMASK(20, 16)
> -
> -#define DML_PRODUCER_BAM_BLOCK_SIZE    0x24
> -#define DML_PRODUCER_BAM_TRANS_SIZE    0x28
> -
> -/* other definitions */
> -#define PRODUCER_PIPE_LOGICAL_SIZE     4096
> -#define CONSUMER_PIPE_LOGICAL_SIZE     4096
> -
> -#define DML_OFFSET                     0x800
> -
> -void dml_start_xfer(struct mmci_host *host, struct mmc_data *data)
> -{
> -       u32 config;
> -       void __iomem *base = host->base + DML_OFFSET;
> -
> -       if (data->flags & MMC_DATA_READ) {
> -               /* Read operation: configure DML for producer operation */
> -               /* Set producer CRCI-x and disable consumer CRCI */
> -               config = readl_relaxed(base + DML_CONFIG);
> -               config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_X_SEL;
> -               config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_DISABLE;
> -               writel_relaxed(config, base + DML_CONFIG);
> -
> -               /* Set the Producer BAM block size */
> -               writel_relaxed(data->blksz, base + DML_PRODUCER_BAM_BLOCK_SIZE);
> -
> -               /* Set Producer BAM Transaction size */
> -               writel_relaxed(data->blocks * data->blksz,
> -                              base + DML_PRODUCER_BAM_TRANS_SIZE);
> -               /* Set Producer Transaction End bit */
> -               config = readl_relaxed(base + DML_CONFIG);
> -               config |= PRODUCER_TRANS_END_EN;
> -               writel_relaxed(config, base + DML_CONFIG);
> -               /* Trigger producer */
> -               writel_relaxed(1, base + DML_PRODUCER_START);
> -       } else {
> -               /* Write operation: configure DML for consumer operation */
> -               /* Set consumer CRCI-x and disable producer CRCI*/
> -               config = readl_relaxed(base + DML_CONFIG);
> -               config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_X_SEL;
> -               config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_DISABLE;
> -               writel_relaxed(config, base + DML_CONFIG);
> -               /* Clear Producer Transaction End bit */
> -               config = readl_relaxed(base + DML_CONFIG);
> -               config &= ~PRODUCER_TRANS_END_EN;
> -               writel_relaxed(config, base + DML_CONFIG);
> -               /* Trigger consumer */
> -               writel_relaxed(1, base + DML_CONSUMER_START);
> -       }
> -
> -       /* make sure the dml is configured before dma is triggered */
> -       wmb();
> -}
> -
> -static int of_get_dml_pipe_index(struct device_node *np, const char *name)
> -{
> -       int index;
> -       struct of_phandle_args  dma_spec;
> -
> -       index = of_property_match_string(np, "dma-names", name);
> -
> -       if (index < 0)
> -               return -ENODEV;
> -
> -       if (of_parse_phandle_with_args(np, "dmas", "#dma-cells", index,
> -                                      &dma_spec))
> -               return -ENODEV;
> -
> -       if (dma_spec.args_count)
> -               return dma_spec.args[0];
> -
> -       return -ENODEV;
> -}
> -
> -/* Initialize the dml hardware connected to SD Card controller */
> -int dml_hw_init(struct mmci_host *host, struct device_node *np)
> -{
> -       u32 config;
> -       void __iomem *base;
> -       int consumer_id, producer_id;
> -
> -       consumer_id = of_get_dml_pipe_index(np, "tx");
> -       producer_id = of_get_dml_pipe_index(np, "rx");
> -
> -       if (producer_id < 0 || consumer_id < 0)
> -               return -ENODEV;
> -
> -       base = host->base + DML_OFFSET;
> -
> -       /* Reset the DML block */
> -       writel_relaxed(1, base + DML_SW_RESET);
> -
> -       /* Disable the producer and consumer CRCI */
> -       config = (PRODUCER_CRCI_DISABLE | CONSUMER_CRCI_DISABLE);
> -       /*
> -        * Disable the bypass mode. Bypass mode will only be used
> -        * if data transfer is to happen in PIO mode and don't
> -        * want the BAM interface to connect with SDCC-DML.
> -        */
> -       config &= ~BYPASS;
> -       /*
> -        * Disable direct mode as we don't DML to MASTER the AHB bus.
> -        * BAM connected with DML should MASTER the AHB bus.
> -        */
> -       config &= ~DIRECT_MODE;
> -       /*
> -        * Disable infinite mode transfer as we won't be doing any
> -        * infinite size data transfers. All data transfer will be
> -        * of finite data size.
> -        */
> -       config &= ~INFINITE_CONS_TRANS;
> -       writel_relaxed(config, base + DML_CONFIG);
> -
> -       /*
> -        * Initialize the logical BAM pipe size for producer
> -        * and consumer.
> -        */
> -       writel_relaxed(PRODUCER_PIPE_LOGICAL_SIZE,
> -                      base + DML_PRODUCER_PIPE_LOGICAL_SIZE);
> -       writel_relaxed(CONSUMER_PIPE_LOGICAL_SIZE,
> -                      base + DML_CONSUMER_PIPE_LOGICAL_SIZE);
> -
> -       /* Initialize Producer/consumer pipe id */
> -       writel_relaxed(producer_id | (consumer_id << CONSUMER_PIPE_ID_SHFT),
> -                      base + DML_PIPE_ID);
> -
> -       /* Make sure dml initialization is finished */
> -       mb();
> -
> -       return 0;
> -}
> diff --git a/drivers/mmc/host/mmci_qcom_dml.h b/drivers/mmc/host/mmci_qcom_dml.h
> deleted file mode 100644
> index 6e405d0..0000000
> --- a/drivers/mmc/host/mmci_qcom_dml.h
> +++ /dev/null
> @@ -1,31 +0,0 @@
> -/*
> - *
> - * Copyright (c) 2011, The Linux Foundation. All rights reserved.
> - *
> - * This program is free software; you can redistribute it and/or modify
> - * it under the terms of the GNU General Public License version 2 and
> - * only version 2 as published by the Free Software Foundation.
> - *
> - * This program is distributed in the hope that 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.
> - *
> - */
> -#ifndef __MMC_QCOM_DML_H__
> -#define __MMC_QCOM_DML_H__
> -
> -#ifdef CONFIG_MMC_QCOM_DML
> -int dml_hw_init(struct mmci_host *host, struct device_node *np);
> -void dml_start_xfer(struct mmci_host *host, struct mmc_data *data);
> -#else
> -static inline int dml_hw_init(struct mmci_host *host, struct device_node *np)
> -{
> -       return -ENOSYS;
> -}
> -static inline void dml_start_xfer(struct mmci_host *host, struct mmc_data *data)
> -{
> -}
> -#endif /* CONFIG_MMC_QCOM_DML */
> -
> -#endif /* __MMC_QCOM_DML_H__ */
> --
> 2.7.4
>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 13/19] mmc: mmci: send stop cmd if a data command fail
  2018-07-04 13:37   ` Ulf Hansson
@ 2018-07-11  8:57     ` Ludovic BARRE
  0 siblings, 0 replies; 41+ messages in thread
From: Ludovic BARRE @ 2018-07-11  8:57 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Rob Herring, Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	Linux ARM, Linux Kernel Mailing List, devicetree, linux-mmc

hi Ulf

thanks for review, comments below

On 07/04/2018 03:37 PM, Ulf Hansson wrote:
> On 12 June 2018 at 15:14, Ludovic Barre <ludovic.Barre@st.com> wrote:
>> From: Ludovic Barre <ludovic.barre@st.com>
>>
>> The mmc framework follows the requirement of SD_Specification:
>> the STOP_TRANSMISSION is sent on multiple write/read commands
>> and the stop command (alone), not needed on other ADTC commands.
>>
>> But, some variants require a stop command "STOP_TRANSMISION" to clear
>> the DPSM "Data Path State Machine" if an error happens on command or data
>> step. If it's not done the next data command will freeze hardware block.
>> Needed to support the STM32 sdmmc variant.
>>
>> Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
>> ---
>>   drivers/mmc/host/mmci.c | 36 +++++++++++++++++++++++++++++++-----
>>   drivers/mmc/host/mmci.h |  3 +++
>>   2 files changed, 34 insertions(+), 5 deletions(-)
>>
>> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
>> index 87724e1..9af7db8 100644
>> --- a/drivers/mmc/host/mmci.c
>> +++ b/drivers/mmc/host/mmci.c
>> @@ -24,6 +24,7 @@
>>   #include <linux/mmc/pm.h>
>>   #include <linux/mmc/host.h>
>>   #include <linux/mmc/card.h>
>> +#include <linux/mmc/mmc.h>
>>   #include <linux/mmc/slot-gpio.h>
>>   #include <linux/amba/bus.h>
>>   #include <linux/clk.h>
>> @@ -522,11 +523,28 @@ static void mmci_set_mask1(struct mmci_host *host, unsigned int mask)
>>          host->mask1_reg = mask;
>>   }
>>
>> -static void mmci_stop_data(struct mmci_host *host)
>> +static int mmci_stop_data(struct mmci_host *host)
> 
> Let's leave this as void function and instead add a check in
> mmci_start_command(), to see if the command is a
> MMC_STOP_TRANSMISSION. If so, then add host->variant->cmdreg_stop to
> the register that is written in it.

In fact: the cmdreg_stop bit (which abort and reinit the DPSM)
should have been activated only in error case. But I see a mistake in 
this piece of code => cmdreg_stop bit is set for all MMC_STOP_TRANSMISSION.

So, I read closely the sdmmc datasheet and it seems possible
to set cmdreg_stop bit for all MMC_STOP_TRANSMISSION (error or not).

So, I will try your proposition to set cmdreg_stop bit in 
mmci_start_command() if the command is MMC_STOP_TRANSMISSION.

> 
> And actually, I think this change alone should be a separate patch
> coming before $subject patch in the series, as it addresses the first
> problem of two.
> 
>>   {
>> +       struct mmc_command *stop = &host->stop_abort;
>> +       struct mmc_data *data = host->data;
>> +       unsigned int cmd = 0;
>> +
>>          mmci_write_datactrlreg(host, 0);
>>          mmci_set_mask1(host, 0);
>>          host->data = NULL;
>> +
>> +       if (host->variant->cmdreg_stop) {
>> +               cmd |= host->variant->cmdreg_stop;
>> +               if (!data->stop) {
>> +                       memset(stop, 0, sizeof(struct mmc_command));
>> +                       stop->opcode = MMC_STOP_TRANSMISSION;
>> +                       stop->arg = 0;
>> +                       stop->flags = MMC_RSP_R1B | MMC_CMD_AC;
>> +                       data->stop = stop;
> 
> I don't understand why you want the host->stop_abort being present in
> the mmci host struct? Can we get rid of that?
> 
> Anyway, re-using the data->stop pointer seems reasonable.

In fact mrq->data->stop could be not referenced (NULL).

for data command, the stop command is allocated in mmc_blk_request
struct mmc_blk_request {
   struct mmc_command stop;
};
struct mmc_data {
   struct mmc_command *stop;
};
struct mmc_request {
   struct mmc_command *stop;
};

in mmc_blk_rw_rq_prep function:
if data.blocks > 1
	brq->mrq.stop = &brq->stop;
else
	brq->mrq.stop = NULL;

in mmc_mrq_prep function:
if (mrq->stop) {
	mrq->data->stop = mrq->stop;


Update about this topic:
This last week I found a new specific need for sdmmc variant.
On response type MMC_RSP_BUSY (example mmc_switch command cmd6, AC
command without data): SDMMC needs to set MMCIDATATIMER to define
busy_timeout.
If timeout expires Datatimeout and DPSM flags occur.
So a MMC_STOP_TRANSMISSION with cmdreg_stop bit is required to
abort and reinitialize the DPSM.

So, I think we could keep "host->stop_abort" which may be used in
cmd or data requests. The stop_abort command is always the same, so
stop_abort command could be initialized only while probe function (under 
variant->cmdreg_stop).

What do you think about it ?

> 
>> +               }
>> +       }
>> +
>> +       return cmd;
>>   }
>>
>>   static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data)
>> @@ -703,6 +721,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
>>                unsigned int status)
>>   {
>>          unsigned int status_err;
>> +       unsigned int cmd_reg = 0;
>>
>>          /* Make sure we have data to handle */
>>          if (!data)
>> @@ -761,7 +780,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
>>          if (status & MCI_DATAEND || data->error) {
>>                  mmci_dma_finalize(host, data);
>>
>> -               mmci_stop_data(host);
>> +               cmd_reg = mmci_stop_data(host);
>>
>>                  if (!data->error)
>>                          /* The error clause is handled above, success! */
>> @@ -770,7 +789,7 @@ mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
>>                  if (!data->stop || host->mrq->sbc) {
>>                          mmci_request_end(host, data->mrq);
>>                  } else {
>> -                       mmci_start_command(host, data->stop, 0);
>> +                       mmci_start_command(host, data->stop, cmd_reg);
>>                  }
>>          }
>>   }
>> @@ -780,6 +799,8 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
>>               unsigned int status)
>>   {
>>          void __iomem *base = host->base;
>> +       struct mmc_data *data = host->data;
>> +       unsigned int cmd_reg = 0;
>>          bool sbc;
>>
>>          if (!cmd)
>> @@ -865,11 +886,16 @@ mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
>>          }
>>
>>          if ((!sbc && !cmd->data) || cmd->error) {
>> -               if (host->data) {
>> +               if (data) {
>>                          /* Terminate the DMA transfer */
>>                          mmci_dma_error(host);
>>
>> -                       mmci_stop_data(host);
>> +                       cmd_reg = mmci_stop_data(host);
>> +
>> +                       if (data->stop) {
> 
> This means a change in behavior for !host->variant->cmdreg_stop
> variants, because the mmc core may have assigned data->stop. Did you
> test this path for any of the old variants?
I just tested on:
-stm32f7 => close to pl180 variant.
-stm32h7 and mp1 => sdmmc variant.
-I like try to snowball but I've not yet find this board.
-no arm or qcom boards

that seem ok for the both (f7/mp1).

> 
> The change as such anyway seems like a good idea. If we had data, we
> may play safe and send a stop command to let the card to try to close
> down the transfer. On the other hand, we don't want this to come as
> side-effect, but rather it deserves is own standalone patch.
> 
> I guess there are two options going forward.
> 1) Split the patch and see if this works for other variants first and
> the add your changes on top for the new ST variant.
> 2) Make this change only to affect the new ST variant.

At first time, I prefer not to have a side effect on other variant.
This part must be rework, like I note above in "Update about this 
topic", the stop transmission with cmdreg_stop bit could be send
on command without data (like cmd6: mmc_switch)

> 
> I am fine with both.
> 
>> +                               mmci_start_command(host, data->stop, cmd_reg);
>> +                               return;
>> +                       }
>>                  }
>>                  mmci_request_end(host, host->mrq);
>>          } else if (sbc) {
>> diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
>> index 2d7e901..4b4cd1d 100644
>> --- a/drivers/mmc/host/mmci.h
>> +++ b/drivers/mmc/host/mmci.h
>> @@ -243,6 +243,7 @@ struct mmci_host;
>>    * @cmdreg_lrsp_crc: enable value for long response with crc
>>    * @cmdreg_srsp_crc: enable value for short response with crc
>>    * @cmdreg_srsp: enable value for short response without crc
>> + * @cmdreg_stop: enable value for stop and abort transmission
>>    * @datalength_bits: number of bits in the MMCIDATALENGTH register
>>    * @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY
>>    *           is asserted (likewise for RX)
>> @@ -298,6 +299,7 @@ struct variant_data {
>>          unsigned int            cmdreg_lrsp_crc;
>>          unsigned int            cmdreg_srsp_crc;
>>          unsigned int            cmdreg_srsp;
>> +       unsigned int            cmdreg_stop;
>>          unsigned int            datalength_bits;
>>          unsigned int            fifosize;
>>          unsigned int            fifohalfsize;
>> @@ -343,6 +345,7 @@ struct mmci_host {
>>          struct mmc_request      *mrq;
>>          struct mmc_command      *cmd;
>>          struct mmc_data         *data;
>> +       struct mmc_command      stop_abort;
>>          struct mmc_host         *mmc;
>>          struct clk              *clk;
>>          bool                    singleirq;
>> --
>> 2.7.4
>>
> 
> Kind regards
> Uffe
> 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 01/19] mmc: mmci: regroup and define dma operations
  2018-07-05 15:17   ` Ulf Hansson
@ 2018-07-11  9:41     ` Ludovic BARRE
  2018-07-11 12:16       ` Ulf Hansson
  0 siblings, 1 reply; 41+ messages in thread
From: Ludovic BARRE @ 2018-07-11  9:41 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Rob Herring, Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	Linux ARM, Linux Kernel Mailing List, devicetree, linux-mmc



On 07/05/2018 05:17 PM, Ulf Hansson wrote:
> On 12 June 2018 at 15:14, Ludovic Barre <ludovic.Barre@st.com> wrote:
>> From: Ludovic Barre <ludovic.barre@st.com>
>>
>> Prepare mmci driver to manage dma interface by new property.
>> This patch defines and regroups dma operations for mmci drivers.
>> mmci_dma_XX prototypes are added to call member of mmci_dma_ops
>> if not null. Based on legacy need, a mmci dma interface has been
>> defined with:
>> -mmci_dma_setup
>> -mmci_dma_release
>> -mmci_dma_pre_req
>> -mmci_dma_start
>> -mmci_dma_finalize
>> -mmci_dma_post_req
>> -mmci_dma_error
>> -mmci_dma_get_next_data
> 
> As I suggested for one of the other patches, I would rather turn core
> mmci functions into library functions, which can be either invoked
> from variant callbacks or assigned directly to them.
> 
> In other words, I would leave the functions that you move in this
> patch to stay in mmci.c. Although some needs to be re-factored and we
> also needs to make some of them available to be called from another
> file, hence the functions needs to be shared via mmci.h rather than
> being declared static.

In previous exchange mail "STM32MP1 SDMMC driver review"
we are said:

 >>> -dma variant à should fit in Qualcomm implementation, reuse (rename)
 >>> mmci_qcom_dml.c file and integrate ST dma in.
 >>
 >> stm32 sdmmc has an internal dma, no need to use dmaengine API;
 >> So some modifications in mmci (pre/post request, mmci_dma_xx). perhaps
 >> should be done with an ops or not.
 >
 >Yes.
 >
 >The Qualcomm variant is also using an internal DMA, hence I thought
 >there may be something we could re-use, or at least have some new
 >common ops for.

It's not crystal clear for me.
Do you always agree with a dma ops which allow to address different
DMA transfer:
-with dmaengine API
-sdmmc idma, without dmaengine API
-...

> 
> Let me take a concrete example on how I would move forward, hopefully
> that explains it a bit better. Please see below.
> 
> [...]
> 
>> -/*
>> - * All the DMA operation mode stuff goes inside this ifdef.
>> - * This assumes that you have a generic DMA device interface,
>> - * no custom DMA interfaces are supported.
>> - */
>> -#ifdef CONFIG_DMA_ENGINE
>> -static void mmci_dma_setup(struct mmci_host *host)
>> -{
>> -       const char *rxname, *txname;
>> -       struct variant_data *variant = host->variant;
>> -
>> -       host->dma_rx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "rx");
>> -       host->dma_tx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "tx");
>> -
>> -       /* initialize pre request cookie */
>> -       host->next_data.cookie = 1;
>> -
>> -       /*
>> -        * If only an RX channel is specified, the driver will
>> -        * attempt to use it bidirectionally, however if it is
>> -        * is specified but cannot be located, DMA will be disabled.
>> -        */
>> -       if (host->dma_rx_channel && !host->dma_tx_channel)
>> -               host->dma_tx_channel = host->dma_rx_channel;
>> -
>> -       if (host->dma_rx_channel)
>> -               rxname = dma_chan_name(host->dma_rx_channel);
>> -       else
>> -               rxname = "none";
>> -
>> -       if (host->dma_tx_channel)
>> -               txname = dma_chan_name(host->dma_tx_channel);
>> -       else
>> -               txname = "none";
>> -
>> -       dev_info(mmc_dev(host->mmc), "DMA channels RX %s, TX %s\n",
>> -                rxname, txname);
>> -
>> -       /*
>> -        * Limit the maximum segment size in any SG entry according to
>> -        * the parameters of the DMA engine device.
>> -        */
>> -       if (host->dma_tx_channel) {
>> -               struct device *dev = host->dma_tx_channel->device->dev;
>> -               unsigned int max_seg_size = dma_get_max_seg_size(dev);
>> -
>> -               if (max_seg_size < host->mmc->max_seg_size)
>> -                       host->mmc->max_seg_size = max_seg_size;
>> -       }
>> -       if (host->dma_rx_channel) {
>> -               struct device *dev = host->dma_rx_channel->device->dev;
>> -               unsigned int max_seg_size = dma_get_max_seg_size(dev);
>> -
>> -               if (max_seg_size < host->mmc->max_seg_size)
>> -                       host->mmc->max_seg_size = max_seg_size;
>> -       }
> 
> Everything above shall be left as generic library function,
> mmci_dma_setup() and I would share it via mmci.h and thus change it
> from being static.
> 

each interfaces mmci_dma_XXX have very different needs depending
dma_ops (legacy, sdmmc idma)

>> -
>> -       if (variant->qcom_dml && host->dma_rx_channel && host->dma_tx_channel)
>> -               if (dml_hw_init(host, host->mmc->parent->of_node))
>> -                       variant->qcom_dml = false;
> 
> This piece of code, should be made specific to the qcom variant and
> managed though a "mmci_host_ops" callback. The corresponding code in
> that callback would then start by invoking mmci_dma_setup(), before it
> continues with the qcom specific operations.
> 
> For legacy variants, the corresponding callback would be set directly
> to mmci_dma_setup() and called through the callback from mmci.c when
> needed. There is no need to have a separate file for DMA for the
> legacy variants, I think.
> 
> [...]
> 
> Kind regards
> Uffe
> 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 01/19] mmc: mmci: regroup and define dma operations
  2018-07-11  9:41     ` Ludovic BARRE
@ 2018-07-11 12:16       ` Ulf Hansson
  2018-07-12  9:09         ` Ludovic BARRE
  0 siblings, 1 reply; 41+ messages in thread
From: Ulf Hansson @ 2018-07-11 12:16 UTC (permalink / raw)
  To: Ludovic BARRE
  Cc: Rob Herring, Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	Linux ARM, Linux Kernel Mailing List, devicetree, linux-mmc

On 11 July 2018 at 11:41, Ludovic BARRE <ludovic.barre@st.com> wrote:
>
>
> On 07/05/2018 05:17 PM, Ulf Hansson wrote:
>>
>> On 12 June 2018 at 15:14, Ludovic Barre <ludovic.Barre@st.com> wrote:
>>>
>>> From: Ludovic Barre <ludovic.barre@st.com>
>>>
>>> Prepare mmci driver to manage dma interface by new property.
>>> This patch defines and regroups dma operations for mmci drivers.
>>> mmci_dma_XX prototypes are added to call member of mmci_dma_ops
>>> if not null. Based on legacy need, a mmci dma interface has been
>>> defined with:
>>> -mmci_dma_setup
>>> -mmci_dma_release
>>> -mmci_dma_pre_req
>>> -mmci_dma_start
>>> -mmci_dma_finalize
>>> -mmci_dma_post_req
>>> -mmci_dma_error
>>> -mmci_dma_get_next_data
>>
>>
>> As I suggested for one of the other patches, I would rather turn core
>> mmci functions into library functions, which can be either invoked
>> from variant callbacks or assigned directly to them.
>>
>> In other words, I would leave the functions that you move in this
>> patch to stay in mmci.c. Although some needs to be re-factored and we
>> also needs to make some of them available to be called from another
>> file, hence the functions needs to be shared via mmci.h rather than
>> being declared static.
>
>
> In previous exchange mail "STM32MP1 SDMMC driver review"
> we are said:
>
>>>> -dma variant à should fit in Qualcomm implementation, reuse (rename)
>>>> mmci_qcom_dml.c file and integrate ST dma in.

Apologize if I may have lead you in a wrong direction, that was not my intent.

However, by looking at $subject patch, your seems to be unnecessarily
shuffling code around. I would like to avoid that.

>>>
>>> stm32 sdmmc has an internal dma, no need to use dmaengine API;
>>> So some modifications in mmci (pre/post request, mmci_dma_xx). perhaps
>>> should be done with an ops or not.
>>
>>Yes.
>>
>>The Qualcomm variant is also using an internal DMA, hence I thought
>>there may be something we could re-use, or at least have some new
>>common ops for.
>
> It's not crystal clear for me.
> Do you always agree with a dma ops which allow to address different
> DMA transfer:
> -with dmaengine API
> -sdmmc idma, without dmaengine API
> -...

If we can use a mmci ops callback to manage the variant differences,
that would be perfect. That combined with making the existing DMA
functions in mmci.c converted to "library" functions, which the mmci
ops callbacks can call, in order to re-use code.

When that isn't really suitable, we may need to add a "quirk" instead,
which would be specific for that particular variant. Along the lines
of what we already do for variant specifics inside mmci.c.

I think we have to decide on case by case basis, what fits best.

Hope this makes a better explanation. If not, please tell, and I can
take an initial stab and post a patch to show you with code how I mean
to move forward.

>
>
>>
>> Let me take a concrete example on how I would move forward, hopefully
>> that explains it a bit better. Please see below.
>>
>> [...]
>>
>>> -/*
>>> - * All the DMA operation mode stuff goes inside this ifdef.
>>> - * This assumes that you have a generic DMA device interface,
>>> - * no custom DMA interfaces are supported.
>>> - */
>>> -#ifdef CONFIG_DMA_ENGINE
>>> -static void mmci_dma_setup(struct mmci_host *host)
>>> -{
>>> -       const char *rxname, *txname;
>>> -       struct variant_data *variant = host->variant;
>>> -
>>> -       host->dma_rx_channel =
>>> dma_request_slave_channel(mmc_dev(host->mmc), "rx");
>>> -       host->dma_tx_channel =
>>> dma_request_slave_channel(mmc_dev(host->mmc), "tx");
>>> -
>>> -       /* initialize pre request cookie */
>>> -       host->next_data.cookie = 1;
>>> -
>>> -       /*
>>> -        * If only an RX channel is specified, the driver will
>>> -        * attempt to use it bidirectionally, however if it is
>>> -        * is specified but cannot be located, DMA will be disabled.
>>> -        */
>>> -       if (host->dma_rx_channel && !host->dma_tx_channel)
>>> -               host->dma_tx_channel = host->dma_rx_channel;
>>> -
>>> -       if (host->dma_rx_channel)
>>> -               rxname = dma_chan_name(host->dma_rx_channel);
>>> -       else
>>> -               rxname = "none";
>>> -
>>> -       if (host->dma_tx_channel)
>>> -               txname = dma_chan_name(host->dma_tx_channel);
>>> -       else
>>> -               txname = "none";
>>> -
>>> -       dev_info(mmc_dev(host->mmc), "DMA channels RX %s, TX %s\n",
>>> -                rxname, txname);
>>> -
>>> -       /*
>>> -        * Limit the maximum segment size in any SG entry according to
>>> -        * the parameters of the DMA engine device.
>>> -        */
>>> -       if (host->dma_tx_channel) {
>>> -               struct device *dev = host->dma_tx_channel->device->dev;
>>> -               unsigned int max_seg_size = dma_get_max_seg_size(dev);
>>> -
>>> -               if (max_seg_size < host->mmc->max_seg_size)
>>> -                       host->mmc->max_seg_size = max_seg_size;
>>> -       }
>>> -       if (host->dma_rx_channel) {
>>> -               struct device *dev = host->dma_rx_channel->device->dev;
>>> -               unsigned int max_seg_size = dma_get_max_seg_size(dev);
>>> -
>>> -               if (max_seg_size < host->mmc->max_seg_size)
>>> -                       host->mmc->max_seg_size = max_seg_size;
>>> -       }
>>
>>
>> Everything above shall be left as generic library function,
>> mmci_dma_setup() and I would share it via mmci.h and thus change it
>> from being static.
>>
>
> each interfaces mmci_dma_XXX have very different needs depending
> dma_ops (legacy, sdmmc idma)

Right. This was just one example.

If I understand, what you are suggesting is to make all of them being
variant specific callbacks, so I assume that would solve the problems.
Just to be clear, I have no problem with that.

Although, that doesn't mean we can't re-use existing dma functions in
mmci.c, when that makes sense.

[...]

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 05/19] mmc: mmci: allow to overwrite clock/power procedure to specific variant
  2018-07-05 13:48   ` Ulf Hansson
@ 2018-07-11 12:19     ` Ludovic BARRE
  2018-07-11 12:38       ` Ulf Hansson
  0 siblings, 1 reply; 41+ messages in thread
From: Ludovic BARRE @ 2018-07-11 12:19 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Rob Herring, Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	Linux ARM, Linux Kernel Mailing List, devicetree, linux-mmc



On 07/05/2018 03:48 PM, Ulf Hansson wrote:
> On 12 June 2018 at 15:14, Ludovic Barre <ludovic.Barre@st.com> wrote:
>> From: Ludovic Barre <ludovic.barre@st.com>
>>
>> A specific variant could have different power or clock procedures.
>> This patch allows to overwrite the default mmci_set_clkreg and
>> mmci_set_pwrreg for a specific variant.
>>
>> Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
>> ---
>>   drivers/mmc/host/mmci.c | 96 +++++++++++++++++++++++++++++--------------------
>>   drivers/mmc/host/mmci.h |  7 ++++
>>   2 files changed, 64 insertions(+), 39 deletions(-)
>>
>> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
>> index ede95b7..801c86b 100644
>> --- a/drivers/mmc/host/mmci.c
>> +++ b/drivers/mmc/host/mmci.c
>> @@ -374,6 +374,52 @@ static void mmci_set_clkreg(struct mmci_host *host, unsigned int desired)
>>          mmci_write_clkreg(host, clk);
>>   }
>>
>> +static void mmci_set_pwrreg(struct mmci_host *host, unsigned char power_mode,
>> +                           unsigned int pwr)
>> +{
>> +       struct variant_data *variant = host->variant;
>> +       struct mmc_host *mmc = host->mmc;
>> +
>> +       switch (power_mode) {
>> +       case MMC_POWER_OFF:
>> +               if (!IS_ERR(mmc->supply.vmmc))
>> +                       mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
>> +
>> +               if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) {
>> +                       regulator_disable(mmc->supply.vqmmc);
>> +                       host->vqmmc_enabled = false;
>> +               }
>> +
>> +               break;
>> +       case MMC_POWER_UP:
>> +               if (!IS_ERR(mmc->supply.vmmc))
>> +                       mmc_regulator_set_ocr(mmc, mmc->supply.vmmc,
>> +                                             mmc->ios.vdd);
>> +
>> +               /*
>> +                * The ST Micro variant doesn't have the PL180s MCI_PWR_UP
>> +                * and instead uses MCI_PWR_ON so apply whatever value is
>> +                * configured in the variant data.
>> +                */
>> +               pwr |= variant->pwrreg_powerup;
>> +
>> +               break;
>> +       case MMC_POWER_ON:
>> +               if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) {
>> +                       if (regulator_enable(mmc->supply.vqmmc) < 0)
>> +                               dev_err(mmc_dev(mmc),
>> +                                       "failed to enable vqmmc regulator\n");
>> +                       else
>> +                               host->vqmmc_enabled = true;
>> +               }
>> +
>> +               pwr |= MCI_PWR_ON;
>> +               break;
>> +       }
>> +
>> +       mmci_write_pwrreg(host, pwr);
>> +}
>> +
>>   static void
>>   mmci_request_end(struct mmci_host *host, struct mmc_request *mrq)
>>   {
>> @@ -1031,7 +1077,7 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
>>   {
>>          struct mmci_host *host = mmc_priv(mmc);
>>          struct variant_data *variant = host->variant;
>> -       u32 pwr = 0;
>> +       unsigned int pwr = 0;
> 
> ?

yes not needed
rewritten due to re-factoring

> 
>>          unsigned long flags;
>>          int ret;
>>
>> @@ -1039,42 +1085,6 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
>>                  host->plat->ios_handler(mmc_dev(mmc), ios))
>>                          dev_err(mmc_dev(mmc), "platform ios_handler failed\n");
>>
>> -       switch (ios->power_mode) {
>> -       case MMC_POWER_OFF:
>> -               if (!IS_ERR(mmc->supply.vmmc))
>> -                       mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, 0);
>> -
>> -               if (!IS_ERR(mmc->supply.vqmmc) && host->vqmmc_enabled) {
>> -                       regulator_disable(mmc->supply.vqmmc);
>> -                       host->vqmmc_enabled = false;
>> -               }
>> -
>> -               break;
>> -       case MMC_POWER_UP:
>> -               if (!IS_ERR(mmc->supply.vmmc))
>> -                       mmc_regulator_set_ocr(mmc, mmc->supply.vmmc, ios->vdd);
>> -
>> -               /*
>> -                * The ST Micro variant doesn't have the PL180s MCI_PWR_UP
>> -                * and instead uses MCI_PWR_ON so apply whatever value is
>> -                * configured in the variant data.
>> -                */
>> -               pwr |= variant->pwrreg_powerup;
>> -
>> -               break;
>> -       case MMC_POWER_ON:
>> -               if (!IS_ERR(mmc->supply.vqmmc) && !host->vqmmc_enabled) {
>> -                       ret = regulator_enable(mmc->supply.vqmmc);
>> -                       if (ret < 0)
>> -                               dev_err(mmc_dev(mmc),
>> -                                       "failed to enable vqmmc regulator\n");
>> -                       else
>> -                               host->vqmmc_enabled = true;
>> -               }
>> -
>> -               pwr |= MCI_PWR_ON;
>> -               break;
>> -       }
> 
> This above looks like pure re-factoring. Please make the above change
> a separate patch.

OK

> 
>>
>>          if (variant->signal_direction && ios->power_mode != MMC_POWER_OFF) {
>>                  /*
>> @@ -1126,8 +1136,16 @@ static void mmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
>>
>>          spin_lock_irqsave(&host->lock, flags);
>>
>> -       mmci_set_clkreg(host, ios->clock);
>> -       mmci_write_pwrreg(host, pwr);
>> +       if (variant->set_clkreg)
>> +               variant->set_clkreg(host, ios->clock);
>> +       else
>> +               mmci_set_clkreg(host, ios->clock);
> 
> This means a change in behavior, which I don't think is what you want.
> 
> 1) The spin lock will be held while doing the regulator operations.

Yes, you are right, the behavior around spin lock has been modified for
legacy. I will try to find a solution to avoid behavior change for
legacy.

For power, sdmmc variant have specific need to reset the sdmmc variant
and set power register before to disable the vqmmc regulator.
that's why I move the regulator operation in mmci_set_pwrreg.

> 2) The clock register will be written to before the regulator
> operations have been done. Not sure if that works fine!?

you are right, it's probably better if the clock is done after power.
do you agree if I change the order?

> 
> An overall comment, I think we should create a mmci_host_ops structure
> and put the needed callbacks in there (kept to a minimum of course),
> rather than putting them as a part of the variant struct. More
> importantly, also the legacy mmci variants should get a mmci_host_ops
> structure assigned during probe.
> 
> The point is, I think it makes the code above (and future wise) more
> flexible. It should also allow us to better share functions between
> the variants. In principle, I expect that we end up with a bunch of
> "library" mmci functions that can be invoked from variant's
> mmci_host_ops (if not assigned directly).

After "Linaro connect" we have exchanged some emails (subject: "STM32MP1 
SDMMC driver review") and the start point was
"Goal is not to redesign mmci.c like sdhci.c."
This it's why I'm surprise by "library" and "mmci_host_ops"
and comment of patch 01.

So, it's not clear for me on where we wish to go.
Could you clarify the way, if it's with a mmci_ops like sdhci_ops?

> 
>> +
>> +       if (variant->set_pwrreg)
>> +               variant->set_pwrreg(host, ios->power_mode, pwr);
>> +       else
>> +               mmci_set_pwrreg(host, ios->power_mode, pwr);
>> +
>>          mmci_reg_delay(host);
>>
>>          spin_unlock_irqrestore(&host->lock, flags);
>> diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
>> index 2ba9640..7265ca6 100644
>> --- a/drivers/mmc/host/mmci.h
>> +++ b/drivers/mmc/host/mmci.h
>> @@ -231,6 +231,7 @@
>>
>>   struct clk;
>>   struct dma_chan;
>> +struct mmci_host;
>>
>>   /**
>>    * struct variant_data - MMCI variant-specific quirks
>> @@ -273,6 +274,8 @@ struct dma_chan;
>>    *            register.
>>    * @opendrain: bitmask identifying the OPENDRAIN bit inside MMCIPOWER register
>>    * @mmci_dma: Pointer to platform-specific DMA callbacks.
>> + * @set_clk_ios: if clock procedure of variant is specific
>> + * @set_pwr_ios: if power procedure of variant is specific
>>    */
>>   struct variant_data {
>>          unsigned int            clkreg;
>> @@ -307,6 +310,9 @@ struct variant_data {
>>          u32                     start_err;
>>          u32                     opendrain;
>>          struct mmci_dma_ops     *mmci_dma;
>> +       void (*set_clkreg)(struct mmci_host *host, unsigned int desired);
>> +       void (*set_pwrreg)(struct mmci_host *host, unsigned char power_mode,
>> +                          unsigned int pwr);
>>   };
>>
>>   struct mmci_host {
>> @@ -328,6 +334,7 @@ struct mmci_host {
>>          u32                     pwr_reg;
>>          u32                     pwr_reg_add;
>>          u32                     clk_reg;
>> +       u32                     clk_reg_add;
> 
> What's this? Some leftover I guess?
> 
>>          u32                     datactrl_reg;
>>          u32                     busy_status;
>>          u32                     mask1_reg;
>> --
>> 2.7.4
>>
> 
> Kind regards
> Uffe
> 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 05/19] mmc: mmci: allow to overwrite clock/power procedure to specific variant
  2018-07-11 12:19     ` Ludovic BARRE
@ 2018-07-11 12:38       ` Ulf Hansson
  0 siblings, 0 replies; 41+ messages in thread
From: Ulf Hansson @ 2018-07-11 12:38 UTC (permalink / raw)
  To: Ludovic BARRE
  Cc: Rob Herring, Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	Linux ARM, Linux Kernel Mailing List, devicetree, linux-mmc

[...]

>> 2) The clock register will be written to before the regulator
>> operations have been done. Not sure if that works fine!?
>
>
> you are right, it's probably better if the clock is done after power.
> do you agree if I change the order?

If the new ST variant can cope with the existing order in the current
code, then let's stick to that to avoid breaking legacy variants.

>
>>
>> An overall comment, I think we should create a mmci_host_ops structure
>> and put the needed callbacks in there (kept to a minimum of course),
>> rather than putting them as a part of the variant struct. More
>> importantly, also the legacy mmci variants should get a mmci_host_ops
>> structure assigned during probe.
>>
>> The point is, I think it makes the code above (and future wise) more
>> flexible. It should also allow us to better share functions between
>> the variants. In principle, I expect that we end up with a bunch of
>> "library" mmci functions that can be invoked from variant's
>> mmci_host_ops (if not assigned directly).
>
>
> After "Linaro connect" we have exchanged some emails (subject: "STM32MP1
> SDMMC driver review") and the start point was
> "Goal is not to redesign mmci.c like sdhci.c."
> This it's why I'm surprise by "library" and "mmci_host_ops"
> and comment of patch 01.

Sometimes it's easier to look at patches/code, instead of giving
qualified guesses what is the best solution. Try something, realize
what is better, then try again...

The point is, we need callbacks to cope with the variants as we can't
have quirks for everything, and you are demonstrating that in $subject
patch.

Since this is the case, then I think it's better to make it more
flexible, already from the beginning and thus put the callbacks in a
new mmci host ops structure instead.

>
> So, it's not clear for me on where we wish to go.
> Could you clarify the way, if it's with a mmci_ops like sdhci_ops?

Yep, something along those lines. However, we don't want to introduce
unnecessary callbacks and complexity. We should strive towards sharing
common functionality through library functions in mmci.c, rather than
having callbacks for every single difference.

Does it makes sense?

[...]

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 02/19] mmc: mmci: merge qcom dml feature into mmci dma
  2018-07-05 15:26   ` Ulf Hansson
@ 2018-07-11 15:19     ` Ludovic BARRE
  2018-07-13 11:17       ` Ulf Hansson
  0 siblings, 1 reply; 41+ messages in thread
From: Ludovic BARRE @ 2018-07-11 15:19 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Rob Herring, Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	Linux ARM, Linux Kernel Mailing List, devicetree, linux-mmc



On 07/05/2018 05:26 PM, Ulf Hansson wrote:
> On 12 June 2018 at 15:14, Ludovic Barre <ludovic.Barre@st.com> wrote:
>> From: Ludovic Barre <ludovic.barre@st.com>
>>
>> This patch integrates qcom dml feature into mmci_dma file.
>> Qualcomm Data Mover lite/local is already a variant of mmci dmaengine.
>>
>> Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
>> ---
>>   drivers/mmc/host/Makefile        |   1 -
>>   drivers/mmc/host/mmci.c          |   1 -
>>   drivers/mmc/host/mmci.h          |  35 ++++++++
>>   drivers/mmc/host/mmci_dma.c      | 135 ++++++++++++++++++++++++++++-
>>   drivers/mmc/host/mmci_qcom_dml.c | 177 ---------------------------------------
>>   drivers/mmc/host/mmci_qcom_dml.h |  31 -------
>>   6 files changed, 169 insertions(+), 211 deletions(-)
>>   delete mode 100644 drivers/mmc/host/mmci_qcom_dml.c
>>   delete mode 100644 drivers/mmc/host/mmci_qcom_dml.h
> 
> No, this is not the way to go. Instead I I think there are two options.
> 
> 1) Keep mmci_qcom_dml.c|h and thus add new files for the stm32 dma variant.
> 
> 2) Start by renaming mmci_qcom_dml.* to mmc_dma.* and then in the next
> step add the code for stm32 dma into the renamed files.
> 
> I guess if there is some overlap in functionality, 2) may be best as
> it could easier avoid open coding. However, I am fine with whatever
> option and I expect that you knows what is best.

After patch 01 & 05 comments:
I will try to define a mmci_ops which contain some functions pointer
called by mmci.c (core).
A variant defines its mmci_ops.
where do you define the specific function:
-in a single file, mmci-ops.c or other (for the name, I'm not inspirated)
-or in specific file for each variant mmci-qcom.c or mmci-stm32.c

following the comment (above), I think we define a single file?

> 
> Kind regards
> Uffe
> 
>>
>> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
>> index daecaa98..608a020 100644
>> --- a/drivers/mmc/host/Makefile
>> +++ b/drivers/mmc/host/Makefile
>> @@ -5,7 +5,6 @@
>>
>>   obj-$(CONFIG_MMC_ARMMMCI) += armmmci.o
>>   armmmci-y := mmci.o mmci_dma.o
>> -armmmci-$(CONFIG_MMC_QCOM_DML) += mmci_qcom_dml.o
>>   obj-$(CONFIG_MMC_PXA)          += pxamci.o
>>   obj-$(CONFIG_MMC_MXC)          += mxcmmc.o
>>   obj-$(CONFIG_MMC_MXS)          += mxs-mmc.o
>> diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
>> index 8868be0..7a15afd 100644
>> --- a/drivers/mmc/host/mmci.c
>> +++ b/drivers/mmc/host/mmci.c
>> @@ -43,7 +43,6 @@
>>
>>   #include "mmci.h"
>>   #include "mmci_dma.h"
>> -#include "mmci_qcom_dml.h"
>>
>>   #define DRIVER_NAME "mmci-pl18x"
>>
>> diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
>> index a73bb98..f7cba35 100644
>> --- a/drivers/mmc/host/mmci.h
>> +++ b/drivers/mmc/host/mmci.h
>> @@ -194,6 +194,41 @@
>>
>>   #define MMCI_PINCTRL_STATE_OPENDRAIN "opendrain"
>>
>> +/* QCOM DML Registers */
>> +#define DML_CONFIG                     0x00
>> +#define PRODUCER_CRCI_MSK              GENMASK(1, 0)
>> +#define PRODUCER_CRCI_DISABLE          0
>> +#define PRODUCER_CRCI_X_SEL            BIT(0)
>> +#define PRODUCER_CRCI_Y_SEL            BIT(1)
>> +#define CONSUMER_CRCI_MSK              GENMASK(3, 2)
>> +#define CONSUMER_CRCI_DISABLE          0
>> +#define CONSUMER_CRCI_X_SEL            BIT(2)
>> +#define CONSUMER_CRCI_Y_SEL            BIT(3)
>> +#define PRODUCER_TRANS_END_EN          BIT(4)
>> +#define BYPASS                         BIT(16)
>> +#define DIRECT_MODE                    BIT(17)
>> +#define INFINITE_CONS_TRANS            BIT(18)
>> +
>> +#define DML_SW_RESET                   0x08
>> +#define DML_PRODUCER_START             0x0c
>> +#define DML_CONSUMER_START             0x10
>> +#define DML_PRODUCER_PIPE_LOGICAL_SIZE 0x14
>> +#define DML_CONSUMER_PIPE_LOGICAL_SIZE 0x18
>> +#define DML_PIPE_ID                    0x1c
>> +#define PRODUCER_PIPE_ID_SHFT          0
>> +#define PRODUCER_PIPE_ID_MSK           GENMASK(4, 0)
>> +#define CONSUMER_PIPE_ID_SHFT          16
>> +#define CONSUMER_PIPE_ID_MSK           GENMASK(20, 16)
>> +
>> +#define DML_PRODUCER_BAM_BLOCK_SIZE    0x24
>> +#define DML_PRODUCER_BAM_TRANS_SIZE    0x28
>> +
>> +/* other definitions */
>> +#define PRODUCER_PIPE_LOGICAL_SIZE     4096
>> +#define CONSUMER_PIPE_LOGICAL_SIZE     4096
>> +
>> +#define DML_OFFSET                     0x800
>> +
>>   struct clk;
>>   struct dma_chan;
>>
>> diff --git a/drivers/mmc/host/mmci_dma.c b/drivers/mmc/host/mmci_dma.c
>> index 98a542d..dd7dae5 100644
>> --- a/drivers/mmc/host/mmci_dma.c
>> +++ b/drivers/mmc/host/mmci_dma.c
>> @@ -8,11 +8,11 @@
>>   #include <linux/dmaengine.h>
>>   #include <linux/mmc/host.h>
>>   #include <linux/mmc/card.h>
>> +#include <linux/of.h>
>>   #include <linux/scatterlist.h>
>>
>>   #include "mmci.h"
>>   #include "mmci_dma.h"
>> -#include "mmci_qcom_dml.h"
>>
>>   int mmci_dma_setup(struct mmci_host *host)
>>   {
>> @@ -101,6 +101,139 @@ struct dmaengine_priv {
>>
>>   #define dma_inprogress(dmae) ((dmae)->dma_in_progress)
>>
>> +#ifdef CONFIG_MMC_QCOM_DML
>> +void dml_start_xfer(struct mmci_host *host, struct mmc_data *data)
>> +{
>> +       u32 config;
>> +       void __iomem *base = host->base + DML_OFFSET;
>> +
>> +       if (data->flags & MMC_DATA_READ) {
>> +               /* Read operation: configure DML for producer operation */
>> +               /* Set producer CRCI-x and disable consumer CRCI */
>> +               config = readl_relaxed(base + DML_CONFIG);
>> +               config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_X_SEL;
>> +               config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_DISABLE;
>> +               writel_relaxed(config, base + DML_CONFIG);
>> +
>> +               /* Set the Producer BAM block size */
>> +               writel_relaxed(data->blksz, base + DML_PRODUCER_BAM_BLOCK_SIZE);
>> +
>> +               /* Set Producer BAM Transaction size */
>> +               writel_relaxed(data->blocks * data->blksz,
>> +                              base + DML_PRODUCER_BAM_TRANS_SIZE);
>> +               /* Set Producer Transaction End bit */
>> +               config = readl_relaxed(base + DML_CONFIG);
>> +               config |= PRODUCER_TRANS_END_EN;
>> +               writel_relaxed(config, base + DML_CONFIG);
>> +               /* Trigger producer */
>> +               writel_relaxed(1, base + DML_PRODUCER_START);
>> +       } else {
>> +               /* Write operation: configure DML for consumer operation */
>> +               /* Set consumer CRCI-x and disable producer CRCI*/
>> +               config = readl_relaxed(base + DML_CONFIG);
>> +               config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_X_SEL;
>> +               config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_DISABLE;
>> +               writel_relaxed(config, base + DML_CONFIG);
>> +               /* Clear Producer Transaction End bit */
>> +               config = readl_relaxed(base + DML_CONFIG);
>> +               config &= ~PRODUCER_TRANS_END_EN;
>> +               writel_relaxed(config, base + DML_CONFIG);
>> +               /* Trigger consumer */
>> +               writel_relaxed(1, base + DML_CONSUMER_START);
>> +       }
>> +
>> +       /* make sure the dml is configured before dma is triggered */
>> +       wmb();
>> +}
>> +
>> +static int of_get_dml_pipe_index(struct device_node *np, const char *name)
>> +{
>> +       int index;
>> +       struct of_phandle_args dma_spec;
>> +
>> +       index = of_property_match_string(np, "dma-names", name);
>> +
>> +       if (index < 0)
>> +               return -ENODEV;
>> +
>> +       if (of_parse_phandle_with_args(np, "dmas", "#dma-cells", index,
>> +                                      &dma_spec))
>> +               return -ENODEV;
>> +
>> +       if (dma_spec.args_count)
>> +               return dma_spec.args[0];
>> +
>> +       return -ENODEV;
>> +}
>> +
>> +/* Initialize the dml hardware connected to SD Card controller */
>> +int dml_hw_init(struct mmci_host *host, struct device_node *np)
>> +{
>> +       u32 config;
>> +       void __iomem *base;
>> +       int consumer_id, producer_id;
>> +
>> +       consumer_id = of_get_dml_pipe_index(np, "tx");
>> +       producer_id = of_get_dml_pipe_index(np, "rx");
>> +
>> +       if (producer_id < 0 || consumer_id < 0)
>> +               return -ENODEV;
>> +
>> +       base = host->base + DML_OFFSET;
>> +
>> +       /* Reset the DML block */
>> +       writel_relaxed(1, base + DML_SW_RESET);
>> +
>> +       /* Disable the producer and consumer CRCI */
>> +       config = (PRODUCER_CRCI_DISABLE | CONSUMER_CRCI_DISABLE);
>> +       /*
>> +        * Disable the bypass mode. Bypass mode will only be used
>> +        * if data transfer is to happen in PIO mode and don't
>> +        * want the BAM interface to connect with SDCC-DML.
>> +        */
>> +       config &= ~BYPASS;
>> +       /*
>> +        * Disable direct mode as we don't DML to MASTER the AHB bus.
>> +        * BAM connected with DML should MASTER the AHB bus.
>> +        */
>> +       config &= ~DIRECT_MODE;
>> +       /*
>> +        * Disable infinite mode transfer as we won't be doing any
>> +        * infinite size data transfers. All data transfer will be
>> +        * of finite data size.
>> +        */
>> +       config &= ~INFINITE_CONS_TRANS;
>> +       writel_relaxed(config, base + DML_CONFIG);
>> +
>> +       /*
>> +        * Initialize the logical BAM pipe size for producer
>> +        * and consumer.
>> +        */
>> +       writel_relaxed(PRODUCER_PIPE_LOGICAL_SIZE,
>> +                      base + DML_PRODUCER_PIPE_LOGICAL_SIZE);
>> +       writel_relaxed(CONSUMER_PIPE_LOGICAL_SIZE,
>> +                      base + DML_CONSUMER_PIPE_LOGICAL_SIZE);
>> +
>> +       /* Initialize Producer/consumer pipe id */
>> +       writel_relaxed(producer_id | (consumer_id << CONSUMER_PIPE_ID_SHFT),
>> +                      base + DML_PIPE_ID);
>> +
>> +       /* Make sure dml initialization is finished */
>> +       mb();
>> +
>> +       return 0;
>> +}
>> +#else
>> +static inline int dml_hw_init(struct mmci_host *host, struct device_node *np)
>> +{
>> +       return -EINVAL;
>> +}
>> +
>> +static inline void dml_start_xfer(struct mmci_host *host, struct mmc_data *data)
>> +{
>> +}
>> +#endif /* CONFIG_MMC_QCOM_DML */
>> +
>>   static int dmaengine_setup(struct mmci_host *host)
>>   {
>>          const char *rxname, *txname;
>> diff --git a/drivers/mmc/host/mmci_qcom_dml.c b/drivers/mmc/host/mmci_qcom_dml.c
>> deleted file mode 100644
>> index 00750c9..0000000
>> --- a/drivers/mmc/host/mmci_qcom_dml.c
>> +++ /dev/null
>> @@ -1,177 +0,0 @@
>> -/*
>> - *
>> - * Copyright (c) 2011, The Linux Foundation. All rights reserved.
>> - *
>> - * This program is free software; you can redistribute it and/or modify
>> - * it under the terms of the GNU General Public License version 2 and
>> - * only version 2 as published by the Free Software Foundation.
>> - *
>> - * This program is distributed in the hope that 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.
>> - *
>> - */
>> -#include <linux/of.h>
>> -#include <linux/of_dma.h>
>> -#include <linux/bitops.h>
>> -#include <linux/mmc/host.h>
>> -#include <linux/mmc/card.h>
>> -#include "mmci.h"
>> -
>> -/* Registers */
>> -#define DML_CONFIG                     0x00
>> -#define PRODUCER_CRCI_MSK              GENMASK(1, 0)
>> -#define PRODUCER_CRCI_DISABLE          0
>> -#define PRODUCER_CRCI_X_SEL            BIT(0)
>> -#define PRODUCER_CRCI_Y_SEL            BIT(1)
>> -#define CONSUMER_CRCI_MSK              GENMASK(3, 2)
>> -#define CONSUMER_CRCI_DISABLE          0
>> -#define CONSUMER_CRCI_X_SEL            BIT(2)
>> -#define CONSUMER_CRCI_Y_SEL            BIT(3)
>> -#define PRODUCER_TRANS_END_EN          BIT(4)
>> -#define BYPASS                         BIT(16)
>> -#define DIRECT_MODE                    BIT(17)
>> -#define INFINITE_CONS_TRANS            BIT(18)
>> -
>> -#define DML_SW_RESET                   0x08
>> -#define DML_PRODUCER_START             0x0c
>> -#define DML_CONSUMER_START             0x10
>> -#define DML_PRODUCER_PIPE_LOGICAL_SIZE 0x14
>> -#define DML_CONSUMER_PIPE_LOGICAL_SIZE 0x18
>> -#define DML_PIPE_ID                    0x1c
>> -#define PRODUCER_PIPE_ID_SHFT          0
>> -#define PRODUCER_PIPE_ID_MSK           GENMASK(4, 0)
>> -#define CONSUMER_PIPE_ID_SHFT          16
>> -#define CONSUMER_PIPE_ID_MSK           GENMASK(20, 16)
>> -
>> -#define DML_PRODUCER_BAM_BLOCK_SIZE    0x24
>> -#define DML_PRODUCER_BAM_TRANS_SIZE    0x28
>> -
>> -/* other definitions */
>> -#define PRODUCER_PIPE_LOGICAL_SIZE     4096
>> -#define CONSUMER_PIPE_LOGICAL_SIZE     4096
>> -
>> -#define DML_OFFSET                     0x800
>> -
>> -void dml_start_xfer(struct mmci_host *host, struct mmc_data *data)
>> -{
>> -       u32 config;
>> -       void __iomem *base = host->base + DML_OFFSET;
>> -
>> -       if (data->flags & MMC_DATA_READ) {
>> -               /* Read operation: configure DML for producer operation */
>> -               /* Set producer CRCI-x and disable consumer CRCI */
>> -               config = readl_relaxed(base + DML_CONFIG);
>> -               config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_X_SEL;
>> -               config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_DISABLE;
>> -               writel_relaxed(config, base + DML_CONFIG);
>> -
>> -               /* Set the Producer BAM block size */
>> -               writel_relaxed(data->blksz, base + DML_PRODUCER_BAM_BLOCK_SIZE);
>> -
>> -               /* Set Producer BAM Transaction size */
>> -               writel_relaxed(data->blocks * data->blksz,
>> -                              base + DML_PRODUCER_BAM_TRANS_SIZE);
>> -               /* Set Producer Transaction End bit */
>> -               config = readl_relaxed(base + DML_CONFIG);
>> -               config |= PRODUCER_TRANS_END_EN;
>> -               writel_relaxed(config, base + DML_CONFIG);
>> -               /* Trigger producer */
>> -               writel_relaxed(1, base + DML_PRODUCER_START);
>> -       } else {
>> -               /* Write operation: configure DML for consumer operation */
>> -               /* Set consumer CRCI-x and disable producer CRCI*/
>> -               config = readl_relaxed(base + DML_CONFIG);
>> -               config = (config & ~CONSUMER_CRCI_MSK) | CONSUMER_CRCI_X_SEL;
>> -               config = (config & ~PRODUCER_CRCI_MSK) | PRODUCER_CRCI_DISABLE;
>> -               writel_relaxed(config, base + DML_CONFIG);
>> -               /* Clear Producer Transaction End bit */
>> -               config = readl_relaxed(base + DML_CONFIG);
>> -               config &= ~PRODUCER_TRANS_END_EN;
>> -               writel_relaxed(config, base + DML_CONFIG);
>> -               /* Trigger consumer */
>> -               writel_relaxed(1, base + DML_CONSUMER_START);
>> -       }
>> -
>> -       /* make sure the dml is configured before dma is triggered */
>> -       wmb();
>> -}
>> -
>> -static int of_get_dml_pipe_index(struct device_node *np, const char *name)
>> -{
>> -       int index;
>> -       struct of_phandle_args  dma_spec;
>> -
>> -       index = of_property_match_string(np, "dma-names", name);
>> -
>> -       if (index < 0)
>> -               return -ENODEV;
>> -
>> -       if (of_parse_phandle_with_args(np, "dmas", "#dma-cells", index,
>> -                                      &dma_spec))
>> -               return -ENODEV;
>> -
>> -       if (dma_spec.args_count)
>> -               return dma_spec.args[0];
>> -
>> -       return -ENODEV;
>> -}
>> -
>> -/* Initialize the dml hardware connected to SD Card controller */
>> -int dml_hw_init(struct mmci_host *host, struct device_node *np)
>> -{
>> -       u32 config;
>> -       void __iomem *base;
>> -       int consumer_id, producer_id;
>> -
>> -       consumer_id = of_get_dml_pipe_index(np, "tx");
>> -       producer_id = of_get_dml_pipe_index(np, "rx");
>> -
>> -       if (producer_id < 0 || consumer_id < 0)
>> -               return -ENODEV;
>> -
>> -       base = host->base + DML_OFFSET;
>> -
>> -       /* Reset the DML block */
>> -       writel_relaxed(1, base + DML_SW_RESET);
>> -
>> -       /* Disable the producer and consumer CRCI */
>> -       config = (PRODUCER_CRCI_DISABLE | CONSUMER_CRCI_DISABLE);
>> -       /*
>> -        * Disable the bypass mode. Bypass mode will only be used
>> -        * if data transfer is to happen in PIO mode and don't
>> -        * want the BAM interface to connect with SDCC-DML.
>> -        */
>> -       config &= ~BYPASS;
>> -       /*
>> -        * Disable direct mode as we don't DML to MASTER the AHB bus.
>> -        * BAM connected with DML should MASTER the AHB bus.
>> -        */
>> -       config &= ~DIRECT_MODE;
>> -       /*
>> -        * Disable infinite mode transfer as we won't be doing any
>> -        * infinite size data transfers. All data transfer will be
>> -        * of finite data size.
>> -        */
>> -       config &= ~INFINITE_CONS_TRANS;
>> -       writel_relaxed(config, base + DML_CONFIG);
>> -
>> -       /*
>> -        * Initialize the logical BAM pipe size for producer
>> -        * and consumer.
>> -        */
>> -       writel_relaxed(PRODUCER_PIPE_LOGICAL_SIZE,
>> -                      base + DML_PRODUCER_PIPE_LOGICAL_SIZE);
>> -       writel_relaxed(CONSUMER_PIPE_LOGICAL_SIZE,
>> -                      base + DML_CONSUMER_PIPE_LOGICAL_SIZE);
>> -
>> -       /* Initialize Producer/consumer pipe id */
>> -       writel_relaxed(producer_id | (consumer_id << CONSUMER_PIPE_ID_SHFT),
>> -                      base + DML_PIPE_ID);
>> -
>> -       /* Make sure dml initialization is finished */
>> -       mb();
>> -
>> -       return 0;
>> -}
>> diff --git a/drivers/mmc/host/mmci_qcom_dml.h b/drivers/mmc/host/mmci_qcom_dml.h
>> deleted file mode 100644
>> index 6e405d0..0000000
>> --- a/drivers/mmc/host/mmci_qcom_dml.h
>> +++ /dev/null
>> @@ -1,31 +0,0 @@
>> -/*
>> - *
>> - * Copyright (c) 2011, The Linux Foundation. All rights reserved.
>> - *
>> - * This program is free software; you can redistribute it and/or modify
>> - * it under the terms of the GNU General Public License version 2 and
>> - * only version 2 as published by the Free Software Foundation.
>> - *
>> - * This program is distributed in the hope that 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.
>> - *
>> - */
>> -#ifndef __MMC_QCOM_DML_H__
>> -#define __MMC_QCOM_DML_H__
>> -
>> -#ifdef CONFIG_MMC_QCOM_DML
>> -int dml_hw_init(struct mmci_host *host, struct device_node *np);
>> -void dml_start_xfer(struct mmci_host *host, struct mmc_data *data);
>> -#else
>> -static inline int dml_hw_init(struct mmci_host *host, struct device_node *np)
>> -{
>> -       return -ENOSYS;
>> -}
>> -static inline void dml_start_xfer(struct mmci_host *host, struct mmc_data *data)
>> -{
>> -}
>> -#endif /* CONFIG_MMC_QCOM_DML */
>> -
>> -#endif /* __MMC_QCOM_DML_H__ */
>> --
>> 2.7.4
>>

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 01/19] mmc: mmci: regroup and define dma operations
  2018-07-11 12:16       ` Ulf Hansson
@ 2018-07-12  9:09         ` Ludovic BARRE
  0 siblings, 0 replies; 41+ messages in thread
From: Ludovic BARRE @ 2018-07-12  9:09 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Rob Herring, Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	Linux ARM, Linux Kernel Mailing List, devicetree, linux-mmc



On 07/11/2018 02:16 PM, Ulf Hansson wrote:
> On 11 July 2018 at 11:41, Ludovic BARRE <ludovic.barre@st.com> wrote:
>>
>>
>> On 07/05/2018 05:17 PM, Ulf Hansson wrote:
>>>
>>> On 12 June 2018 at 15:14, Ludovic Barre <ludovic.Barre@st.com> wrote:
>>>>
>>>> From: Ludovic Barre <ludovic.barre@st.com>
>>>>
>>>> Prepare mmci driver to manage dma interface by new property.
>>>> This patch defines and regroups dma operations for mmci drivers.
>>>> mmci_dma_XX prototypes are added to call member of mmci_dma_ops
>>>> if not null. Based on legacy need, a mmci dma interface has been
>>>> defined with:
>>>> -mmci_dma_setup
>>>> -mmci_dma_release
>>>> -mmci_dma_pre_req
>>>> -mmci_dma_start
>>>> -mmci_dma_finalize
>>>> -mmci_dma_post_req
>>>> -mmci_dma_error
>>>> -mmci_dma_get_next_data
>>>
>>>
>>> As I suggested for one of the other patches, I would rather turn core
>>> mmci functions into library functions, which can be either invoked
>>> from variant callbacks or assigned directly to them.
>>>
>>> In other words, I would leave the functions that you move in this
>>> patch to stay in mmci.c. Although some needs to be re-factored and we
>>> also needs to make some of them available to be called from another
>>> file, hence the functions needs to be shared via mmci.h rather than
>>> being declared static.
>>
>>
>> In previous exchange mail "STM32MP1 SDMMC driver review"
>> we are said:
>>
>>>>> -dma variant à should fit in Qualcomm implementation, reuse (rename)
>>>>> mmci_qcom_dml.c file and integrate ST dma in.
> 
> Apologize if I may have lead you in a wrong direction, that was not my intent.
> 
> However, by looking at $subject patch, your seems to be unnecessarily
> shuffling code around. I would like to avoid that.
> 
>>>>
>>>> stm32 sdmmc has an internal dma, no need to use dmaengine API;
>>>> So some modifications in mmci (pre/post request, mmci_dma_xx). perhaps
>>>> should be done with an ops or not.
>>>
>>> Yes.
>>>
>>> The Qualcomm variant is also using an internal DMA, hence I thought
>>> there may be something we could re-use, or at least have some new
>>> common ops for.
>>
>> It's not crystal clear for me.
>> Do you always agree with a dma ops which allow to address different
>> DMA transfer:
>> -with dmaengine API
>> -sdmmc idma, without dmaengine API
>> -...
> 
> If we can use a mmci ops callback to manage the variant differences,
> that would be perfect. That combined with making the existing DMA
> functions in mmci.c converted to "library" functions, which the mmci
> ops callbacks can call, in order to re-use code.
> 
> When that isn't really suitable, we may need to add a "quirk" instead,
> which would be specific for that particular variant. Along the lines
> of what we already do for variant specifics inside mmci.c.
> 
> I think we have to decide on case by case basis, what fits best.
> 
> Hope this makes a better explanation. If not, please tell, and I can
> take an initial stab and post a patch to show you with code how I mean
> to move forward.
> 
>>
>>
>>>
>>> Let me take a concrete example on how I would move forward, hopefully
>>> that explains it a bit better. Please see below.
>>>
>>> [...]
>>>
>>>> -/*
>>>> - * All the DMA operation mode stuff goes inside this ifdef.
>>>> - * This assumes that you have a generic DMA device interface,
>>>> - * no custom DMA interfaces are supported.
>>>> - */
>>>> -#ifdef CONFIG_DMA_ENGINE
>>>> -static void mmci_dma_setup(struct mmci_host *host)
>>>> -{
>>>> -       const char *rxname, *txname;
>>>> -       struct variant_data *variant = host->variant;
>>>> -
>>>> -       host->dma_rx_channel =
>>>> dma_request_slave_channel(mmc_dev(host->mmc), "rx");
>>>> -       host->dma_tx_channel =
>>>> dma_request_slave_channel(mmc_dev(host->mmc), "tx");
>>>> -
>>>> -       /* initialize pre request cookie */
>>>> -       host->next_data.cookie = 1;
>>>> -
>>>> -       /*
>>>> -        * If only an RX channel is specified, the driver will
>>>> -        * attempt to use it bidirectionally, however if it is
>>>> -        * is specified but cannot be located, DMA will be disabled.
>>>> -        */
>>>> -       if (host->dma_rx_channel && !host->dma_tx_channel)
>>>> -               host->dma_tx_channel = host->dma_rx_channel;
>>>> -
>>>> -       if (host->dma_rx_channel)
>>>> -               rxname = dma_chan_name(host->dma_rx_channel);
>>>> -       else
>>>> -               rxname = "none";
>>>> -
>>>> -       if (host->dma_tx_channel)
>>>> -               txname = dma_chan_name(host->dma_tx_channel);
>>>> -       else
>>>> -               txname = "none";
>>>> -
>>>> -       dev_info(mmc_dev(host->mmc), "DMA channels RX %s, TX %s\n",
>>>> -                rxname, txname);
>>>> -
>>>> -       /*
>>>> -        * Limit the maximum segment size in any SG entry according to
>>>> -        * the parameters of the DMA engine device.
>>>> -        */
>>>> -       if (host->dma_tx_channel) {
>>>> -               struct device *dev = host->dma_tx_channel->device->dev;
>>>> -               unsigned int max_seg_size = dma_get_max_seg_size(dev);
>>>> -
>>>> -               if (max_seg_size < host->mmc->max_seg_size)
>>>> -                       host->mmc->max_seg_size = max_seg_size;
>>>> -       }
>>>> -       if (host->dma_rx_channel) {
>>>> -               struct device *dev = host->dma_rx_channel->device->dev;
>>>> -               unsigned int max_seg_size = dma_get_max_seg_size(dev);
>>>> -
>>>> -               if (max_seg_size < host->mmc->max_seg_size)
>>>> -                       host->mmc->max_seg_size = max_seg_size;
>>>> -       }
>>>
>>>
>>> Everything above shall be left as generic library function,
>>> mmci_dma_setup() and I would share it via mmci.h and thus change it
>>> from being static.
>>>
>>
>> each interfaces mmci_dma_XXX have very different needs depending
>> dma_ops (legacy, sdmmc idma)
> 
> Right. This was just one example.
> 
> If I understand, what you are suggesting is to make all of them being
> variant specific callbacks, so I assume that would solve the problems.
> Just to be clear, I have no problem with that.
> 
> Although, that doesn't mean we can't re-use existing dma functions in
> mmci.c, when that makes sense.

Yes, when examine dmaengine_XX ops and sdmmc_idma_XX ops (in patch 01
and 17) there are few common piece of code. So I think we will have same
dma functions pointer in mmci_ops. However, the cookie management may be
shared

> 
> [...]
> 
> Kind regards
> Uffe
> 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 02/19] mmc: mmci: merge qcom dml feature into mmci dma
  2018-07-11 15:19     ` Ludovic BARRE
@ 2018-07-13 11:17       ` Ulf Hansson
  2018-07-13 13:08         ` Ludovic BARRE
  0 siblings, 1 reply; 41+ messages in thread
From: Ulf Hansson @ 2018-07-13 11:17 UTC (permalink / raw)
  To: Ludovic BARRE
  Cc: Rob Herring, Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	Linux ARM, Linux Kernel Mailing List, devicetree, linux-mmc

On 11 July 2018 at 17:19, Ludovic BARRE <ludovic.barre@st.com> wrote:
>
>
> On 07/05/2018 05:26 PM, Ulf Hansson wrote:
>>
>> On 12 June 2018 at 15:14, Ludovic Barre <ludovic.Barre@st.com> wrote:
>>>
>>> From: Ludovic Barre <ludovic.barre@st.com>
>>>
>>> This patch integrates qcom dml feature into mmci_dma file.
>>> Qualcomm Data Mover lite/local is already a variant of mmci dmaengine.
>>>
>>> Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
>>> ---
>>>   drivers/mmc/host/Makefile        |   1 -
>>>   drivers/mmc/host/mmci.c          |   1 -
>>>   drivers/mmc/host/mmci.h          |  35 ++++++++
>>>   drivers/mmc/host/mmci_dma.c      | 135 ++++++++++++++++++++++++++++-
>>>   drivers/mmc/host/mmci_qcom_dml.c | 177
>>> ---------------------------------------
>>>   drivers/mmc/host/mmci_qcom_dml.h |  31 -------
>>>   6 files changed, 169 insertions(+), 211 deletions(-)
>>>   delete mode 100644 drivers/mmc/host/mmci_qcom_dml.c
>>>   delete mode 100644 drivers/mmc/host/mmci_qcom_dml.h
>>
>>
>> No, this is not the way to go. Instead I I think there are two options.
>>
>> 1) Keep mmci_qcom_dml.c|h and thus add new files for the stm32 dma
>> variant.
>>
>> 2) Start by renaming mmci_qcom_dml.* to mmc_dma.* and then in the next
>> step add the code for stm32 dma into the renamed files.
>>
>> I guess if there is some overlap in functionality, 2) may be best as
>> it could easier avoid open coding. However, I am fine with whatever
>> option and I expect that you knows what is best.
>
>
> After patch 01 & 05 comments:
> I will try to define a mmci_ops which contain some functions pointer
> called by mmci.c (core).
> A variant defines its mmci_ops.
> where do you define the specific function:
> -in a single file, mmci-ops.c or other (for the name, I'm not inspirated)
> -or in specific file for each variant mmci-qcom.c or mmci-stm32.c
>
> following the comment (above), I think we define a single file?

If I understand the question, the problem is how we should assign the
mmc host ops, which corresponds to the probed variant data!?

I took a stub at it and posted two patches which I think you should be
able to build upon. Please have a look.

[...]

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 02/19] mmc: mmci: merge qcom dml feature into mmci dma
  2018-07-13 11:17       ` Ulf Hansson
@ 2018-07-13 13:08         ` Ludovic BARRE
  2018-07-30 15:15           ` Ulf Hansson
  0 siblings, 1 reply; 41+ messages in thread
From: Ludovic BARRE @ 2018-07-13 13:08 UTC (permalink / raw)
  To: Ulf Hansson
  Cc: Rob Herring, Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	Linux ARM, Linux Kernel Mailing List, devicetree, linux-mmc



On 07/13/2018 01:17 PM, Ulf Hansson wrote:
> On 11 July 2018 at 17:19, Ludovic BARRE <ludovic.barre@st.com> wrote:
>>
>>
>> On 07/05/2018 05:26 PM, Ulf Hansson wrote:
>>>
>>> On 12 June 2018 at 15:14, Ludovic Barre <ludovic.Barre@st.com> wrote:
>>>>
>>>> From: Ludovic Barre <ludovic.barre@st.com>
>>>>
>>>> This patch integrates qcom dml feature into mmci_dma file.
>>>> Qualcomm Data Mover lite/local is already a variant of mmci dmaengine.
>>>>
>>>> Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
>>>> ---
>>>>    drivers/mmc/host/Makefile        |   1 -
>>>>    drivers/mmc/host/mmci.c          |   1 -
>>>>    drivers/mmc/host/mmci.h          |  35 ++++++++
>>>>    drivers/mmc/host/mmci_dma.c      | 135 ++++++++++++++++++++++++++++-
>>>>    drivers/mmc/host/mmci_qcom_dml.c | 177
>>>> ---------------------------------------
>>>>    drivers/mmc/host/mmci_qcom_dml.h |  31 -------
>>>>    6 files changed, 169 insertions(+), 211 deletions(-)
>>>>    delete mode 100644 drivers/mmc/host/mmci_qcom_dml.c
>>>>    delete mode 100644 drivers/mmc/host/mmci_qcom_dml.h
>>>
>>>
>>> No, this is not the way to go. Instead I I think there are two options.
>>>
>>> 1) Keep mmci_qcom_dml.c|h and thus add new files for the stm32 dma
>>> variant.
>>>
>>> 2) Start by renaming mmci_qcom_dml.* to mmc_dma.* and then in the next
>>> step add the code for stm32 dma into the renamed files.
>>>
>>> I guess if there is some overlap in functionality, 2) may be best as
>>> it could easier avoid open coding. However, I am fine with whatever
>>> option and I expect that you knows what is best.
>>
>>
>> After patch 01 & 05 comments:
>> I will try to define a mmci_ops which contain some functions pointer
>> called by mmci.c (core).
>> A variant defines its mmci_ops.
>> where do you define the specific function:
>> -in a single file, mmci-ops.c or other (for the name, I'm not inspirated)
>> -or in specific file for each variant mmci-qcom.c or mmci-stm32.c
>>
>> following the comment (above), I think we define a single file?
> 
> If I understand the question, the problem is how we should assign the
> mmc host ops, which corresponds to the probed variant data!?
> 
> I took a stub at it and posted two patches which I think you should be
> able to build upon. Please have a look.

I review your patch on mmci_host_ops and init, I agree with your series,
I was going in the same direction.
The comment above was on file organization, what do you prefer?
-a single file with: all callback and all mmci_host_ops of each variant
-or each variant in specific file (like sdhci): mmci-qcom.c | 
mmci-stm32.c ...

> 
> [...]
> 
> Kind regards
> Uffe
> 

^ permalink raw reply	[flat|nested] 41+ messages in thread

* Re: [PATCH 02/19] mmc: mmci: merge qcom dml feature into mmci dma
  2018-07-13 13:08         ` Ludovic BARRE
@ 2018-07-30 15:15           ` Ulf Hansson
  0 siblings, 0 replies; 41+ messages in thread
From: Ulf Hansson @ 2018-07-30 15:15 UTC (permalink / raw)
  To: Ludovic BARRE
  Cc: Rob Herring, Maxime Coquelin, Alexandre Torgue, Gerald Baeza,
	Linux ARM, Linux Kernel Mailing List, devicetree, linux-mmc

On 13 July 2018 at 15:08, Ludovic BARRE <ludovic.barre@st.com> wrote:
>
>
> On 07/13/2018 01:17 PM, Ulf Hansson wrote:
>>
>> On 11 July 2018 at 17:19, Ludovic BARRE <ludovic.barre@st.com> wrote:
>>>
>>>
>>>
>>> On 07/05/2018 05:26 PM, Ulf Hansson wrote:
>>>>
>>>>
>>>> On 12 June 2018 at 15:14, Ludovic Barre <ludovic.Barre@st.com> wrote:
>>>>>
>>>>>
>>>>> From: Ludovic Barre <ludovic.barre@st.com>
>>>>>
>>>>> This patch integrates qcom dml feature into mmci_dma file.
>>>>> Qualcomm Data Mover lite/local is already a variant of mmci dmaengine.
>>>>>
>>>>> Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
>>>>> ---
>>>>>    drivers/mmc/host/Makefile        |   1 -
>>>>>    drivers/mmc/host/mmci.c          |   1 -
>>>>>    drivers/mmc/host/mmci.h          |  35 ++++++++
>>>>>    drivers/mmc/host/mmci_dma.c      | 135 ++++++++++++++++++++++++++++-
>>>>>    drivers/mmc/host/mmci_qcom_dml.c | 177
>>>>> ---------------------------------------
>>>>>    drivers/mmc/host/mmci_qcom_dml.h |  31 -------
>>>>>    6 files changed, 169 insertions(+), 211 deletions(-)
>>>>>    delete mode 100644 drivers/mmc/host/mmci_qcom_dml.c
>>>>>    delete mode 100644 drivers/mmc/host/mmci_qcom_dml.h
>>>>
>>>>
>>>>
>>>> No, this is not the way to go. Instead I I think there are two options.
>>>>
>>>> 1) Keep mmci_qcom_dml.c|h and thus add new files for the stm32 dma
>>>> variant.
>>>>
>>>> 2) Start by renaming mmci_qcom_dml.* to mmc_dma.* and then in the next
>>>> step add the code for stm32 dma into the renamed files.
>>>>
>>>> I guess if there is some overlap in functionality, 2) may be best as
>>>> it could easier avoid open coding. However, I am fine with whatever
>>>> option and I expect that you knows what is best.
>>>
>>>
>>>
>>> After patch 01 & 05 comments:
>>> I will try to define a mmci_ops which contain some functions pointer
>>> called by mmci.c (core).
>>> A variant defines its mmci_ops.
>>> where do you define the specific function:
>>> -in a single file, mmci-ops.c or other (for the name, I'm not inspirated)
>>> -or in specific file for each variant mmci-qcom.c or mmci-stm32.c
>>>
>>> following the comment (above), I think we define a single file?
>>
>>
>> If I understand the question, the problem is how we should assign the
>> mmc host ops, which corresponds to the probed variant data!?
>>
>> I took a stub at it and posted two patches which I think you should be
>> able to build upon. Please have a look.
>
>
> I review your patch on mmci_host_ops and init, I agree with your series,
> I was going in the same direction.
> The comment above was on file organization, what do you prefer?
> -a single file with: all callback and all mmci_host_ops of each variant
> -or each variant in specific file (like sdhci): mmci-qcom.c | mmci-stm32.c

The latter seems better.

Kind regards
Uffe

^ permalink raw reply	[flat|nested] 41+ messages in thread

end of thread, other threads:[~2018-07-30 15:15 UTC | newest]

Thread overview: 41+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-06-12 13:14 [PATCH 00/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
2018-06-12 13:14 ` [PATCH 01/19] mmc: mmci: regroup and define dma operations Ludovic Barre
2018-07-05 15:17   ` Ulf Hansson
2018-07-11  9:41     ` Ludovic BARRE
2018-07-11 12:16       ` Ulf Hansson
2018-07-12  9:09         ` Ludovic BARRE
2018-06-12 13:14 ` [PATCH 02/19] mmc: mmci: merge qcom dml feature into mmci dma Ludovic Barre
2018-07-05 15:26   ` Ulf Hansson
2018-07-11 15:19     ` Ludovic BARRE
2018-07-13 11:17       ` Ulf Hansson
2018-07-13 13:08         ` Ludovic BARRE
2018-07-30 15:15           ` Ulf Hansson
2018-06-12 13:14 ` [PATCH 03/19] mmc: mmci: add datactrl block size variant property Ludovic Barre
2018-06-12 13:14 ` [PATCH 04/19] mmc: mmci: expand startbiterr to irqmask and error check Ludovic Barre
2018-06-12 13:14 ` [PATCH 05/19] mmc: mmci: allow to overwrite clock/power procedure to specific variant Ludovic Barre
2018-07-05 13:48   ` Ulf Hansson
2018-07-11 12:19     ` Ludovic BARRE
2018-07-11 12:38       ` Ulf Hansson
2018-06-12 13:14 ` [PATCH 06/19] mmc: mmci: add variant properties to define cpsm & cmdresp bits Ludovic Barre
2018-07-05 14:20   ` Ulf Hansson
2018-06-12 13:14 ` [PATCH 07/19] mmc: mmci: add variant property to define dpsm bit Ludovic Barre
2018-06-12 13:14 ` [PATCH 08/19] mmc: mmci: add variant property to define irq pio mask Ludovic Barre
2018-06-12 13:14 ` [PATCH 09/19] mmc: mmci: add variant property to write datactrl before command Ludovic Barre
2018-06-12 13:14 ` [PATCH 10/19] mmc: mmci: add variant property to allow remain data Ludovic Barre
2018-07-05 13:55   ` Ulf Hansson
2018-06-12 13:14 ` [PATCH 11/19] mmc: mmci: add variant property to check specific data constraint Ludovic Barre
2018-06-12 13:14 ` [PATCH 12/19] mmc: mmci: add variant property to request a reset Ludovic Barre
2018-06-25 21:23   ` Rob Herring
2018-06-12 13:14 ` [PATCH 13/19] mmc: mmci: send stop cmd if a data command fail Ludovic Barre
2018-07-04 13:37   ` Ulf Hansson
2018-07-11  8:57     ` Ludovic BARRE
2018-06-12 13:14 ` [PATCH 14/19] mmc: mmci: add clock divider for stm32 sdmmc Ludovic Barre
2018-06-12 13:14 ` [PATCH 15/19] mmc: mmci: add stm32 sdmmc registers Ludovic Barre
2018-06-12 13:14 ` [PATCH 16/19] mmc: mmci: add DT bindings for STM32 sdmmc Ludovic Barre
2018-06-25 21:47   ` Rob Herring
2018-06-12 13:14 ` [PATCH 17/19] mmc: mmci: add stm32 sdmmc idma support Ludovic Barre
2018-06-12 13:14 ` [PATCH 18/19] mmc: mmci: add specific clk/pwr procedure for stm32 sdmmc Ludovic Barre
2018-07-05 14:49   ` Ulf Hansson
2018-06-12 13:14 ` [PATCH 19/19] mmc: mmci: add stm32 sdmmc variant Ludovic Barre
2018-06-29 13:51 ` [PATCH 00/19] " Ludovic BARRE
2018-06-29 15:18   ` Ulf Hansson

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).