All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4 1/3] m68k: mcf5441x: add support for esdhc mmc controller
@ 2020-05-13 20:41 Angelo Dureghello
  2020-05-13 20:41 ` [PATCH v4 2/3] mmc: sdhci: add quirks for be to le byte swapping Angelo Dureghello
  2020-05-13 20:41 ` [PATCH v4 3/3] mmc: host: add Coldfire esdhc support Angelo Dureghello
  0 siblings, 2 replies; 6+ messages in thread
From: Angelo Dureghello @ 2020-05-13 20:41 UTC (permalink / raw)
  To: adrian.hunter, ulf.hansson, gerg; +Cc: linux-mmc, linux-m68k, Angelo Dureghello

Add support for sdhci-edshc mmc controller.

Signed-off-by: Angelo Dureghello <angelo.dureghello@timesys.com>
---
Changes for v3:
- removed volatile cast from clk.c
Changes for v4:
- comment style fix in m5441xsim.h
---
 arch/m68k/coldfire/clk.c                    | 15 ++++++++++
 arch/m68k/coldfire/device.c                 | 33 +++++++++++++++++++--
 arch/m68k/coldfire/m5441x.c                 | 12 +++++++-
 arch/m68k/include/asm/m5441xsim.h           | 15 ++++++++++
 arch/m68k/include/asm/mcfclk.h              |  2 ++
 include/linux/platform_data/mmc-esdhc-mcf.h | 17 +++++++++++
 6 files changed, 91 insertions(+), 3 deletions(-)
 create mode 100644 include/linux/platform_data/mmc-esdhc-mcf.h

