* [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.