* [PATCH v5 1/3] m68k: mcf5441x: add support for esdhc mmc controller
@ 2020-05-15 22:27 Angelo Dureghello
2020-05-15 22:27 ` [PATCH v5 2/3] mmc: sdhci: add quirks for be to le byte swapping Angelo Dureghello
2020-05-15 22:27 ` [PATCH v5 3/3] mmc: host: add Coldfire esdhc support Angelo Dureghello
0 siblings, 2 replies; 7+ messages in thread
From: Angelo Dureghello @ 2020-05-15 22:27 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
Changes for v5:
- fix compilation warnings from __clk_enable2 and __clk_disable2
---
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..75a057445472 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)
+{
+ __raw_writel(__raw_readl(MCFSDHC_CLK) | (1 << clk->slot), MCFSDHC_CLK);
+}
+
+static void __clk_disable2(struct clk *clk)
+{
+ __raw_writel(__raw_readl(MCFSDHC_CLK) & ~(1 << 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] 7+ messages in thread
* [PATCH v5 2/3] mmc: sdhci: add quirks for be to le byte swapping
2020-05-15 22:27 [PATCH v5 1/3] m68k: mcf5441x: add support for esdhc mmc controller Angelo Dureghello
@ 2020-05-15 22:27 ` Angelo Dureghello
2020-05-16 18:06 ` Adrian Hunter
2020-05-15 22:27 ` [PATCH v5 3/3] mmc: host: add Coldfire esdhc support Angelo Dureghello
1 sibling, 1 reply; 7+ messages in thread
From: Angelo Dureghello @ 2020-05-15 22:27 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
Changes for v5:
- remove useless comment before swapping bounce buffer
- rename pre_dma_transfer to copy_to_bounce_buffer
---
drivers/mmc/host/sdhci.c | 9 ++++++---
drivers/mmc/host/sdhci.h | 2 ++
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
index 3f716466fcfd..b7ba43e3b7c5 100644
--- a/drivers/mmc/host/sdhci.c
+++ b/drivers/mmc/host/sdhci.c
@@ -634,9 +634,12 @@ static int sdhci_pre_dma_transfer(struct sdhci_host *host,
}
if (mmc_get_dma_dir(data) == DMA_TO_DEVICE) {
/* Copy the data to the bounce buffer */
- sg_copy_to_buffer(data->sg, data->sg_len,
- host->bounce_buffer,
- length);
+ 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..632ea297bb60 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 (*copy_to_bounce_buffer)(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] 7+ messages in thread
* [PATCH v5 3/3] mmc: host: add Coldfire esdhc support
2020-05-15 22:27 [PATCH v5 1/3] m68k: mcf5441x: add support for esdhc mmc controller Angelo Dureghello
2020-05-15 22:27 ` [PATCH v5 2/3] mmc: sdhci: add quirks for be to le byte swapping Angelo Dureghello
@ 2020-05-15 22:27 ` Angelo Dureghello
2020-05-16 18:09 ` Adrian Hunter
1 sibling, 1 reply; 7+ messages in thread
From: Angelo Dureghello @ 2020-05-15 22:27 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
Changes for v5:
- better probe cleanup on errors
- test for bounce buffer to be allocated
- rename pre_dma_transfer to copy_to_bounce_buffer
- change swap to swap and copy to bounce buffer
- fix line alignments to pass checkpatch --strict
---
drivers/mmc/host/Kconfig | 13 +
drivers/mmc/host/Makefile | 1 +
drivers/mmc/host/sdhci-esdhc-mcf.c | 522 +++++++++++++++++++++++++++++
3 files changed, 536 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..445faf76e676
--- /dev/null
+++ b/drivers/mmc/host/sdhci-esdhc-mcf.c
@@ -0,0 +1,522 @@
+// 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 BIT(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_copy_to_bounce_buffer(struct sdhci_host *host,
+ struct mmc_data *data)
+{
+ unsigned int length = data->blksz * data->blocks;
+
+ sg_copy_to_buffer(data->sg, data->sg_len,
+ host->bounce_buffer, length);
+
+ 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,
+ .copy_to_bounce_buffer = esdhc_mcf_copy_to_bounce_buffer,
+ .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)
+ goto unprep_per;
+
+ err = clk_prepare_enable(mcf_data->clk_ahb);
+ if (err)
+ goto unprep_ipg;
+
+ err = esdhc_mcf_plat_init(host, mcf_data);
+ if (err)
+ goto unprep_ahb;
+
+ err = sdhci_setup_host(host);
+ if (err)
+ goto unprep_ahb;
+
+ if (!host->bounce_buffer) {
+ dev_err(&pdev->dev, "bounce buffer not allocated");
+ err = -ENOMEM;
+ goto cleanup;
+ }
+
+ err = __sdhci_add_host(host);
+ if (err)
+ goto cleanup;
+
+ 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");
--
2.26.2
^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH v5 2/3] mmc: sdhci: add quirks for be to le byte swapping
2020-05-15 22:27 ` [PATCH v5 2/3] mmc: sdhci: add quirks for be to le byte swapping Angelo Dureghello
@ 2020-05-16 18:06 ` Adrian Hunter
0 siblings, 0 replies; 7+ messages in thread
From: Adrian Hunter @ 2020-05-16 18:06 UTC (permalink / raw)
To: Angelo Dureghello, ulf.hansson, gerg; +Cc: linux-mmc, linux-m68k
On 16/05/20 1:27 am, Angelo Dureghello wrote:
> Some controller as the ColdFire eshdc may require an endianness
> byte swap, becouse DMA read endianness is not configurable.
becouse -> because
Needs a bit more explanation i.e.
Facilitate using the bounce buffer for this by adding ->copy_to_bounce_buffer().
Nevertheless:
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
>
> Signed-off-by: Angelo Dureghello <angelo.dureghello@timesys.com>
> ---
> Changes for v3:
> - add post request callback
> Changes for v4:
> none
> Changes for v5:
> - remove useless comment before swapping bounce buffer
> - rename pre_dma_transfer to copy_to_bounce_buffer
> ---
> drivers/mmc/host/sdhci.c | 9 ++++++---
> drivers/mmc/host/sdhci.h | 2 ++
> 2 files changed, 8 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c
> index 3f716466fcfd..b7ba43e3b7c5 100644
> --- a/drivers/mmc/host/sdhci.c
> +++ b/drivers/mmc/host/sdhci.c
> @@ -634,9 +634,12 @@ static int sdhci_pre_dma_transfer(struct sdhci_host *host,
> }
> if (mmc_get_dma_dir(data) == DMA_TO_DEVICE) {
> /* Copy the data to the bounce buffer */
> - sg_copy_to_buffer(data->sg, data->sg_len,
> - host->bounce_buffer,
> - length);
> + 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..632ea297bb60 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 (*copy_to_bounce_buffer)(struct sdhci_host *host,
> + struct mmc_data *data);
> void (*request_done)(struct sdhci_host *host,
> struct mmc_request *mrq);
> };
>
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v5 3/3] mmc: host: add Coldfire esdhc support
2020-05-15 22:27 ` [PATCH v5 3/3] mmc: host: add Coldfire esdhc support Angelo Dureghello
@ 2020-05-16 18:09 ` Adrian Hunter
2020-05-18 8:00 ` Angelo Dureghello
0 siblings, 1 reply; 7+ messages in thread
From: Adrian Hunter @ 2020-05-16 18:09 UTC (permalink / raw)
To: Angelo Dureghello, ulf.hansson, gerg; +Cc: linux-mmc, linux-m68k
On 16/05/20 1:27 am, 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.
>
> Signed-off-by: Angelo Dureghello <angelo.dureghello@timesys.com>
One minor comment below, nevertheless:
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
> ---
> Changes for v3:
> - fix write support
> Changes for v4:
> none
> Changes for v5:
> - better probe cleanup on errors
> - test for bounce buffer to be allocated
> - rename pre_dma_transfer to copy_to_bounce_buffer
> - change swap to swap and copy to bounce buffer
> - fix line alignments to pass checkpatch --strict
> ---
> drivers/mmc/host/Kconfig | 13 +
> drivers/mmc/host/Makefile | 1 +
> drivers/mmc/host/sdhci-esdhc-mcf.c | 522 +++++++++++++++++++++++++++++
> 3 files changed, 536 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..445faf76e676
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci-esdhc-mcf.c
> @@ -0,0 +1,522 @@
> +// 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 BIT(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_copy_to_bounce_buffer(struct sdhci_host *host,
> + struct mmc_data *data)
> +{
> + unsigned int length = data->blksz * data->blocks;
> +
> + sg_copy_to_buffer(data->sg, data->sg_len,
> + host->bounce_buffer, length);
> +
> + esdhc_mcf_buffer_swap32((u32 *)host->bounce_buffer,
> + data->blksz * data->blocks);
data->blksz * data->blocks is already calculated as 'length'
> +}
> +
> +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,
> + .copy_to_bounce_buffer = esdhc_mcf_copy_to_bounce_buffer,
> + .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)
> + goto unprep_per;
> +
> + err = clk_prepare_enable(mcf_data->clk_ahb);
> + if (err)
> + goto unprep_ipg;
> +
> + err = esdhc_mcf_plat_init(host, mcf_data);
> + if (err)
> + goto unprep_ahb;
> +
> + err = sdhci_setup_host(host);
> + if (err)
> + goto unprep_ahb;
> +
> + if (!host->bounce_buffer) {
> + dev_err(&pdev->dev, "bounce buffer not allocated");
> + err = -ENOMEM;
> + goto cleanup;
> + }
> +
> + err = __sdhci_add_host(host);
> + if (err)
> + goto cleanup;
> +
> + 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] 7+ messages in thread
* Re: [PATCH v5 3/3] mmc: host: add Coldfire esdhc support
2020-05-16 18:09 ` Adrian Hunter
@ 2020-05-18 8:00 ` Angelo Dureghello
2020-05-18 8:02 ` Ulf Hansson
0 siblings, 1 reply; 7+ messages in thread
From: Angelo Dureghello @ 2020-05-18 8:00 UTC (permalink / raw)
To: Adrian Hunter; +Cc: Ulf Hansson, Greg Ungerer, linux-mmc, linux-m68k
Hi Adrian and all,
thanks,
i can fix also these small issues in a v6 in case. Since is "acked" not sure
if i have to do it. Please let me know.
Also, checkpatch signals a missing maintainer name for this driver,
if there is any need to set a name for it ?
Regards,
Angelo
On Sat, May 16, 2020 at 8:09 PM Adrian Hunter <adrian.hunter@intel.com> wrote:
>
> On 16/05/20 1:27 am, 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.
> >
> > Signed-off-by: Angelo Dureghello <angelo.dureghello@timesys.com>
>
> One minor comment below, nevertheless:
>
> Acked-by: Adrian Hunter <adrian.hunter@intel.com>
>
> > ---
> > Changes for v3:
> > - fix write support
> > Changes for v4:
> > none
> > Changes for v5:
> > - better probe cleanup on errors
> > - test for bounce buffer to be allocated
> > - rename pre_dma_transfer to copy_to_bounce_buffer
> > - change swap to swap and copy to bounce buffer
> > - fix line alignments to pass checkpatch --strict
> > ---
> > drivers/mmc/host/Kconfig | 13 +
> > drivers/mmc/host/Makefile | 1 +
> > drivers/mmc/host/sdhci-esdhc-mcf.c | 522 +++++++++++++++++++++++++++++
> > 3 files changed, 536 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..445faf76e676
> > --- /dev/null
> > +++ b/drivers/mmc/host/sdhci-esdhc-mcf.c
> > @@ -0,0 +1,522 @@
> > +// 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 BIT(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_copy_to_bounce_buffer(struct sdhci_host *host,
> > + struct mmc_data *data)
> > +{
> > + unsigned int length = data->blksz * data->blocks;
> > +
> > + sg_copy_to_buffer(data->sg, data->sg_len,
> > + host->bounce_buffer, length);
> > +
> > + esdhc_mcf_buffer_swap32((u32 *)host->bounce_buffer,
> > + data->blksz * data->blocks);
>
> data->blksz * data->blocks is already calculated as 'length'
>
> > +}
> > +
> > +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,
> > + .copy_to_bounce_buffer = esdhc_mcf_copy_to_bounce_buffer,
> > + .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)
> > + goto unprep_per;
> > +
> > + err = clk_prepare_enable(mcf_data->clk_ahb);
> > + if (err)
> > + goto unprep_ipg;
> > +
> > + err = esdhc_mcf_plat_init(host, mcf_data);
> > + if (err)
> > + goto unprep_ahb;
> > +
> > + err = sdhci_setup_host(host);
> > + if (err)
> > + goto unprep_ahb;
> > +
> > + if (!host->bounce_buffer) {
> > + dev_err(&pdev->dev, "bounce buffer not allocated");
> > + err = -ENOMEM;
> > + goto cleanup;
> > + }
> > +
> > + err = __sdhci_add_host(host);
> > + if (err)
> > + goto cleanup;
> > +
> > + 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");
> >
>
--
Angelo Dureghello
Timesys
e. angelo.dureghello@timesys.com
^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v5 3/3] mmc: host: add Coldfire esdhc support
2020-05-18 8:00 ` Angelo Dureghello
@ 2020-05-18 8:02 ` Ulf Hansson
0 siblings, 0 replies; 7+ messages in thread
From: Ulf Hansson @ 2020-05-18 8:02 UTC (permalink / raw)
To: Angelo Dureghello; +Cc: Adrian Hunter, Greg Ungerer, linux-mmc, linux-m68k
On Mon, 18 May 2020 at 09:55, Angelo Dureghello
<angelo.dureghello@timesys.com> wrote:
>
> Hi Adrian and all,
>
> thanks,
>
> i can fix also these small issues in a v6 in case. Since is "acked" not sure
> if i have to do it. Please let me know.
Please re-spin a v6 to address Adrian's comments and add his acks.
In some cases I am amending the patches while applying, but this time
it looks like it's easier for me to get a re-spin.
>
> Also, checkpatch signals a missing maintainer name for this driver,
> if there is any need to set a name for it ?
If you are willing to look after it, please add a patch adding
yourself as maintainer!
Otherwise I think the generic SDHCI section covers it.
Kind regards
Uffe
>
> Regards,
> Angelo
>
> On Sat, May 16, 2020 at 8:09 PM Adrian Hunter <adrian.hunter@intel.com> wrote:
> >
> > On 16/05/20 1:27 am, 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.
> > >
> > > Signed-off-by: Angelo Dureghello <angelo.dureghello@timesys.com>
> >
> > One minor comment below, nevertheless:
> >
> > Acked-by: Adrian Hunter <adrian.hunter@intel.com>
> >
> > > ---
> > > Changes for v3:
> > > - fix write support
> > > Changes for v4:
> > > none
> > > Changes for v5:
> > > - better probe cleanup on errors
> > > - test for bounce buffer to be allocated
> > > - rename pre_dma_transfer to copy_to_bounce_buffer
> > > - change swap to swap and copy to bounce buffer
> > > - fix line alignments to pass checkpatch --strict
> > > ---
> > > drivers/mmc/host/Kconfig | 13 +
> > > drivers/mmc/host/Makefile | 1 +
> > > drivers/mmc/host/sdhci-esdhc-mcf.c | 522 +++++++++++++++++++++++++++++
> > > 3 files changed, 536 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..445faf76e676
> > > --- /dev/null
> > > +++ b/drivers/mmc/host/sdhci-esdhc-mcf.c
> > > @@ -0,0 +1,522 @@
> > > +// 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 BIT(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_copy_to_bounce_buffer(struct sdhci_host *host,
> > > + struct mmc_data *data)
> > > +{
> > > + unsigned int length = data->blksz * data->blocks;
> > > +
> > > + sg_copy_to_buffer(data->sg, data->sg_len,
> > > + host->bounce_buffer, length);
> > > +
> > > + esdhc_mcf_buffer_swap32((u32 *)host->bounce_buffer,
> > > + data->blksz * data->blocks);
> >
> > data->blksz * data->blocks is already calculated as 'length'
> >
> > > +}
> > > +
> > > +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,
> > > + .copy_to_bounce_buffer = esdhc_mcf_copy_to_bounce_buffer,
> > > + .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)
> > > + goto unprep_per;
> > > +
> > > + err = clk_prepare_enable(mcf_data->clk_ahb);
> > > + if (err)
> > > + goto unprep_ipg;
> > > +
> > > + err = esdhc_mcf_plat_init(host, mcf_data);
> > > + if (err)
> > > + goto unprep_ahb;
> > > +
> > > + err = sdhci_setup_host(host);
> > > + if (err)
> > > + goto unprep_ahb;
> > > +
> > > + if (!host->bounce_buffer) {
> > > + dev_err(&pdev->dev, "bounce buffer not allocated");
> > > + err = -ENOMEM;
> > > + goto cleanup;
> > > + }
> > > +
> > > + err = __sdhci_add_host(host);
> > > + if (err)
> > > + goto cleanup;
> > > +
> > > + 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");
> > >
> >
>
>
> --
> Angelo Dureghello
> Timesys
> e. angelo.dureghello@timesys.com
^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2020-05-18 8:03 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-05-15 22:27 [PATCH v5 1/3] m68k: mcf5441x: add support for esdhc mmc controller Angelo Dureghello
2020-05-15 22:27 ` [PATCH v5 2/3] mmc: sdhci: add quirks for be to le byte swapping Angelo Dureghello
2020-05-16 18:06 ` Adrian Hunter
2020-05-15 22:27 ` [PATCH v5 3/3] mmc: host: add Coldfire esdhc support Angelo Dureghello
2020-05-16 18:09 ` Adrian Hunter
2020-05-18 8:00 ` Angelo Dureghello
2020-05-18 8:02 ` Ulf Hansson
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).