diff --git a/arch/m68k/coldfire/clk.c b/arch/m68k/coldfire/clk.c
index 7bc666e482eb..8d048a72e173 100644
--- a/arch/m68k/coldfire/clk.c
+++ b/arch/m68k/coldfire/clk.c
@@ -73,6 +73,21 @@ struct clk_ops clk_ops1 = {
 #endif /* MCFPM_PPMCR1 */
 #endif /* MCFPM_PPMCR0 */
 
+static void __clk_enable2(struct clk *clk)
+{
+	__set_bit(clk->slot, MCFSDHC_CLK);
+}
+
+static void __clk_disable2(struct clk *clk)
+{
+	__clear_bit(clk->slot, MCFSDHC_CLK);
+}
+
+struct clk_ops clk_ops2 = {
+	.enable		= __clk_enable2,
+	.disable	= __clk_disable2,
+};
+
 struct clk *clk_get(struct device *dev, const char *id)
 {
 	const char *clk_name = dev ? dev_name(dev) : id ? id : NULL;
diff --git a/arch/m68k/coldfire/device.c b/arch/m68k/coldfire/device.c
index b4103b6bfdeb..9ef4ec0aea00 100644
--- a/arch/m68k/coldfire/device.c
+++ b/arch/m68k/coldfire/device.c
@@ -22,6 +22,7 @@
 #include <asm/mcfqspi.h>
 #include <linux/platform_data/edma.h>
 #include <linux/platform_data/dma-mcf-edma.h>
+#include <linux/platform_data/mmc-esdhc-mcf.h>
 
 /*
  *	All current ColdFire parts contain from 2, 3, 4 or 10 UARTS.
@@ -551,9 +552,35 @@ static struct platform_device mcf_edma = {
 		.platform_data = &mcf_edma_data,
 	}
 };
-
 #endif /* IS_ENABLED(CONFIG_MCF_EDMA) */
 
+#if IS_ENABLED(CONFIG_MMC)
+static struct mcf_esdhc_platform_data mcf_esdhc_data = {
+	.max_bus_width = 4,
+	.cd_type = ESDHC_CD_NONE,
+};
+
+static struct resource mcf_esdhc_resources[] = {
+	{
+		.start = MCFSDHC_BASE,
+		.end = MCFSDHC_BASE + MCFSDHC_SIZE - 1,
+		.flags = IORESOURCE_MEM,
+	}, {
+		.start = MCF_IRQ_SDHC,
+		.end = MCF_IRQ_SDHC,
+		.flags = IORESOURCE_IRQ,
+	},
+};
+
+static struct platform_device mcf_esdhc = {
+	.name			= "sdhci-esdhc-mcf",
+	.id			= 0,
+	.num_resources		= ARRAY_SIZE(mcf_esdhc_resources),
+	.resource		= mcf_esdhc_resources,
+	.dev.platform_data	= &mcf_esdhc_data,
+};
+#endif /* IS_ENABLED(CONFIG_MMC) */
+
 static struct platform_device *mcf_devices[] __initdata = {
 	&mcf_uart,
 #if IS_ENABLED(CONFIG_FEC)
@@ -586,6 +613,9 @@ static struct platform_device *mcf_devices[] __initdata = {
 #if IS_ENABLED(CONFIG_MCF_EDMA)
 	&mcf_edma,
 #endif
+#if IS_ENABLED(CONFIG_MMC)
+	&mcf_esdhc,
+#endif
 };
 
 /*
@@ -614,4 +644,3 @@ static int __init mcf_init_devices(void)
 }
 
 arch_initcall(mcf_init_devices);
-
diff --git a/arch/m68k/coldfire/m5441x.c b/arch/m68k/coldfire/m5441x.c
index 5bd24c9b865d..ffa02de1a3fb 100644
--- a/arch/m68k/coldfire/m5441x.c
+++ b/arch/m68k/coldfire/m5441x.c
@@ -52,7 +52,7 @@ DEFINE_CLK(0, "mcfssi.0", 47, MCF_CLK);
 DEFINE_CLK(0, "pll.0", 48, MCF_CLK);
 DEFINE_CLK(0, "mcfrng.0", 49, MCF_CLK);
 DEFINE_CLK(0, "mcfssi.1", 50, MCF_CLK);
-DEFINE_CLK(0, "mcfsdhc.0", 51, MCF_CLK);
+DEFINE_CLK(0, "sdhci-esdhc-mcf.0", 51, MCF_CLK);
 DEFINE_CLK(0, "enet-fec.0", 53, MCF_CLK);
 DEFINE_CLK(0, "enet-fec.1", 54, MCF_CLK);
 DEFINE_CLK(0, "switch.0", 55, MCF_CLK);
@@ -74,6 +74,10 @@ DEFINE_CLK(1, "mcfpwm.0", 34, MCF_BUSCLK);
 DEFINE_CLK(1, "sys.0", 36, MCF_BUSCLK);
 DEFINE_CLK(1, "gpio.0", 37, MCF_BUSCLK);
 
+DEFINE_CLK(2, "ipg.0", 0, MCF_CLK);
+DEFINE_CLK(2, "ahb.0", 1, MCF_CLK);
+DEFINE_CLK(2, "per.0", 2, MCF_CLK);
+
 struct clk *mcf_clks[] = {
 	&__clk_0_2,
 	&__clk_0_8,
@@ -131,6 +135,11 @@ struct clk *mcf_clks[] = {
 	&__clk_1_34,
 	&__clk_1_36,
 	&__clk_1_37,
+
+	&__clk_2_0,
+	&__clk_2_1,
+	&__clk_2_2,
+
 	NULL,
 };
 
@@ -151,6 +160,7 @@ static struct clk * const enable_clks[] __initconst = {
 	&__clk_0_33, /* pit.1 */
 	&__clk_0_37, /* eport */
 	&__clk_0_48, /* pll */
+	&__clk_0_51, /* esdhc */
 
 	&__clk_1_36, /* CCM/reset module/Power management */
 	&__clk_1_37, /* gpio */
diff --git a/arch/m68k/include/asm/m5441xsim.h b/arch/m68k/include/asm/m5441xsim.h
index 4892f314ff38..e091e36d3464 100644
--- a/arch/m68k/include/asm/m5441xsim.h
+++ b/arch/m68k/include/asm/m5441xsim.h
@@ -278,6 +278,13 @@
 #define MCFGPIO_IRQ_VECBASE	(MCFINT_VECBASE - MCFGPIO_IRQ_MIN)
 #define MCFGPIO_PIN_MAX		87
 
+/*
+ * Phase Locked Loop (PLL)
+ */
+#define MCF_PLL_CR		0xFC0C0000
+#define MCF_PLL_DR		0xFC0C0004
+#define MCF_PLL_SR		0xFC0C0008
+
 /*
  *  DSPI module.
  */
@@ -298,5 +305,13 @@
 #define MCFEDMA_IRQ_INTR16	(MCFINT1_VECBASE + MCFEDMA_EDMA_INTR16)
 #define MCFEDMA_IRQ_INTR56	(MCFINT2_VECBASE + MCFEDMA_EDMA_INTR56)
 #define MCFEDMA_IRQ_ERR	(MCFINT0_VECBASE + MCFINT0_EDMA_ERR)
+/*
+ *  esdhc module.
+ */
+#define MCFSDHC_BASE		0xfc0cc000
+#define MCFSDHC_SIZE		256
+#define MCFINT2_SDHC		31
+#define MCF_IRQ_SDHC		(MCFINT2_VECBASE + MCFINT2_SDHC)
+#define MCFSDHC_CLK		(MCFSDHC_BASE + 0x2c)
 
 #endif /* m5441xsim_h */
diff --git a/arch/m68k/include/asm/mcfclk.h b/arch/m68k/include/asm/mcfclk.h
index 0aca504fae31..722627e06d66 100644
--- a/arch/m68k/include/asm/mcfclk.h
+++ b/arch/m68k/include/asm/mcfclk.h
@@ -30,6 +30,8 @@ extern struct clk_ops clk_ops0;
 extern struct clk_ops clk_ops1;
 #endif /* MCFPM_PPMCR1 */
 
+extern struct clk_ops clk_ops2;
+
 #define DEFINE_CLK(clk_bank, clk_name, clk_slot, clk_rate) \
 static struct clk __clk_##clk_bank##_##clk_slot = { \
 	.name = clk_name, \
diff --git a/include/linux/platform_data/mmc-esdhc-mcf.h b/include/linux/platform_data/mmc-esdhc-mcf.h
new file mode 100644
index 000000000000..85cb786a62fe
--- /dev/null
+++ b/include/linux/platform_data/mmc-esdhc-mcf.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __LINUX_PLATFORM_DATA_MCF_ESDHC_H__
+#define __LINUX_PLATFORM_DATA_MCF_ESDHC_H__
+
+enum cd_types {
+	ESDHC_CD_NONE,		/* no CD, neither controller nor gpio */
+	ESDHC_CD_CONTROLLER,	/* mmc controller internal CD */
+	ESDHC_CD_PERMANENT,	/* no CD, card permanently wired to host */
+};
+
+struct mcf_esdhc_platform_data {
+	int max_bus_width;
+	int cd_type;
+};
+
+#endif /* __LINUX_PLATFORM_DATA_MCF_ESDHC_H__ */
-- 
2.26.2


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

* [PATCH v4 2/3] mmc: sdhci: add quirks for be to le byte swapping
  2020-05-13 20:41 [PATCH v4 1/3] m68k: mcf5441x: add support for esdhc mmc controller Angelo Dureghello
@ 2020-05-13 20:41 ` Angelo Dureghello
  2020-05-15 13:39   ` Adrian Hunter
  2020-05-13 20:41 ` [PATCH v4 3/3] mmc: host: add Coldfire esdhc support Angelo Dureghello
  1 sibling, 1 reply; 6+ messages in thread
From: Angelo Dureghello @ 2020-05-13 20:41 UTC (permalink / raw)
  To: adrian.hunter, ulf.hansson, gerg; +Cc: linux-mmc, linux-m68k, Angelo Dureghello

Some controller as the ColdFire eshdc may require an endianness
byte swap, becouse DMA read endianness is not configurable.

Signed-off-by: Angelo Dureghello <angelo.dureghello@timesys.com>
---
Changes for v3:
- add post request callback
Changes for v4:
none
---
 drivers/mmc/host/sdhci.c | 7 +++++++
 drivers/mmc/host/sdhci.h | 2 ++
 2 files changed, 9 insertions(+)

diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 3f716466fcfd..d3bfbb6e11d5 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -637,6 +637,13 @@ static int sdhci_pre_dma_transfer(struct sdhci_host *host,
 			sg_copy_to_buffer(data->sg, data->sg_len,
 					  host->bounce_buffer,
 					  length);
+
+			/*
+			 * Endianness can't be swapped in the sg list,
+			 * since the sg list is processed again later on.
+			 */
+			if (host->ops->pre_dma_transfer)
+				host->ops->pre_dma_transfer(host, data);
 		}
 		/* Switch ownership to the DMA */
 		dma_sync_single_for_device(host->mmc->parent,
diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
index 79dffbb731d3..076054308423 100644
--- a/drivers/mmc/host/sdhci.h
+++ b/drivers/mmc/host/sdhci.h
@@ -653,6 +653,8 @@ struct sdhci_ops {
 	void	(*voltage_switch)(struct sdhci_host *host);
 	void	(*adma_write_desc)(struct sdhci_host *host, void **desc,
 				   dma_addr_t addr, int len, unsigned int cmd);
+	void	(*pre_dma_transfer)(struct sdhci_host *host,
+				    struct mmc_data *data);
 	void	(*request_done)(struct sdhci_host *host,
 				struct mmc_request *mrq);
 };
-- 
2.26.2


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

* [PATCH v4 3/3] mmc: host: add Coldfire esdhc support
  2020-05-13 20:41 [PATCH v4 1/3] m68k: mcf5441x: add support for esdhc mmc controller Angelo Dureghello
  2020-05-13 20:41 ` [PATCH v4 2/3] mmc: sdhci: add quirks for be to le byte swapping Angelo Dureghello
@ 2020-05-13 20:41 ` Angelo Dureghello
  2020-05-15 14:02   ` Adrian Hunter
  1 sibling, 1 reply; 6+ messages in thread
From: Angelo Dureghello @ 2020-05-13 20:41 UTC (permalink / raw)
  To: adrian.hunter, ulf.hansson, gerg; +Cc: linux-mmc, linux-m68k, Angelo Dureghello

This driver has been developed as a separate module starting
from the similar sdhci-esdhc-imx.c.

Reasons for a separate sdchi-esdhc-mcf driver:

- m68K architecture does not support devicetrees, so modifying
sdhci-of-esdhc.c that is devicetree-related adding platform data
seems not appropriate,
- clock-related part, has to be implemented specifically for
mcf5441x family (see esdhc_mcf_pltfm_set_clock()),
- this is a big endian cpu accessing a big endian controller,
but about sdma, this controller does not support hw swap, which
needs to be handled with specific code,
- some other minor differences but mainly to avoid risks on
tweaking inside largely used imx driver. Adding just a small
size ColdFire-specific driver, with benefits in a further less
risky maintenance.

Signed-off-by: Angelo Dureghello <angelo.dureghello@timesys.com>
---
Changes for v3:
- fix write support
Changes for v4:
none
---
 drivers/mmc/host/Kconfig           |  13 +
 drivers/mmc/host/Makefile          |   1 +
 drivers/mmc/host/sdhci-esdhc-mcf.c | 503 +++++++++++++++++++++++++++++
 3 files changed, 517 insertions(+)
 create mode 100644 drivers/mmc/host/sdhci-esdhc-mcf.c

diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 462b5352fea7..da793fc95203 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -235,6 +235,19 @@ config MMC_SDHCI_CNS3XXX
 
 	  If unsure, say N.
 
+config MMC_SDHCI_ESDHC_MCF
+	tristate "SDHCI support for the Freescale eSDHC ColdFire controller"
+	depends on M5441x
+	depends on MMC_SDHCI_PLTFM
+	select MMC_SDHCI_IO_ACCESSORS
+	help
+	  This selects the Freescale eSDHC controller support for
+	  ColdFire mcf5441x devices.
+
+	  If you have a controller with this interface, say Y or M here.
+
+	  If unsure, say N.
+
 config MMC_SDHCI_ESDHC_IMX
 	tristate "SDHCI support for the Freescale eSDHC/uSDHC i.MX controller"
 	depends on ARCH_MXC
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index b929ef941208..af2cdaadc4d3 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -82,6 +82,7 @@ obj-$(CONFIG_MMC_REALTEK_USB)	+= rtsx_usb_sdmmc.o
 obj-$(CONFIG_MMC_SDHCI_PLTFM)		+= sdhci-pltfm.o
 obj-$(CONFIG_MMC_SDHCI_CADENCE)		+= sdhci-cadence.o
 obj-$(CONFIG_MMC_SDHCI_CNS3XXX)		+= sdhci-cns3xxx.o
+obj-$(CONFIG_MMC_SDHCI_ESDHC_MCF)       += sdhci-esdhc-mcf.o
 obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX)	+= sdhci-esdhc-imx.o
 obj-$(CONFIG_MMC_SDHCI_DOVE)		+= sdhci-dove.o
 obj-$(CONFIG_MMC_SDHCI_TEGRA)		+= sdhci-tegra.o
diff --git a/drivers/mmc/host/sdhci-esdhc-mcf.c b/drivers/mmc/host/sdhci-esdhc-mcf.c
new file mode 100644
index 000000000000..658b687c7a51
--- /dev/null
+++ b/drivers/mmc/host/sdhci-esdhc-mcf.c
@@ -0,0 +1,503 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Freescale eSDHC ColdFire family controller driver, platform bus.
+ *
+ * Copyright (c) 2020 Timesys Corporation
+ *   Author: Angelo Dureghello <angelo.dureghello@timesys.it>
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/platform_data/mmc-esdhc-mcf.h>
+#include <linux/mmc/mmc.h>
+#include "sdhci-pltfm.h"
+#include "sdhci-esdhc.h"
+
+#define	ESDHC_PROCTL_D3CD		0x08
+#define ESDHC_SYS_CTRL_DTOCV_MASK	0x0f
+#define ESDHC_DEFAULT_HOST_CONTROL	0x28
+
+/*
+ * Freescale eSDHC has DMA ERR flag at bit 28, not as std spec says, bit 25.
+ */
+#define ESDHC_INT_VENDOR_SPEC_DMA_ERR	(1 << 28)
+
+struct pltfm_mcf_data {
+	struct clk *clk_ipg;
+	struct clk *clk_ahb;
+	struct clk *clk_per;
+	int aside;
+	int current_bus_width;
+};
+
+static inline void esdhc_mcf_buffer_swap32(u32 *buf, int len)
+{
+	int i;
+	u32 temp;
+
+	len = (len + 3) >> 2;
+
+	for (i = 0; i < len;  i++) {
+		temp = swab32(*buf);
+		*buf++ = temp;
+	}
+}
+
+static inline void esdhc_clrset_be(struct sdhci_host *host,
+				   u32 mask, u32 val, int reg)
+{
+	void __iomem *base = host->ioaddr + (reg & ~3);
+	u8 shift = (reg & 3) << 3;
+
+	mask <<= shift;
+	val <<= shift;
+
+	if (reg == SDHCI_HOST_CONTROL)
+		val |= ESDHC_PROCTL_D3CD;
+
+	writel((readl(base) & ~mask) | val, base);
+}
+
+/*
+ * Note: mcf is big-endian, single bytes need to be accessed at big endian
+ * offsets.
+ */
+static void esdhc_mcf_writeb_be(struct sdhci_host *host, u8 val, int reg)
+{
+	void __iomem *base = host->ioaddr + (reg & ~3);
+	u8 shift = (reg & 3) << 3;
+	u32 mask = ~(0xff << shift);
+
+	if (reg == SDHCI_HOST_CONTROL) {
+		u32 host_ctrl = ESDHC_DEFAULT_HOST_CONTROL;
+		u8 dma_bits = (val & SDHCI_CTRL_DMA_MASK) >> 3;
+		u8 tmp = readb(host->ioaddr + SDHCI_HOST_CONTROL + 1);
+
+		tmp &= ~0x03;
+		tmp |= dma_bits;
+
+		/*
+		 * Recomposition needed, restore always endianness and
+		 * keep D3CD and AI, just setting bus width.
+		 */
+		host_ctrl |= val;
+		host_ctrl |= (dma_bits << 8);
+		writel(host_ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
+
+		return;
+	}
+
+	writel((readl(base) & mask) | (val << shift), base);
+}
+
+static void esdhc_mcf_writew_be(struct sdhci_host *host, u16 val, int reg)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host);
+	void __iomem *base = host->ioaddr + (reg & ~3);
+	u8 shift = (reg & 3) << 3;
+	u32 mask = ~(0xffff << shift);
+
+	switch (reg) {
+	case SDHCI_TRANSFER_MODE:
+		mcf_data->aside = val;
+		return;
+	case SDHCI_COMMAND:
+		if (host->cmd->opcode == MMC_STOP_TRANSMISSION)
+			val |= SDHCI_CMD_ABORTCMD;
+
+		/*
+		 * As for the fsl driver,
+		 * we have to set the mode in a single write here.
+		 */
+		writel(val << 16 | mcf_data->aside,
+			       host->ioaddr + SDHCI_TRANSFER_MODE);
+		return;
+	}
+
+	writel((readl(base) & mask) | (val << shift), base);
+}
+
+static void esdhc_mcf_writel_be(struct sdhci_host *host, u32 val, int reg)
+{
+	writel(val, host->ioaddr + reg);
+}
+
+static u8 esdhc_mcf_readb_be(struct sdhci_host *host, int reg)
+{
+	if (reg == SDHCI_HOST_CONTROL) {
+		u8 __iomem *base = host->ioaddr + (reg & ~3);
+		u16 val = readw(base + 2);
+		u8 dma_bits = (val >> 5) & SDHCI_CTRL_DMA_MASK;
+		u8 host_ctrl = val & 0xff;
+
+		host_ctrl &= ~SDHCI_CTRL_DMA_MASK;
+		host_ctrl |= dma_bits;
+
+		return host_ctrl;
+	}
+
+	return readb(host->ioaddr + (reg ^ 0x3));
+}
+
+static u16 esdhc_mcf_readw_be(struct sdhci_host *host, int reg)
+{
+	/*
+	 * For SDHCI_HOST_VERSION, sdhci specs defines 0xFE,
+	 * a wrong offset for us, we are at 0xFC.
+	 */
+	if (reg == SDHCI_HOST_VERSION)
+		reg -= 2;
+
+	return readw(host->ioaddr + (reg ^ 0x2));
+}
+
+static u32 esdhc_mcf_readl_be(struct sdhci_host *host, int reg)
+{
+	u32 val;
+
+	val = readl(host->ioaddr + reg);
+
+	/*
+	 * RM (25.3.9) sd pin clock must never exceed 25Mhz.
+	 * So forcing legacy mode at 25Mhz.
+	 */
+	if (unlikely(reg == SDHCI_CAPABILITIES))
+		val &= ~SDHCI_CAN_DO_HISPD;
+
+	if (unlikely(reg == SDHCI_INT_STATUS)) {
+		if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) {
+			val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR;
+			val |= SDHCI_INT_ADMA_ERROR;
+		}
+	}
+
+	return val;
+}
+
+static unsigned int esdhc_mcf_get_max_timeout_count(struct sdhci_host *host)
+{
+	return 1 << 27;
+}
+
+static void esdhc_mcf_set_timeout(struct sdhci_host *host,
+				  struct mmc_command *cmd)
+{
+	/* Use maximum timeout counter */
+	esdhc_clrset_be(host, ESDHC_SYS_CTRL_DTOCV_MASK, 0xE,
+				SDHCI_TIMEOUT_CONTROL);
+}
+
+static void esdhc_mcf_reset(struct sdhci_host *host, u8 mask)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host);
+
+	sdhci_reset(host, mask);
+
+	esdhc_clrset_be(host, ESDHC_CTRL_BUSWIDTH_MASK,
+			mcf_data->current_bus_width, SDHCI_HOST_CONTROL);
+
+	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
+	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
+}
+
+static unsigned int esdhc_mcf_pltfm_get_max_clock(struct sdhci_host *host)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+
+	return pltfm_host->clock;
+}
+
+static unsigned int esdhc_mcf_pltfm_get_min_clock(struct sdhci_host *host)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+
+	return pltfm_host->clock / 256 / 16;
+}
+
+static void esdhc_mcf_pltfm_set_clock(struct sdhci_host *host,
+					 unsigned int clock)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	unsigned long *pll_dr = (unsigned long *)MCF_PLL_DR;
+	u32 fvco, fsys, fesdhc, temp;
+	const int sdclkfs[] = {2, 4, 8, 16, 32, 64, 128, 256};
+	int delta, old_delta = clock;
+	int i, q, ri, rq;
+
+	if (clock == 0) {
+		host->mmc->actual_clock = 0;
+		return;
+	}
+
+	/*
+	 * ColdFire eSDHC clock.s
+	 *
+	 * pll -+-> / outdiv1 --> fsys
+	 *      +-> / outdiv3 --> eSDHC clock ---> / SDCCLKFS / DVS
+	 *
+	 * mcf5441x datasheet says:
+	 * (8.1.2) eSDHC should be 40 MHz max
+	 * (25.3.9) eSDHC input is, as example, 96 Mhz ...
+	 * (25.3.9) sd pin clock must never exceed 25Mhz
+	 *
+	 * fvco = fsys * outdvi1 + 1
+	 * fshdc = fvco / outdiv3 + 1
+	 */
+	temp = readl(pll_dr);
+	fsys = pltfm_host->clock;
+	fvco = fsys * ((temp & 0x1f) + 1);
+	fesdhc = fvco / (((temp >> 10) & 0x1f) + 1);
+
+	for (i = 0; i < 8; ++i) {
+		int result = fesdhc / sdclkfs[i];
+
+		for (q = 1; q < 17; ++q) {
+			int finale = result / q;
+
+			delta = abs(clock - finale);
+
+			if (delta < old_delta) {
+				old_delta = delta;
+				ri = i;
+				rq = q;
+			}
+		}
+	}
+
+	/*
+	 * Apply divisors and re-enable all the clocks
+	 */
+	temp = ((sdclkfs[ri] >> 1) << 8) | ((rq - 1) << 4) |
+		(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN);
+	esdhc_clrset_be(host, 0x0000fff7, temp, SDHCI_CLOCK_CONTROL);
+
+	host->mmc->actual_clock = clock;
+
+	mdelay(1);
+}
+
+static void esdhc_mcf_pltfm_set_bus_width(struct sdhci_host *host, int width)
+{
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host);
+
+	switch (width) {
+	case MMC_BUS_WIDTH_4:
+		mcf_data->current_bus_width = ESDHC_CTRL_4BITBUS;
+		break;
+	default:
+		mcf_data->current_bus_width = 0;
+		break;
+	}
+
+	esdhc_clrset_be(host, ESDHC_CTRL_BUSWIDTH_MASK,
+			mcf_data->current_bus_width, SDHCI_HOST_CONTROL);
+}
+
+static void esdhc_mcf_request_done(struct sdhci_host *host,
+				   struct mmc_request *mrq)
+{
+	struct scatterlist *sg;
+	u32 *buffer;
+	int i;
+
+	if (!mrq->data || !mrq->data->bytes_xfered)
+		goto exit_done;
+
+	if (mmc_get_dma_dir(mrq->data) != DMA_FROM_DEVICE)
+		goto exit_done;
+
+	/*
+	 * On mcf5441x there is no hw sdma option/flag to select the dma
+	 * transfer endiannes. A swap after the transfer is needed.
+	 */
+	for_each_sg(mrq->data->sg, sg, mrq->data->sg_len, i) {
+		buffer = (u32 *)sg_virt(sg);
+		esdhc_mcf_buffer_swap32(buffer, sg->length);
+	}
+
+exit_done:
+	mmc_request_done(host->mmc, mrq);
+}
+
+static void esdhc_mcf_pre_dma_transfer(struct sdhci_host *host,
+	struct mmc_data *data)
+{
+	esdhc_mcf_buffer_swap32((u32 *)host->bounce_buffer,
+				data->blksz * data->blocks);
+}
+
+static struct sdhci_ops sdhci_esdhc_ops = {
+	.reset = esdhc_mcf_reset,
+	.set_clock = esdhc_mcf_pltfm_set_clock,
+	.get_max_clock = esdhc_mcf_pltfm_get_max_clock,
+	.get_min_clock = esdhc_mcf_pltfm_get_min_clock,
+	.set_bus_width = esdhc_mcf_pltfm_set_bus_width,
+	.get_max_timeout_count = esdhc_mcf_get_max_timeout_count,
+	.set_timeout = esdhc_mcf_set_timeout,
+	.write_b = esdhc_mcf_writeb_be,
+	.write_w = esdhc_mcf_writew_be,
+	.write_l = esdhc_mcf_writel_be,
+	.read_b = esdhc_mcf_readb_be,
+	.read_w = esdhc_mcf_readw_be,
+	.read_l = esdhc_mcf_readl_be,
+	.pre_dma_transfer = esdhc_mcf_pre_dma_transfer,
+	.request_done = esdhc_mcf_request_done,
+};
+
+static const struct sdhci_pltfm_data sdhci_esdhc_mcf_pdata = {
+	.ops = &sdhci_esdhc_ops,
+	.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_FORCE_DMA,
+		 /*
+		  * Mandatory quirk,
+		  * controller does not support cmd23,
+		  * without, on > 8G cards cmd23 is used, and
+		  * driver times out.
+		  */
+		  SDHCI_QUIRK2_HOST_NO_CMD23,
+};
+
+static int esdhc_mcf_plat_init(struct sdhci_host *host,
+				     struct pltfm_mcf_data *mcf_data)
+{
+	struct mcf_esdhc_platform_data *plat_data;
+
+	if (!host->mmc->parent->platform_data) {
+		dev_err(mmc_dev(host->mmc), "no platform data!\n");
+		return -EINVAL;
+	}
+
+	plat_data = ((struct mcf_esdhc_platform_data *)
+			host->mmc->parent->platform_data);
+
+	/* Card_detect */
+	switch (plat_data->cd_type) {
+	default:
+	case ESDHC_CD_CONTROLLER:
+		/* We have a working card_detect back */
+		host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
+		break;
+	case ESDHC_CD_PERMANENT:
+		host->mmc->caps |= MMC_CAP_NONREMOVABLE;
+		break;
+	case ESDHC_CD_NONE:
+		break;
+	}
+
+	switch (plat_data->max_bus_width) {
+	case 4:
+		host->mmc->caps |= MMC_CAP_4_BIT_DATA;
+		break;
+	case 1:
+	default:
+		host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
+		break;
+	}
+
+	return 0;
+}
+
+static int sdhci_esdhc_mcf_probe(struct platform_device *pdev)
+{
+	struct sdhci_host *host;
+	struct sdhci_pltfm_host *pltfm_host;
+	struct pltfm_mcf_data *mcf_data;
+	int err;
+
+	host = sdhci_pltfm_init(pdev, &sdhci_esdhc_mcf_pdata,
+				sizeof(*mcf_data));
+
+	if (IS_ERR(host))
+		return PTR_ERR(host);
+
+	pltfm_host = sdhci_priv(host);
+	mcf_data = sdhci_pltfm_priv(pltfm_host);
+
+	host->sdma_boundary = 0;
+
+	host->flags |= SDHCI_AUTO_CMD12;
+
+	mcf_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
+	if (IS_ERR(mcf_data->clk_ipg)) {
+		err = PTR_ERR(mcf_data->clk_ipg);
+		goto err_exit;
+	}
+
+	mcf_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
+	if (IS_ERR(mcf_data->clk_ahb)) {
+		err = PTR_ERR(mcf_data->clk_ahb);
+		goto err_exit;
+	}
+
+	mcf_data->clk_per = devm_clk_get(&pdev->dev, "per");
+	if (IS_ERR(mcf_data->clk_per)) {
+		err = PTR_ERR(mcf_data->clk_per);
+		goto err_exit;
+	}
+
+	pltfm_host->clk = mcf_data->clk_per;
+	pltfm_host->clock = clk_get_rate(pltfm_host->clk);
+	err = clk_prepare_enable(mcf_data->clk_per);
+	if (err)
+		goto err_exit;
+
+	err = clk_prepare_enable(mcf_data->clk_ipg);
+	if (err) {
+		clk_disable_unprepare(mcf_data->clk_per);
+		goto err_exit;
+	}
+	err = clk_prepare_enable(mcf_data->clk_ahb);
+	if (err) {
+		clk_disable_unprepare(mcf_data->clk_per);
+		clk_disable_unprepare(mcf_data->clk_ipg);
+		goto err_exit;
+	}
+
+	err = esdhc_mcf_plat_init(host, mcf_data);
+	if (err)
+		goto err_exit;
+
+	err = sdhci_add_host(host);
+	if (err)
+		goto err_exit;
+
+	return 0;
+
+err_exit:
+	sdhci_pltfm_free(pdev);
+
+	return err;
+}
+
+static int sdhci_esdhc_mcf_remove(struct platform_device *pdev)
+{
+	struct sdhci_host *host = platform_get_drvdata(pdev);
+	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
+	struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host);
+
+	sdhci_remove_host(host, 0);
+
+	clk_disable_unprepare(mcf_data->clk_ipg);
+	clk_disable_unprepare(mcf_data->clk_ahb);
+	clk_disable_unprepare(mcf_data->clk_per);
+
+	sdhci_pltfm_free(pdev);
+
+	return 0;
+}
+
+static struct platform_driver sdhci_esdhc_mcf_driver = {
+	.driver	= {
+		.name = "sdhci-esdhc-mcf",
+	},
+	.probe = sdhci_esdhc_mcf_probe,
+	.remove = sdhci_esdhc_mcf_remove,
+};
+
+module_platform_driver(sdhci_esdhc_mcf_driver);
+
+MODULE_DESCRIPTION("SDHCI driver for Freescale ColdFire eSDHC");
+MODULE_AUTHOR("Angelo Dureghello <angelo.dureghello@timesys.com>");
+MODULE_LICENSE("GPL v2");
-- 
2.26.2


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

* Re: [PATCH v4 2/3] mmc: sdhci: add quirks for be to le byte swapping
  2020-05-13 20:41 ` [PATCH v4 2/3] mmc: sdhci: add quirks for be to le byte swapping Angelo Dureghello
@ 2020-05-15 13:39   ` Adrian Hunter
  2020-05-15 14:16     ` Angelo Dureghello
  0 siblings, 1 reply; 6+ messages in thread
From: Adrian Hunter @ 2020-05-15 13:39 UTC (permalink / raw)
  To: Angelo Dureghello, ulf.hansson, gerg; +Cc: linux-mmc, linux-m68k

On 13/05/20 11:41 pm, Angelo Dureghello wrote:
> Some controller as the ColdFire eshdc may require an endianness
> byte swap, becouse DMA read endianness is not configurable.
> 
> Signed-off-by: Angelo Dureghello <angelo.dureghello@timesys.com>
> ---
> Changes for v3:
> - add post request callback
> Changes for v4:
> none
> ---
>  drivers/mmc/host/sdhci.c | 7 +++++++
>  drivers/mmc/host/sdhci.h | 2 ++
>  2 files changed, 9 insertions(+)
> 
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 3f716466fcfd..d3bfbb6e11d5 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -637,6 +637,13 @@ static int sdhci_pre_dma_transfer(struct sdhci_host *host,
>  			sg_copy_to_buffer(data->sg, data->sg_len,
>  					  host->bounce_buffer,
>  					  length);
> +
> +			/*
> +			 * Endianness can't be swapped in the sg list,
> +			 * since the sg list is processed again later on.
> +			 */

The data in the sg list must not be changed because we don't know how
else it might be being used.  Maybe leave out the comment.

> +			if (host->ops->pre_dma_transfer)
> +				host->ops->pre_dma_transfer(host, data);

Let's make this slightly more generic i.e.

			if (host->ops->copy_to_bounce_buffer) {
				host->ops->copy_to_bounce_buffer(host, data);
			} else {
				sg_copy_to_buffer(data->sg, data->sg_len,
						  host->bounce_buffer,
						  length);
			}


>  		}
>  		/* Switch ownership to the DMA */
>  		dma_sync_single_for_device(host->mmc->parent,
> diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> index 79dffbb731d3..076054308423 100644
> --- a/drivers/mmc/host/sdhci.h
> +++ b/drivers/mmc/host/sdhci.h
> @@ -653,6 +653,8 @@ struct sdhci_ops {
>  	void	(*voltage_switch)(struct sdhci_host *host);
>  	void	(*adma_write_desc)(struct sdhci_host *host, void **desc,
>  				   dma_addr_t addr, int len, unsigned int cmd);
> +	void	(*pre_dma_transfer)(struct sdhci_host *host,
> +				    struct mmc_data *data);
>  	void	(*request_done)(struct sdhci_host *host,
>  				struct mmc_request *mrq);
>  };
> 


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

* Re: [PATCH v4 3/3] mmc: host: add Coldfire esdhc support
  2020-05-13 20:41 ` [PATCH v4 3/3] mmc: host: add Coldfire esdhc support Angelo Dureghello
@ 2020-05-15 14:02   ` Adrian Hunter
  0 siblings, 0 replies; 6+ messages in thread
From: Adrian Hunter @ 2020-05-15 14:02 UTC (permalink / raw)
  To: Angelo Dureghello, ulf.hansson, gerg; +Cc: linux-mmc, linux-m68k

On 13/05/20 11:41 pm, Angelo Dureghello wrote:
> This driver has been developed as a separate module starting
> from the similar sdhci-esdhc-imx.c.
> 
> Reasons for a separate sdchi-esdhc-mcf driver:
> 
> - m68K architecture does not support devicetrees, so modifying
> sdhci-of-esdhc.c that is devicetree-related adding platform data
> seems not appropriate,
> - clock-related part, has to be implemented specifically for
> mcf5441x family (see esdhc_mcf_pltfm_set_clock()),
> - this is a big endian cpu accessing a big endian controller,
> but about sdma, this controller does not support hw swap, which
> needs to be handled with specific code,
> - some other minor differences but mainly to avoid risks on
> tweaking inside largely used imx driver. Adding just a small
> size ColdFire-specific driver, with benefits in a further less
> risky maintenance.

Since this is new code, I suggest running scripts/checkpatch.pl --strict on it.

> 
> Signed-off-by: Angelo Dureghello <angelo.dureghello@timesys.com>
> ---
> Changes for v3:
> - fix write support
> Changes for v4:
> none
> ---
>  drivers/mmc/host/Kconfig           |  13 +
>  drivers/mmc/host/Makefile          |   1 +
>  drivers/mmc/host/sdhci-esdhc-mcf.c | 503 +++++++++++++++++++++++++++++
>  3 files changed, 517 insertions(+)
>  create mode 100644 drivers/mmc/host/sdhci-esdhc-mcf.c
> 
> diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
> index 462b5352fea7..da793fc95203 100644
> --- a/drivers/mmc/host/Kconfig
> +++ b/drivers/mmc/host/Kconfig
> @@ -235,6 +235,19 @@ config MMC_SDHCI_CNS3XXX
>  
>  	  If unsure, say N.
>  
> +config MMC_SDHCI_ESDHC_MCF
> +	tristate "SDHCI support for the Freescale eSDHC ColdFire controller"
> +	depends on M5441x
> +	depends on MMC_SDHCI_PLTFM
> +	select MMC_SDHCI_IO_ACCESSORS
> +	help
> +	  This selects the Freescale eSDHC controller support for
> +	  ColdFire mcf5441x devices.
> +
> +	  If you have a controller with this interface, say Y or M here.
> +
> +	  If unsure, say N.
> +
>  config MMC_SDHCI_ESDHC_IMX
>  	tristate "SDHCI support for the Freescale eSDHC/uSDHC i.MX controller"
>  	depends on ARCH_MXC
> diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
> index b929ef941208..af2cdaadc4d3 100644
> --- a/drivers/mmc/host/Makefile
> +++ b/drivers/mmc/host/Makefile
> @@ -82,6 +82,7 @@ obj-$(CONFIG_MMC_REALTEK_USB)	+= rtsx_usb_sdmmc.o
>  obj-$(CONFIG_MMC_SDHCI_PLTFM)		+= sdhci-pltfm.o
>  obj-$(CONFIG_MMC_SDHCI_CADENCE)		+= sdhci-cadence.o
>  obj-$(CONFIG_MMC_SDHCI_CNS3XXX)		+= sdhci-cns3xxx.o
> +obj-$(CONFIG_MMC_SDHCI_ESDHC_MCF)       += sdhci-esdhc-mcf.o
>  obj-$(CONFIG_MMC_SDHCI_ESDHC_IMX)	+= sdhci-esdhc-imx.o
>  obj-$(CONFIG_MMC_SDHCI_DOVE)		+= sdhci-dove.o
>  obj-$(CONFIG_MMC_SDHCI_TEGRA)		+= sdhci-tegra.o
> diff --git a/drivers/mmc/host/sdhci-esdhc-mcf.c b/drivers/mmc/host/sdhci-esdhc-mcf.c
> new file mode 100644
> index 000000000000..658b687c7a51
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-esdhc-mcf.c
> @@ -0,0 +1,503 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Freescale eSDHC ColdFire family controller driver, platform bus.
> + *
> + * Copyright (c) 2020 Timesys Corporation
> + *   Author: Angelo Dureghello <angelo.dureghello@timesys.it>
> + */
> +
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/platform_data/mmc-esdhc-mcf.h>
> +#include <linux/mmc/mmc.h>
> +#include "sdhci-pltfm.h"
> +#include "sdhci-esdhc.h"
> +
> +#define	ESDHC_PROCTL_D3CD		0x08
> +#define ESDHC_SYS_CTRL_DTOCV_MASK	0x0f
> +#define ESDHC_DEFAULT_HOST_CONTROL	0x28
> +
> +/*
> + * Freescale eSDHC has DMA ERR flag at bit 28, not as std spec says, bit 25.
> + */
> +#define ESDHC_INT_VENDOR_SPEC_DMA_ERR	(1 << 28)
> +
> +struct pltfm_mcf_data {
> +	struct clk *clk_ipg;
> +	struct clk *clk_ahb;
> +	struct clk *clk_per;
> +	int aside;
> +	int current_bus_width;
> +};
> +
> +static inline void esdhc_mcf_buffer_swap32(u32 *buf, int len)
> +{
> +	int i;
> +	u32 temp;
> +
> +	len = (len + 3) >> 2;
> +
> +	for (i = 0; i < len;  i++) {
> +		temp = swab32(*buf);
> +		*buf++ = temp;
> +	}
> +}
> +
> +static inline void esdhc_clrset_be(struct sdhci_host *host,
> +				   u32 mask, u32 val, int reg)
> +{
> +	void __iomem *base = host->ioaddr + (reg & ~3);
> +	u8 shift = (reg & 3) << 3;
> +
> +	mask <<= shift;
> +	val <<= shift;
> +
> +	if (reg == SDHCI_HOST_CONTROL)
> +		val |= ESDHC_PROCTL_D3CD;
> +
> +	writel((readl(base) & ~mask) | val, base);
> +}
> +
> +/*
> + * Note: mcf is big-endian, single bytes need to be accessed at big endian
> + * offsets.
> + */
> +static void esdhc_mcf_writeb_be(struct sdhci_host *host, u8 val, int reg)
> +{
> +	void __iomem *base = host->ioaddr + (reg & ~3);
> +	u8 shift = (reg & 3) << 3;
> +	u32 mask = ~(0xff << shift);
> +
> +	if (reg == SDHCI_HOST_CONTROL) {
> +		u32 host_ctrl = ESDHC_DEFAULT_HOST_CONTROL;
> +		u8 dma_bits = (val & SDHCI_CTRL_DMA_MASK) >> 3;
> +		u8 tmp = readb(host->ioaddr + SDHCI_HOST_CONTROL + 1);
> +
> +		tmp &= ~0x03;
> +		tmp |= dma_bits;
> +
> +		/*
> +		 * Recomposition needed, restore always endianness and
> +		 * keep D3CD and AI, just setting bus width.
> +		 */
> +		host_ctrl |= val;
> +		host_ctrl |= (dma_bits << 8);
> +		writel(host_ctrl, host->ioaddr + SDHCI_HOST_CONTROL);
> +
> +		return;
> +	}
> +
> +	writel((readl(base) & mask) | (val << shift), base);
> +}
> +
> +static void esdhc_mcf_writew_be(struct sdhci_host *host, u16 val, int reg)
> +{
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host);
> +	void __iomem *base = host->ioaddr + (reg & ~3);
> +	u8 shift = (reg & 3) << 3;
> +	u32 mask = ~(0xffff << shift);
> +
> +	switch (reg) {
> +	case SDHCI_TRANSFER_MODE:
> +		mcf_data->aside = val;
> +		return;
> +	case SDHCI_COMMAND:
> +		if (host->cmd->opcode == MMC_STOP_TRANSMISSION)
> +			val |= SDHCI_CMD_ABORTCMD;
> +
> +		/*
> +		 * As for the fsl driver,
> +		 * we have to set the mode in a single write here.
> +		 */
> +		writel(val << 16 | mcf_data->aside,
> +			       host->ioaddr + SDHCI_TRANSFER_MODE);
> +		return;
> +	}
> +
> +	writel((readl(base) & mask) | (val << shift), base);
> +}
> +
> +static void esdhc_mcf_writel_be(struct sdhci_host *host, u32 val, int reg)
> +{
> +	writel(val, host->ioaddr + reg);
> +}
> +
> +static u8 esdhc_mcf_readb_be(struct sdhci_host *host, int reg)
> +{
> +	if (reg == SDHCI_HOST_CONTROL) {
> +		u8 __iomem *base = host->ioaddr + (reg & ~3);
> +		u16 val = readw(base + 2);
> +		u8 dma_bits = (val >> 5) & SDHCI_CTRL_DMA_MASK;
> +		u8 host_ctrl = val & 0xff;
> +
> +		host_ctrl &= ~SDHCI_CTRL_DMA_MASK;
> +		host_ctrl |= dma_bits;
> +
> +		return host_ctrl;
> +	}
> +
> +	return readb(host->ioaddr + (reg ^ 0x3));
> +}
> +
> +static u16 esdhc_mcf_readw_be(struct sdhci_host *host, int reg)
> +{
> +	/*
> +	 * For SDHCI_HOST_VERSION, sdhci specs defines 0xFE,
> +	 * a wrong offset for us, we are at 0xFC.
> +	 */
> +	if (reg == SDHCI_HOST_VERSION)
> +		reg -= 2;
> +
> +	return readw(host->ioaddr + (reg ^ 0x2));
> +}
> +
> +static u32 esdhc_mcf_readl_be(struct sdhci_host *host, int reg)
> +{
> +	u32 val;
> +
> +	val = readl(host->ioaddr + reg);
> +
> +	/*
> +	 * RM (25.3.9) sd pin clock must never exceed 25Mhz.
> +	 * So forcing legacy mode at 25Mhz.
> +	 */
> +	if (unlikely(reg == SDHCI_CAPABILITIES))
> +		val &= ~SDHCI_CAN_DO_HISPD;
> +
> +	if (unlikely(reg == SDHCI_INT_STATUS)) {
> +		if (val & ESDHC_INT_VENDOR_SPEC_DMA_ERR) {
> +			val &= ~ESDHC_INT_VENDOR_SPEC_DMA_ERR;
> +			val |= SDHCI_INT_ADMA_ERROR;
> +		}
> +	}
> +
> +	return val;
> +}
> +
> +static unsigned int esdhc_mcf_get_max_timeout_count(struct sdhci_host *host)
> +{
> +	return 1 << 27;
> +}
> +
> +static void esdhc_mcf_set_timeout(struct sdhci_host *host,
> +				  struct mmc_command *cmd)
> +{
> +	/* Use maximum timeout counter */
> +	esdhc_clrset_be(host, ESDHC_SYS_CTRL_DTOCV_MASK, 0xE,
> +				SDHCI_TIMEOUT_CONTROL);
> +}
> +
> +static void esdhc_mcf_reset(struct sdhci_host *host, u8 mask)
> +{
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host);
> +
> +	sdhci_reset(host, mask);
> +
> +	esdhc_clrset_be(host, ESDHC_CTRL_BUSWIDTH_MASK,
> +			mcf_data->current_bus_width, SDHCI_HOST_CONTROL);
> +
> +	sdhci_writel(host, host->ier, SDHCI_INT_ENABLE);
> +	sdhci_writel(host, host->ier, SDHCI_SIGNAL_ENABLE);
> +}
> +
> +static unsigned int esdhc_mcf_pltfm_get_max_clock(struct sdhci_host *host)
> +{
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +
> +	return pltfm_host->clock;
> +}
> +
> +static unsigned int esdhc_mcf_pltfm_get_min_clock(struct sdhci_host *host)
> +{
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +
> +	return pltfm_host->clock / 256 / 16;
> +}
> +
> +static void esdhc_mcf_pltfm_set_clock(struct sdhci_host *host,
> +					 unsigned int clock)
> +{
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	unsigned long *pll_dr = (unsigned long *)MCF_PLL_DR;
> +	u32 fvco, fsys, fesdhc, temp;
> +	const int sdclkfs[] = {2, 4, 8, 16, 32, 64, 128, 256};
> +	int delta, old_delta = clock;
> +	int i, q, ri, rq;
> +
> +	if (clock == 0) {
> +		host->mmc->actual_clock = 0;
> +		return;
> +	}
> +
> +	/*
> +	 * ColdFire eSDHC clock.s
> +	 *
> +	 * pll -+-> / outdiv1 --> fsys
> +	 *      +-> / outdiv3 --> eSDHC clock ---> / SDCCLKFS / DVS
> +	 *
> +	 * mcf5441x datasheet says:
> +	 * (8.1.2) eSDHC should be 40 MHz max
> +	 * (25.3.9) eSDHC input is, as example, 96 Mhz ...
> +	 * (25.3.9) sd pin clock must never exceed 25Mhz
> +	 *
> +	 * fvco = fsys * outdvi1 + 1
> +	 * fshdc = fvco / outdiv3 + 1
> +	 */
> +	temp = readl(pll_dr);
> +	fsys = pltfm_host->clock;
> +	fvco = fsys * ((temp & 0x1f) + 1);
> +	fesdhc = fvco / (((temp >> 10) & 0x1f) + 1);
> +
> +	for (i = 0; i < 8; ++i) {
> +		int result = fesdhc / sdclkfs[i];
> +
> +		for (q = 1; q < 17; ++q) {
> +			int finale = result / q;
> +
> +			delta = abs(clock - finale);
> +
> +			if (delta < old_delta) {
> +				old_delta = delta;
> +				ri = i;
> +				rq = q;
> +			}
> +		}
> +	}
> +
> +	/*
> +	 * Apply divisors and re-enable all the clocks
> +	 */
> +	temp = ((sdclkfs[ri] >> 1) << 8) | ((rq - 1) << 4) |
> +		(ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN);
> +	esdhc_clrset_be(host, 0x0000fff7, temp, SDHCI_CLOCK_CONTROL);
> +
> +	host->mmc->actual_clock = clock;
> +
> +	mdelay(1);
> +}
> +
> +static void esdhc_mcf_pltfm_set_bus_width(struct sdhci_host *host, int width)
> +{
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host);
> +
> +	switch (width) {
> +	case MMC_BUS_WIDTH_4:
> +		mcf_data->current_bus_width = ESDHC_CTRL_4BITBUS;
> +		break;
> +	default:
> +		mcf_data->current_bus_width = 0;
> +		break;
> +	}
> +
> +	esdhc_clrset_be(host, ESDHC_CTRL_BUSWIDTH_MASK,
> +			mcf_data->current_bus_width, SDHCI_HOST_CONTROL);
> +}
> +
> +static void esdhc_mcf_request_done(struct sdhci_host *host,
> +				   struct mmc_request *mrq)
> +{
> +	struct scatterlist *sg;
> +	u32 *buffer;
> +	int i;
> +
> +	if (!mrq->data || !mrq->data->bytes_xfered)
> +		goto exit_done;
> +
> +	if (mmc_get_dma_dir(mrq->data) != DMA_FROM_DEVICE)
> +		goto exit_done;
> +
> +	/*
> +	 * On mcf5441x there is no hw sdma option/flag to select the dma
> +	 * transfer endiannes. A swap after the transfer is needed.
> +	 */
> +	for_each_sg(mrq->data->sg, sg, mrq->data->sg_len, i) {
> +		buffer = (u32 *)sg_virt(sg);
> +		esdhc_mcf_buffer_swap32(buffer, sg->length);
> +	}
> +
> +exit_done:
> +	mmc_request_done(host->mmc, mrq);
> +}
> +
> +static void esdhc_mcf_pre_dma_transfer(struct sdhci_host *host,
> +	struct mmc_data *data)
> +{
> +	esdhc_mcf_buffer_swap32((u32 *)host->bounce_buffer,
> +				data->blksz * data->blocks);
> +}
> +
> +static struct sdhci_ops sdhci_esdhc_ops = {
> +	.reset = esdhc_mcf_reset,
> +	.set_clock = esdhc_mcf_pltfm_set_clock,
> +	.get_max_clock = esdhc_mcf_pltfm_get_max_clock,
> +	.get_min_clock = esdhc_mcf_pltfm_get_min_clock,
> +	.set_bus_width = esdhc_mcf_pltfm_set_bus_width,
> +	.get_max_timeout_count = esdhc_mcf_get_max_timeout_count,
> +	.set_timeout = esdhc_mcf_set_timeout,
> +	.write_b = esdhc_mcf_writeb_be,
> +	.write_w = esdhc_mcf_writew_be,
> +	.write_l = esdhc_mcf_writel_be,
> +	.read_b = esdhc_mcf_readb_be,
> +	.read_w = esdhc_mcf_readw_be,
> +	.read_l = esdhc_mcf_readl_be,
> +	.pre_dma_transfer = esdhc_mcf_pre_dma_transfer,
> +	.request_done = esdhc_mcf_request_done,
> +};
> +
> +static const struct sdhci_pltfm_data sdhci_esdhc_mcf_pdata = {
> +	.ops = &sdhci_esdhc_ops,
> +	.quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_FORCE_DMA,
> +		 /*
> +		  * Mandatory quirk,
> +		  * controller does not support cmd23,
> +		  * without, on > 8G cards cmd23 is used, and
> +		  * driver times out.
> +		  */
> +		  SDHCI_QUIRK2_HOST_NO_CMD23,
> +};
> +
> +static int esdhc_mcf_plat_init(struct sdhci_host *host,
> +				     struct pltfm_mcf_data *mcf_data)
> +{
> +	struct mcf_esdhc_platform_data *plat_data;
> +
> +	if (!host->mmc->parent->platform_data) {
> +		dev_err(mmc_dev(host->mmc), "no platform data!\n");
> +		return -EINVAL;
> +	}
> +
> +	plat_data = ((struct mcf_esdhc_platform_data *)
> +			host->mmc->parent->platform_data);
> +
> +	/* Card_detect */
> +	switch (plat_data->cd_type) {
> +	default:
> +	case ESDHC_CD_CONTROLLER:
> +		/* We have a working card_detect back */
> +		host->quirks &= ~SDHCI_QUIRK_BROKEN_CARD_DETECTION;
> +		break;
> +	case ESDHC_CD_PERMANENT:
> +		host->mmc->caps |= MMC_CAP_NONREMOVABLE;
> +		break;
> +	case ESDHC_CD_NONE:
> +		break;
> +	}
> +
> +	switch (plat_data->max_bus_width) {
> +	case 4:
> +		host->mmc->caps |= MMC_CAP_4_BIT_DATA;
> +		break;
> +	case 1:
> +	default:
> +		host->quirks |= SDHCI_QUIRK_FORCE_1_BIT_DATA;
> +		break;
> +	}
> +
> +	return 0;
> +}
> +
> +static int sdhci_esdhc_mcf_probe(struct platform_device *pdev)
> +{
> +	struct sdhci_host *host;
> +	struct sdhci_pltfm_host *pltfm_host;
> +	struct pltfm_mcf_data *mcf_data;
> +	int err;
> +
> +	host = sdhci_pltfm_init(pdev, &sdhci_esdhc_mcf_pdata,
> +				sizeof(*mcf_data));
> +
> +	if (IS_ERR(host))
> +		return PTR_ERR(host);
> +
> +	pltfm_host = sdhci_priv(host);
> +	mcf_data = sdhci_pltfm_priv(pltfm_host);
> +
> +	host->sdma_boundary = 0;
> +
> +	host->flags |= SDHCI_AUTO_CMD12;
> +
> +	mcf_data->clk_ipg = devm_clk_get(&pdev->dev, "ipg");
> +	if (IS_ERR(mcf_data->clk_ipg)) {
> +		err = PTR_ERR(mcf_data->clk_ipg);
> +		goto err_exit;
> +	}
> +
> +	mcf_data->clk_ahb = devm_clk_get(&pdev->dev, "ahb");
> +	if (IS_ERR(mcf_data->clk_ahb)) {
> +		err = PTR_ERR(mcf_data->clk_ahb);
> +		goto err_exit;
> +	}
> +
> +	mcf_data->clk_per = devm_clk_get(&pdev->dev, "per");
> +	if (IS_ERR(mcf_data->clk_per)) {
> +		err = PTR_ERR(mcf_data->clk_per);
> +		goto err_exit;
> +	}
> +
> +	pltfm_host->clk = mcf_data->clk_per;
> +	pltfm_host->clock = clk_get_rate(pltfm_host->clk);
> +	err = clk_prepare_enable(mcf_data->clk_per);
> +	if (err)
> +		goto err_exit;
> +
> +	err = clk_prepare_enable(mcf_data->clk_ipg);
> +	if (err) {
> +		clk_disable_unprepare(mcf_data->clk_per);

It would be preferable to use kernel error style i.e.

		goto unprep_per;

> +		goto err_exit;
> +	}
> +	err = clk_prepare_enable(mcf_data->clk_ahb);
> +	if (err) {
> +		clk_disable_unprepare(mcf_data->clk_per);
> +		clk_disable_unprepare(mcf_data->clk_ipg);

Ditto i.e.
		goto unprep_ipg;

> +		goto err_exit;
> +	}
> +
> +	err = esdhc_mcf_plat_init(host, mcf_data);
> +	if (err)
> +		goto err_exit;

		goto unprep_ahb;

> +
> +	err = sdhci_add_host(host);

It looks like you are relying on the bounce buffer, so it would be prudent
to check it got allocated.

	err = sdhci_setup_host(host);
	if (err)
		goto unprep_ahb;

	if (!host->bounce_buffer) {
		dev_err(blah blah);
		err = -ENOMEM;
		goto cleanup;
	}

	err = __sdhci_add_host(host);
	if (err)
		goto cleanup;

	return 0;



> +	if (err)
> +		goto err_exit;
> +
> +	return 0;
> +

cleanup:
	sdhci_cleanup_host(host);
unprep_ahb:
	clk_disable_unprepare(mcf_data->clk_ahb);
unprep_ipg:
	clk_disable_unprepare(mcf_data->clk_ipg);
unprep_per:
	clk_disable_unprepare(mcf_data->clk_per);
> +err_exit:
> +	sdhci_pltfm_free(pdev);
> +
> +	return err;
> +}
> +
> +static int sdhci_esdhc_mcf_remove(struct platform_device *pdev)
> +{
> +	struct sdhci_host *host = platform_get_drvdata(pdev);
> +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
> +	struct pltfm_mcf_data *mcf_data = sdhci_pltfm_priv(pltfm_host);
> +
> +	sdhci_remove_host(host, 0);
> +
> +	clk_disable_unprepare(mcf_data->clk_ipg);
> +	clk_disable_unprepare(mcf_data->clk_ahb);
> +	clk_disable_unprepare(mcf_data->clk_per);
> +
> +	sdhci_pltfm_free(pdev);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver sdhci_esdhc_mcf_driver = {
> +	.driver	= {
> +		.name = "sdhci-esdhc-mcf",
> +	},
> +	.probe = sdhci_esdhc_mcf_probe,
> +	.remove = sdhci_esdhc_mcf_remove,
> +};
> +
> +module_platform_driver(sdhci_esdhc_mcf_driver);
> +
> +MODULE_DESCRIPTION("SDHCI driver for Freescale ColdFire eSDHC");
> +MODULE_AUTHOR("Angelo Dureghello <angelo.dureghello@timesys.com>");
> +MODULE_LICENSE("GPL v2");
> 


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

* Re: [PATCH v4 2/3] mmc: sdhci: add quirks for be to le byte swapping
  2020-05-15 13:39   ` Adrian Hunter
@ 2020-05-15 14:16     ` Angelo Dureghello
  0 siblings, 0 replies; 6+ messages in thread
From: Angelo Dureghello @ 2020-05-15 14:16 UTC (permalink / raw)
  To: Adrian Hunter; +Cc: Ulf Hansson, Greg Ungerer, linux-mmc, linux-m68k

Hi Adrian,

thanks for the feedbacks,
providing v5 in short, will run also checkpatch with "--strict".

On Fri, May 15, 2020 at 3:38 PM Adrian Hunter <adrian.hunter@intel.com> wrote:
>
> On 13/05/20 11:41 pm, Angelo Dureghello wrote:
> > Some controller as the ColdFire eshdc may require an endianness
> > byte swap, becouse DMA read endianness is not configurable.
> >
> > Signed-off-by: Angelo Dureghello <angelo.dureghello@timesys.com>
> > ---
> > Changes for v3:
> > - add post request callback
> > Changes for v4:
> > none
> > ---
> >  drivers/mmc/host/sdhci.c | 7 +++++++
> >  drivers/mmc/host/sdhci.h | 2 ++
> >  2 files changed, 9 insertions(+)
> >
> > diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> > index 3f716466fcfd..d3bfbb6e11d5 100644
> > --- a/drivers/mmc/host/sdhci.c
> > +++ b/drivers/mmc/host/sdhci.c
> > @@ -637,6 +637,13 @@ static int sdhci_pre_dma_transfer(struct sdhci_host *host,
> >                       sg_copy_to_buffer(data->sg, data->sg_len,
> >                                         host->bounce_buffer,
> >                                         length);
> > +
> > +                     /*
> > +                      * Endianness can't be swapped in the sg list,
> > +                      * since the sg list is processed again later on.
> > +                      */
>
> The data in the sg list must not be changed because we don't know how
> else it might be being used.  Maybe leave out the comment.
>
> > +                     if (host->ops->pre_dma_transfer)
> > +                             host->ops->pre_dma_transfer(host, data);
>
> Let's make this slightly more generic i.e.
>
>                         if (host->ops->copy_to_bounce_buffer) {
>                                 host->ops->copy_to_bounce_buffer(host, data);
>                         } else {
>                                 sg_copy_to_buffer(data->sg, data->sg_len,
>                                                   host->bounce_buffer,
>                                                   length);
>                         }
>
>
> >               }
> >               /* Switch ownership to the DMA */
> >               dma_sync_single_for_device(host->mmc->parent,
> > diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h
> > index 79dffbb731d3..076054308423 100644
> > --- a/drivers/mmc/host/sdhci.h
> > +++ b/drivers/mmc/host/sdhci.h
> > @@ -653,6 +653,8 @@ struct sdhci_ops {
> >       void    (*voltage_switch)(struct sdhci_host *host);
> >       void    (*adma_write_desc)(struct sdhci_host *host, void **desc,
> >                                  dma_addr_t addr, int len, unsigned int cmd);
> > +     void    (*pre_dma_transfer)(struct sdhci_host *host,
> > +                                 struct mmc_data *data);
> >       void    (*request_done)(struct sdhci_host *host,
> >                               struct mmc_request *mrq);
> >  };
> >
>

Regards,
Angelo

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

end of thread, other threads:[~2020-05-15 14:12 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-13 20:41 [PATCH v4 1/3] m68k: mcf5441x: add support for esdhc mmc controller Angelo Dureghello
2020-05-13 20:41 ` [PATCH v4 2/3] mmc: sdhci: add quirks for be to le byte swapping Angelo Dureghello
2020-05-15 13:39   ` Adrian Hunter
2020-05-15 14:16     ` Angelo Dureghello
2020-05-13 20:41 ` [PATCH v4 3/3] mmc: host: add Coldfire esdhc support Angelo Dureghello
2020-05-15 14:02   ` Adrian Hunter

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.