summaryrefslogtreecommitdiffstats
path: root/drivers/mci
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/mci')
-rw-r--r--drivers/mci/Kconfig98
-rw-r--r--drivers/mci/Makefile10
-rw-r--r--drivers/mci/am654-sdhci.c680
-rw-r--r--drivers/mci/arasan-sdhci.c756
-rw-r--r--drivers/mci/atmel-mci-regs.h50
-rw-r--r--drivers/mci/atmel-sdhci-common.c345
-rw-r--r--drivers/mci/atmel-sdhci-pbl.c128
-rw-r--r--drivers/mci/atmel-sdhci.c172
-rw-r--r--drivers/mci/atmel-sdhci.h29
-rw-r--r--drivers/mci/atmel_mci.c530
-rw-r--r--drivers/mci/atmel_mci_common.c475
-rw-r--r--drivers/mci/atmel_mci_pbl.c122
-rw-r--r--drivers/mci/bcm2835-sdhost.c19
-rw-r--r--drivers/mci/dove-sdhci.c160
-rw-r--r--drivers/mci/dw_mmc.c128
-rw-r--r--drivers/mci/dw_mmc.h18
-rw-r--r--drivers/mci/dwcmshc-sdhci.c402
-rw-r--r--drivers/mci/imx-esdhc-common.c116
-rw-r--r--drivers/mci/imx-esdhc-pbl.c399
-rw-r--r--drivers/mci/imx-esdhc.c175
-rw-r--r--drivers/mci/imx-esdhc.h61
-rw-r--r--drivers/mci/imx.c31
-rw-r--r--drivers/mci/mci-bcm2835.c84
-rw-r--r--drivers/mci/mci-bcm2835.h2
-rw-r--r--drivers/mci/mci-core.c964
-rw-r--r--drivers/mci/mci_spi.c77
-rw-r--r--drivers/mci/mmci.c31
-rw-r--r--drivers/mci/mmci.h13
-rw-r--r--drivers/mci/mxs.c45
-rw-r--r--drivers/mci/omap_hsmmc.c51
-rw-r--r--drivers/mci/pxamci.c28
-rw-r--r--drivers/mci/pxamci.h12
-rw-r--r--drivers/mci/rockchip-dwcmshc-sdhci.c361
-rw-r--r--drivers/mci/s3c.c774
-rw-r--r--drivers/mci/sdhci.c863
-rw-r--r--drivers/mci/sdhci.h254
-rw-r--r--drivers/mci/stm32_sdmmc2.c103
-rw-r--r--drivers/mci/tegra-sdmmc.c59
38 files changed, 6096 insertions, 2529 deletions
diff --git a/drivers/mci/Kconfig b/drivers/mci/Kconfig
index 80b3a26002..1e8c85643b 100644
--- a/drivers/mci/Kconfig
+++ b/drivers/mci/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
config MCI_SDHCI
bool
@@ -11,13 +12,20 @@ if MCI
comment "--- Feature list ---"
+config MCI_TUNING
+ bool "EXPERIMENTAL - support MMC tuning for higher speeds"
+ help
+ Say 'y' here if supporting drivers should do tuning to support
+ higher clock speeds than 52 MHz SDR. MMC only; SD-Card max
+ frequency is 50MHz SDR at present.
+
config MCI_STARTUP
- bool "Probe on system start"
+ bool "Force probe on system start"
help
- Say 'y' here if the MCI framework should probe for attached MCI cards
- on system start up. This is required if the card carries barebox's
- environment (for example on systems where the MCI card is the sole
- bootmedia). Otherwise probing run on demand with "mci*.probe=1"
+ Say 'y' here if the MCI framework should always probe for all attached
+ MCI cards on system start up. This may required for some legacy boards.
+ When this is 'n', probing happens on demand either with "mci*.probe=1"
+ or with driver/board code calling device_detect.
config MCI_INFO
bool "MCI Info"
@@ -34,9 +42,37 @@ config MCI_WRITE
config MCI_MMC_BOOT_PARTITIONS
bool "support MMC boot partitions"
+ help
+ Provide access to the 'boot partitions' of devices of type 'MMC'.
+ These so called 'hardware partitions' act like an independent memory
+ device and thus, need special handling.
+
+ Note: only 'MMC' have 'boot partitions'. So, if you don't use an
+ 'MMC' device, you don't need this support.
+
+config MCI_MMC_GPP_PARTITIONS
+ bool "support MMC general purpose partitions (GPP)"
+ help
+ Provide access to the 'general purpose partitions' of devices of type
+ 'MMC'. These so called 'hardware partitions' act like an independent
+ memory device and thus, need special handling.
+
+ Note: only 'MMC' devices have 'general purpose partitions'. So, if
+ you don't use an 'MMC' device, you don't need this support.
+
+ Note: by default, 'MMC' devices have no 'general purpose partitions',
+ it requires a special one-time configuration step to enable them.
comment "--- MCI host drivers ---"
+config MCI_DWC_MSHC
+ bool "Synopsys DesignWare Cores MSHC"
+ depends on HAS_DMA
+ select MCI_SDHCI
+ help
+ This selects support for the Synopsys DesignWare Mobile Storage Host Controller
+ block (DWC_mshc), this provides host support for SD/eMMC interfaces, in SDMA mode.
+
config MCI_DW
bool "Synopsys DesignWare Memory Card Interface"
depends on HAS_DMA
@@ -54,29 +90,31 @@ config MCI_DW_PIO
config MCI_MXS
bool "i.MX23/i.MX28"
depends on ARCH_MXS
+ select STMP_DEVICE
help
Enable this entry to add support to read and write SD cards on a
i.MX23/i.MX28 based system.
-config MCI_S3C
- bool "S3C"
- depends on ARCH_S3C24xx
+config MCI_ROCKCHIP_DWCMSHC
+ bool "MCI sdhc support for Rockchip SoCs"
+ select MCI_SDHCI
help
- Enable this entry to add support to read and write SD cards on a
- Samsung S3C24xx based system.
+ Enable this entry to add support for a Rockchip derivation of the
+ DWCMSHC controller found on some Rockchip SoCs like the RK3568.
config MCI_BCM283X
bool "MCI support for BCM283X"
- depends on ARCH_BCM283X
+ depends on ARCH_BCM283X || COMPILE_TEST
+ select MCI_SDHCI
config MCI_BCM283X_SDHOST
bool "BCM283X sdhost"
- depends on ARCH_BCM283X
+ depends on ARCH_BCM283X || COMPILE_TEST
select MCI_SDHCI
config MCI_DOVE
bool "Marvell Dove SDHCI"
- depends on ARCH_DOVE
+ depends on ARCH_DOVE || COMPILE_TEST
select MCI_SDHCI
help
Enable this entry to add support to read and write SD cards on a
@@ -84,14 +122,14 @@ config MCI_DOVE
config MCI_IMX
bool "i.MX"
- depends on ARCH_IMX27 || ARCH_IMX31
+ depends on ARCH_IMX27 || ARCH_IMX31 || COMPILE_TEST
help
Enable this entry to add support to read and write SD cards on a
Freescale i.MX based system.
config MCI_IMX_ESDHC
bool "i.MX esdhc"
- depends on ARCH_IMX || ARCH_LAYERSCAPE
+ depends on ARCH_IMX || ARCH_LAYERSCAPE || COMPILE_TEST
select MCI_SDHCI
help
Enable this entry to add support to read and write SD cards on a
@@ -119,11 +157,19 @@ config MCI_PXA
config MCI_ATMEL
bool "ATMEL (AT91)"
- depends on ARCH_AT91
+ depends on ARCH_AT91 || COMPILE_TEST
help
Enable this entry to add support to read and write SD cards on a
Atmel AT91.
+config MCI_ATMEL_SDHCI
+ bool "ATMEL SDHCI (sama5d2)"
+ select MCI_SDHCI
+ depends on ARCH_AT91
+ help
+ Enable this entry to add support to read and write SD cards on an
+ Atmel sama5d2
+
config MCI_MMCI
bool "ARM PL180 MMCI"
depends on ARM_AMBA
@@ -133,7 +179,7 @@ config MCI_MMCI
config MCI_TEGRA
bool "Tegra SD/MMC"
- depends on ARCH_TEGRA
+ depends on ARCH_TEGRA || COMPILE_TEST
select MCI_SDHCI
help
Enable this to support SD and MMC card read/write on a Tegra based
@@ -141,10 +187,19 @@ config MCI_TEGRA
config MCI_ARASAN
bool "Arasan SDHCI Controller"
+ select MCI_SDHCI
help
Enable this to support SD and MMC card read/write on systems with
the Arasan SD3.0 / SDIO3.0 / eMMC4.51 host controller.
+config MCI_AM654
+ bool "Support for the SDHCI Controller in TI's AM654 SOCs"
+ select MCI_SDHCI
+ help
+ This selects the Secure Digital Host Controller Interface (SDHCI)
+ support present in TI's AM654 SOCs. The controller supports
+ SD/MMC/SDIO devices.
+
config MCI_SPI
bool "MMC/SD over SPI"
select CRC7
@@ -167,6 +222,7 @@ config MCI_STM32_SDMMC2
bool "STMicroelectronics STM32H7 SD/MMC Host Controller support"
depends on ARM_AMBA
depends on RESET_CONTROLLER
+ depends on ARCH_STM32 || COMPILE_TEST
help
This selects support for the SD/MMC controller on STM32H7 SoCs.
If you have a board based on such a SoC and with a SD/MMC slot,
@@ -177,3 +233,11 @@ endif
config MCI_IMX_ESDHC_PBL
bool
select MCI_SDHCI
+
+config MCI_ATMEL_PBL
+ bool
+ select MCI_ATMEL
+
+config MCI_ATMEL_SDHCI_PBL
+ bool
+ select MCI_SDHCI
diff --git a/drivers/mci/Makefile b/drivers/mci/Makefile
index 54eb65978e..d8d7818a48 100644
--- a/drivers/mci/Makefile
+++ b/drivers/mci/Makefile
@@ -1,19 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0-only
obj-$(CONFIG_MCI) += mci-core.o
+obj-$(CONFIG_MCI_AM654) += am654-sdhci.o
obj-$(CONFIG_MCI_ARASAN) += arasan-sdhci.o
-obj-$(CONFIG_MCI_ATMEL) += atmel_mci.o
+obj-$(CONFIG_MCI_ATMEL) += atmel_mci.o atmel_mci_common.o
+obj-$(CONFIG_MCI_ATMEL_SDHCI) += atmel-sdhci.o atmel-sdhci-common.o
obj-$(CONFIG_MCI_BCM283X) += mci-bcm2835.o
obj-$(CONFIG_MCI_BCM283X_SDHOST) += bcm2835-sdhost.o
obj-$(CONFIG_MCI_DOVE) += dove-sdhci.o
+pbl-$(CONFIG_MCI_ATMEL_PBL) += atmel_mci_pbl.o atmel_mci_common.o
+pbl-$(CONFIG_MCI_ATMEL_SDHCI_PBL) += atmel-sdhci-pbl.o atmel-sdhci-common.o
obj-$(CONFIG_MCI_IMX) += imx.o
obj-$(CONFIG_MCI_IMX_ESDHC) += imx-esdhc.o imx-esdhc-common.o
pbl-$(CONFIG_MCI_IMX_ESDHC_PBL) += imx-esdhc-pbl.o imx-esdhc-common.o
obj-$(CONFIG_MCI_MXS) += mxs.o
obj-$(CONFIG_MCI_OMAP_HSMMC) += omap_hsmmc.o
obj-$(CONFIG_MCI_PXA) += pxamci.o
-obj-$(CONFIG_MCI_S3C) += s3c.o
+obj-$(CONFIG_MCI_ROCKCHIP_DWCMSHC) += rockchip-dwcmshc-sdhci.o
obj-$(CONFIG_MCI_TEGRA) += tegra-sdmmc.o
obj-$(CONFIG_MCI_SPI) += mci_spi.o
obj-$(CONFIG_MCI_DW) += dw_mmc.o
+obj-$(CONFIG_MCI_DWC_MSHC) += dwcmshc-sdhci.o
obj-$(CONFIG_MCI_MMCI) += mmci.o
obj-$(CONFIG_MCI_STM32_SDMMC2) += stm32_sdmmc2.o
obj-pbl-$(CONFIG_MCI_SDHCI) += sdhci.o
diff --git a/drivers/mci/am654-sdhci.c b/drivers/mci/am654-sdhci.c
new file mode 100644
index 0000000000..391b65591c
--- /dev/null
+++ b/drivers/mci/am654-sdhci.c
@@ -0,0 +1,680 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ *
+ * Texas Instruments' K3 SD Host Controller Interface
+ */
+#include <common.h>
+#include <init.h>
+#include <driver.h>
+#include <mci.h>
+#include <clock.h>
+#include <errno.h>
+#include <io.h>
+#include <regmap.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include "sdhci.h"
+
+/* CTL_CFG Registers */
+#define CTL_CFG_2 0x14
+
+#define SLOTTYPE_MASK GENMASK(31, 30)
+#define SLOTTYPE_EMBEDDED BIT(30)
+
+/* PHY Registers */
+#define PHY_CTRL1 0x100
+#define PHY_CTRL2 0x104
+#define PHY_CTRL3 0x108
+#define PHY_CTRL4 0x10C
+#define PHY_CTRL5 0x110
+#define PHY_CTRL6 0x114
+#define PHY_STAT1 0x130
+#define PHY_STAT2 0x134
+
+#define IOMUX_ENABLE_SHIFT 31
+#define IOMUX_ENABLE_MASK BIT(IOMUX_ENABLE_SHIFT)
+#define OTAPDLYENA_SHIFT 20
+#define OTAPDLYENA_MASK BIT(OTAPDLYENA_SHIFT)
+#define OTAPDLYSEL_SHIFT 12
+#define OTAPDLYSEL_MASK GENMASK(15, 12)
+#define STRBSEL_SHIFT 24
+#define STRBSEL_4BIT_MASK GENMASK(27, 24)
+#define STRBSEL_8BIT_MASK GENMASK(31, 24)
+#define SEL50_SHIFT 8
+#define SEL50_MASK BIT(SEL50_SHIFT)
+#define SEL100_SHIFT 9
+#define SEL100_MASK BIT(SEL100_SHIFT)
+#define FREQSEL_SHIFT 8
+#define FREQSEL_MASK GENMASK(10, 8)
+#define CLKBUFSEL_SHIFT 0
+#define CLKBUFSEL_MASK GENMASK(2, 0)
+#define DLL_TRIM_ICP_SHIFT 4
+#define DLL_TRIM_ICP_MASK GENMASK(7, 4)
+#define DR_TY_SHIFT 20
+#define DR_TY_MASK GENMASK(22, 20)
+#define ENDLL_SHIFT 1
+#define ENDLL_MASK BIT(ENDLL_SHIFT)
+#define DLLRDY_SHIFT 0
+#define DLLRDY_MASK BIT(DLLRDY_SHIFT)
+#define PDB_SHIFT 0
+#define PDB_MASK BIT(PDB_SHIFT)
+#define CALDONE_SHIFT 1
+#define CALDONE_MASK BIT(CALDONE_SHIFT)
+#define RETRIM_SHIFT 17
+#define RETRIM_MASK BIT(RETRIM_SHIFT)
+#define SELDLYTXCLK_SHIFT 17
+#define SELDLYTXCLK_MASK BIT(SELDLYTXCLK_SHIFT)
+#define SELDLYRXCLK_SHIFT 16
+#define SELDLYRXCLK_MASK BIT(SELDLYRXCLK_SHIFT)
+#define ITAPDLYSEL_SHIFT 0
+#define ITAPDLYSEL_MASK GENMASK(4, 0)
+#define ITAPDLYENA_SHIFT 8
+#define ITAPDLYENA_MASK BIT(ITAPDLYENA_SHIFT)
+#define ITAPCHGWIN_SHIFT 9
+#define ITAPCHGWIN_MASK BIT(ITAPCHGWIN_SHIFT)
+
+#define DRIVER_STRENGTH_50_OHM 0x0
+#define DRIVER_STRENGTH_33_OHM 0x1
+#define DRIVER_STRENGTH_66_OHM 0x2
+#define DRIVER_STRENGTH_100_OHM 0x3
+#define DRIVER_STRENGTH_40_OHM 0x4
+
+#define AM654_SDHCI_MIN_FREQ 400000
+#define CLOCK_TOO_SLOW_HZ 50000000
+
+#define MMC_CAP2_HS200 0
+#define MMC_CAP2_HS400 0
+#define MMC_CAP_UHS_SDR104 0
+#define MMC_CAP_UHS_SDR12 0
+#define MMC_CAP_UHS_DDR50 0
+#define MMC_CAP_UHS_SDR25 0
+#define MMC_CAP_UHS_SDR50 0
+
+struct timing_data {
+ const char *otap_binding;
+ const char *itap_binding;
+ u32 capability;
+};
+
+static const struct timing_data td[] = {
+ [MMC_TIMING_LEGACY] = {
+ "ti,otap-del-sel-legacy",
+ "ti,itap-del-sel-legacy",
+ 0
+ },
+ [MMC_TIMING_MMC_HS] = {
+ "ti,otap-del-sel-mmc-hs",
+ "ti,itap-del-sel-mms-hs",
+ MMC_CAP_MMC_HIGHSPEED
+ },
+ [MMC_TIMING_SD_HS] = {
+ "ti,otap-del-sel-sd-hs",
+ "ti,itap-del-sel-sd-hs",
+ MMC_CAP_SD_HIGHSPEED
+ },
+ [MMC_TIMING_UHS_SDR12] = {
+ "ti,otap-del-sel-sdr12",
+ "ti,itap-del-sel-sdr12",
+ MMC_CAP_UHS_SDR12
+ },
+ [MMC_TIMING_UHS_SDR25] = {
+ "ti,otap-del-sel-sdr25",
+ "ti,itap-del-sel-sdr25",
+ MMC_CAP_UHS_SDR25
+ },
+ [MMC_TIMING_UHS_SDR50] = {
+ "ti,otap-del-sel-sdr50",
+ NULL,
+ MMC_CAP_UHS_SDR50
+ },
+ [MMC_TIMING_UHS_SDR104] = {
+ "ti,otap-del-sel-sdr104",
+ NULL,
+ MMC_CAP_UHS_SDR104
+ },
+ [MMC_TIMING_UHS_DDR50] = {
+ "ti,otap-del-sel-ddr50",
+ NULL,
+ MMC_CAP_UHS_DDR50
+ },
+ [MMC_TIMING_MMC_DDR52] = {
+ "ti,otap-del-sel-ddr52",
+ "ti,itap-del-sel-ddr52",
+ MMC_CAP_DDR
+ },
+ [MMC_TIMING_MMC_HS200] = {
+ "ti,otap-del-sel-hs200",
+ NULL,
+ MMC_CAP2_HS200
+ },
+ [MMC_TIMING_MMC_HS400] = {
+ "ti,otap-del-sel-hs400",
+ NULL,
+ MMC_CAP2_HS400
+ },
+};
+
+struct am654_sdhci_plat {
+ struct sdhci sdhci;
+ struct mci_host mci;
+ struct clk *clk;
+ struct clk *clk_ahb;
+ const struct am654_driver_data *soc_data;
+ bool fails_without_test_cd;
+
+ struct regmap *base;
+ bool non_removable;
+ u32 otap_del_sel[ARRAY_SIZE(td)];
+ u32 itap_del_sel[ARRAY_SIZE(td)];
+ u32 trm_icp;
+ u32 drv_strength;
+ u32 strb_sel;
+ u32 clkbuf_sel;
+#define DLL_PRESENT BIT(0)
+#define IOMUX_PRESENT BIT(1)
+#define FREQSEL_2_BIT BIT(2)
+#define STRBSEL_4_BIT BIT(3)
+#define DLL_CALIB BIT(4)
+};
+
+struct am654_driver_data {
+ int (*set_ios_post)(struct am654_sdhci_plat *plat, struct mci_ios *ios);
+ u32 flags;
+};
+
+static int am654_sdhci_setup_dll(struct am654_sdhci_plat *plat,
+ unsigned int speed)
+{
+ int sel50, sel100, freqsel;
+ u32 mask, val;
+ int ret;
+
+ /* Disable delay chain mode */
+ regmap_update_bits(plat->base, PHY_CTRL5,
+ SELDLYTXCLK_MASK | SELDLYRXCLK_MASK, 0);
+
+ if (plat->soc_data->flags & FREQSEL_2_BIT) {
+ switch (speed) {
+ case 200000000:
+ sel50 = 0;
+ sel100 = 0;
+ break;
+ case 100000000:
+ sel50 = 0;
+ sel100 = 1;
+ break;
+ default:
+ sel50 = 1;
+ sel100 = 0;
+ }
+
+ /* Configure PHY DLL frequency */
+ mask = SEL50_MASK | SEL100_MASK;
+ val = (sel50 << SEL50_SHIFT) | (sel100 << SEL100_SHIFT);
+ regmap_update_bits(plat->base, PHY_CTRL5, mask, val);
+ } else {
+ switch (speed) {
+ case 200000000:
+ freqsel = 0x0;
+ break;
+ default:
+ freqsel = 0x4;
+ }
+ regmap_update_bits(plat->base, PHY_CTRL5, FREQSEL_MASK,
+ freqsel << FREQSEL_SHIFT);
+ }
+
+ /* Configure DLL TRIM */
+ mask = DLL_TRIM_ICP_MASK;
+ val = plat->trm_icp << DLL_TRIM_ICP_SHIFT;
+
+ /* Configure DLL driver strength */
+ mask |= DR_TY_MASK;
+ val |= plat->drv_strength << DR_TY_SHIFT;
+ regmap_update_bits(plat->base, PHY_CTRL1, mask, val);
+
+ /* Enable DLL */
+ regmap_update_bits(plat->base, PHY_CTRL1, ENDLL_MASK,
+ 0x1 << ENDLL_SHIFT);
+ /*
+ * Poll for DLL ready. Use a one second timeout.
+ * Works in all experiments done so far
+ */
+ ret = regmap_read_poll_timeout(plat->base, PHY_STAT1, val,
+ val & DLLRDY_MASK, 1000000);
+
+ return ret;
+}
+
+static void am654_sdhci_write_itapdly(struct am654_sdhci_plat *plat,
+ u32 itapdly)
+{
+ /* Set ITAPCHGWIN before writing to ITAPDLY */
+ regmap_update_bits(plat->base, PHY_CTRL4, ITAPCHGWIN_MASK,
+ 1 << ITAPCHGWIN_SHIFT);
+ regmap_update_bits(plat->base, PHY_CTRL4, ITAPDLYSEL_MASK,
+ itapdly << ITAPDLYSEL_SHIFT);
+ regmap_update_bits(plat->base, PHY_CTRL4, ITAPCHGWIN_MASK, 0);
+}
+
+static void am654_sdhci_setup_delay_chain(struct am654_sdhci_plat *plat,
+ int mode)
+{
+ u32 mask, val;
+
+ val = 1 << SELDLYTXCLK_SHIFT | 1 << SELDLYRXCLK_SHIFT;
+ mask = SELDLYTXCLK_MASK | SELDLYRXCLK_MASK;
+ regmap_update_bits(plat->base, PHY_CTRL5, mask, val);
+
+ am654_sdhci_write_itapdly(plat, plat->itap_del_sel[mode]);
+}
+
+static int am654_sdhci_set_ios_post(struct am654_sdhci_plat *plat, struct mci_ios *ios)
+{
+ unsigned int speed = ios->clock;
+ int mode = ios->timing;
+ u32 otap_del_sel;
+ u32 mask, val;
+ int ret;
+
+ /* Reset SD Clock Enable */
+ val = sdhci_read16(&plat->sdhci, SDHCI_CLOCK_CONTROL);
+ val &= ~SDHCI_CLOCK_CARD_EN;
+ sdhci_write16(&plat->sdhci, SDHCI_CLOCK_CONTROL, val);
+
+ regmap_update_bits(plat->base, PHY_CTRL1, ENDLL_MASK, 0);
+
+ /* restart clock */
+ sdhci_set_clock(&plat->sdhci, speed, clk_get_rate(plat->clk));
+
+ /* switch phy back on */
+ otap_del_sel = plat->otap_del_sel[mode];
+ mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
+ val = (1 << OTAPDLYENA_SHIFT) |
+ (otap_del_sel << OTAPDLYSEL_SHIFT);
+
+ /* Write to STRBSEL for HS400 speed mode */
+ if (mode == MMC_TIMING_MMC_HS400) {
+ if (plat->soc_data->flags & STRBSEL_4_BIT)
+ mask |= STRBSEL_4BIT_MASK;
+ else
+ mask |= STRBSEL_8BIT_MASK;
+
+ val |= plat->strb_sel << STRBSEL_SHIFT;
+ }
+
+ regmap_update_bits(plat->base, PHY_CTRL4, mask, val);
+
+ if (mode > MMC_TIMING_UHS_SDR25 && speed >= CLOCK_TOO_SLOW_HZ) {
+ ret = am654_sdhci_setup_dll(plat, speed);
+ if (ret)
+ return ret;
+ } else {
+ am654_sdhci_setup_delay_chain(plat, mode);
+ }
+
+ regmap_update_bits(plat->base, PHY_CTRL5, CLKBUFSEL_MASK,
+ plat->clkbuf_sel);
+
+ return 0;
+}
+
+static int am654_sdhci_init(struct mci_host *mci, struct device *dev)
+{
+ struct am654_sdhci_plat *plat = container_of(mci, struct am654_sdhci_plat, mci);
+ u32 ctl_cfg_2 = 0;
+ u32 mask, val;
+ int ret;
+
+ ret = sdhci_reset(&plat->sdhci, SDHCI_RESET_ALL);
+ if (ret)
+ return ret;
+
+ if (plat->fails_without_test_cd) {
+ val = sdhci_read8(&plat->sdhci, SDHCI_HOST_CONTROL);
+ val |= SDHCI_CTRL_CDTEST_INS | SDHCI_CTRL_CDTEST_EN;
+ sdhci_write8(&plat->sdhci, SDHCI_HOST_CONTROL, val);
+ }
+
+ sdhci_write8(&plat->sdhci, SDHCI_POWER_CONTROL,
+ SDHCI_BUS_VOLTAGE_330 | SDHCI_BUS_POWER_EN);
+ udelay(400);
+
+ sdhci_write32(&plat->sdhci, SDHCI_INT_ENABLE, ~0);
+ sdhci_write32(&plat->sdhci, SDHCI_SIGNAL_ENABLE, 0x00);
+
+ /* Reset OTAP to default value */
+ mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
+ regmap_update_bits(plat->base, PHY_CTRL4, mask, 0x0);
+
+ if (plat->soc_data->flags & DLL_CALIB) {
+ regmap_read(plat->base, PHY_STAT1, &val);
+ if (~val & CALDONE_MASK) {
+ /* Calibrate IO lines */
+ regmap_update_bits(plat->base, PHY_CTRL1, PDB_MASK,
+ PDB_MASK);
+ ret = regmap_read_poll_timeout(plat->base, PHY_STAT1,
+ val, val & CALDONE_MASK,
+ 20);
+ if (ret)
+ return ret;
+ }
+ }
+
+ /* Enable pins by setting IO mux to 0 */
+ if (plat->soc_data->flags & IOMUX_PRESENT)
+ regmap_update_bits(plat->base, PHY_CTRL1, IOMUX_ENABLE_MASK, 0);
+
+ /* Set slot type based on SD or eMMC */
+ if (plat->non_removable)
+ ctl_cfg_2 = SLOTTYPE_EMBEDDED;
+
+ regmap_update_bits(plat->base, CTL_CFG_2, SLOTTYPE_MASK, ctl_cfg_2);
+
+ return 0;
+}
+
+const struct am654_driver_data am654_drv_data = {
+ .flags = DLL_PRESENT | IOMUX_PRESENT | FREQSEL_2_BIT | STRBSEL_4_BIT,
+};
+
+const struct am654_driver_data am654_sr1_drv_data = {
+ .flags = IOMUX_PRESENT | FREQSEL_2_BIT | DLL_PRESENT | DLL_CALIB |
+ STRBSEL_4_BIT,
+};
+
+const struct am654_driver_data j721e_8bit_drv_data = {
+ .flags = DLL_PRESENT | DLL_CALIB,
+};
+
+static int j721e_4bit_sdhci_set_ios_post(struct am654_sdhci_plat *plat, struct mci_ios *ios)
+{
+ u32 otap_del_sel, mask, val;
+
+ otap_del_sel = plat->otap_del_sel[ios->timing];
+ mask = OTAPDLYENA_MASK | OTAPDLYSEL_MASK;
+ val = (1 << OTAPDLYENA_SHIFT) | (otap_del_sel << OTAPDLYSEL_SHIFT);
+ regmap_update_bits(plat->base, PHY_CTRL4, mask, val);
+
+ regmap_update_bits(plat->base, PHY_CTRL5, CLKBUFSEL_MASK,
+ plat->clkbuf_sel);
+
+ return 0;
+}
+
+const struct am654_driver_data j721e_4bit_drv_data = {
+ .set_ios_post = &j721e_4bit_sdhci_set_ios_post,
+ .flags = IOMUX_PRESENT,
+};
+
+static const struct am654_driver_data sdhci_am64_8bit_drvdata = {
+ .set_ios_post = &am654_sdhci_set_ios_post,
+ .flags = DLL_PRESENT | DLL_CALIB,
+};
+
+static const struct am654_driver_data sdhci_am64_4bit_drvdata = {
+ .set_ios_post = &j721e_4bit_sdhci_set_ios_post,
+ .flags = IOMUX_PRESENT,
+};
+
+static int sdhci_am654_get_otap_delay(struct am654_sdhci_plat *plat)
+{
+ struct device *dev = plat->mci.hw_dev;
+ struct device_node *np = dev->of_node;
+ int ret;
+ int i;
+
+ /* ti,otap-del-sel-legacy is mandatory */
+ ret = of_property_read_u32(np, "ti,otap-del-sel-legacy",
+ &plat->otap_del_sel[0]);
+ if (ret)
+ return ret;
+ /*
+ * Remove the corresponding capability if an otap-del-sel
+ * value is not found
+ */
+ for (i = MMC_TIMING_MMC_HS; i <= MMC_TIMING_MMC_HS400; i++) {
+ ret = of_property_read_u32(np, td[i].otap_binding,
+ &plat->otap_del_sel[i]);
+ if (ret) {
+ dev_dbg(dev, "Couldn't find %s\n", td[i].otap_binding);
+ /*
+ * Remove the corresponding capability
+ * if an otap-del-sel value is not found
+ */
+ plat->mci.host_caps &= ~td[i].capability;
+ }
+
+ if (td[i].itap_binding)
+ of_property_read_u32(np, td[i].itap_binding,
+ &plat->itap_del_sel[i]);
+ }
+
+ return 0;
+}
+
+static void print_error(struct am654_sdhci_plat *host, int cmdidx)
+{
+ dev_dbg(host->mci.hw_dev,
+ "error while transfering data for command %d\n", cmdidx);
+ dev_dbg(host->mci.hw_dev, "state = 0x%08x , interrupt = 0x%08x\n",
+ sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE),
+ sdhci_read32(&host->sdhci, SDHCI_INT_NORMAL_STATUS));
+}
+
+static int am654_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
+ struct mci_data *data)
+{
+ struct am654_sdhci_plat *host = container_of(mci, struct am654_sdhci_plat, mci);
+ u32 command, xfer;
+ int ret;
+ dma_addr_t dma;
+
+ ret = sdhci_wait_idle_data(&host->sdhci, cmd);
+ if (ret)
+ return ret;
+
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
+
+ sdhci_write8(&host->sdhci, SDHCI_TIMEOUT_CONTROL, 0xe);
+
+ sdhci_setup_data_dma(&host->sdhci, data, &dma);
+
+ sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data,
+ dma == SDHCI_NO_DMA ? false : true,
+ &command, &xfer);
+
+ sdhci_write16(&host->sdhci, SDHCI_TRANSFER_MODE, xfer);
+ sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, cmd->cmdarg);
+ sdhci_write16(&host->sdhci, SDHCI_COMMAND, command);
+
+ ret = sdhci_wait_for_done(&host->sdhci, SDHCI_INT_CMD_COMPLETE);
+ if (ret)
+ goto error;
+
+ sdhci_read_response(&host->sdhci, cmd);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE);
+
+ ret = sdhci_transfer_data_dma(&host->sdhci, data, dma);
+
+error:
+ if (ret) {
+ print_error(host, cmd->cmdidx);
+ sdhci_reset(&host->sdhci, SDHCI_RESET_CMD);
+ sdhci_reset(&host->sdhci, SDHCI_RESET_DATA);
+ }
+
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
+
+ return ret;
+}
+
+static void am654_sdhci_set_ios(struct mci_host *mci, struct mci_ios *ios)
+{
+ struct am654_sdhci_plat *plat = container_of(mci, struct am654_sdhci_plat, mci);
+ u32 val;
+
+ if (ios->clock)
+ sdhci_set_clock(&plat->sdhci, ios->clock, plat->sdhci.max_clk);
+
+ sdhci_set_bus_width(&plat->sdhci, ios->bus_width);
+
+ val = sdhci_read8(&plat->sdhci, SDHCI_HOST_CONTROL);
+
+ if (ios->clock > 26000000)
+ val |= SDHCI_CTRL_HISPD;
+ else
+ val &= ~SDHCI_CTRL_HISPD;
+
+ sdhci_write8(&plat->sdhci, SDHCI_HOST_CONTROL, val);
+
+ plat->soc_data->set_ios_post(plat, ios);
+}
+
+static const struct regmap_config regmap_config = {
+ .reg_bits = 32,
+ .val_bits = 32,
+ .reg_stride = 4,
+ .max_register = 0x400,
+};
+
+static int am654_sdhci_probe(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct am654_sdhci_plat *plat;
+ struct mci_host *mci;
+ struct resource *iores;
+ u32 drv_strength;
+ int ret;
+
+ plat = xzalloc(sizeof(*plat));
+
+ ret = dev_get_drvdata(dev, (const void **)&plat->soc_data);
+ if (ret)
+ return ret;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ plat->sdhci.base = IOMEM(iores->start);
+
+ iores = dev_request_mem_resource(dev, 1);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ plat->base = regmap_init_mmio(dev, IOMEM(iores->start), &regmap_config);
+ if (IS_ERR(plat->base)) {
+ dev_err(dev, "failed to init regmap: %ld\n",
+ PTR_ERR(plat->base));
+ return PTR_ERR(plat->base);
+ }
+
+ plat->clk = clk_get(dev, "clk_xin");
+ if (IS_ERR(plat->clk)) {
+ dev_err(dev, "failed to get clock\n");
+ return ret;
+ }
+
+ clk_enable(plat->clk);
+
+ plat->clk_ahb = clk_get(dev, "clk_ahb");
+ if (IS_ERR(plat->clk_ahb)) {
+ dev_err(dev, "failed to get ahb clock\n");
+ return ret;
+ }
+
+ clk_enable(plat->clk_ahb);
+
+ mci = &plat->mci;
+ mci->f_max = clk_get_rate(plat->clk);
+ mci->f_min = 50000000 / 256;
+
+ if (plat->soc_data->flags & DLL_PRESENT) {
+ ret = of_property_read_u32(np, "ti,trm-icp", &plat->trm_icp);
+ if (ret)
+ return ret;
+
+ ret = of_property_read_u32(np, "ti,driver-strength-ohm",
+ &drv_strength);
+ if (ret)
+ return ret;
+
+ switch (drv_strength) {
+ case 50:
+ plat->drv_strength = DRIVER_STRENGTH_50_OHM;
+ break;
+ case 33:
+ plat->drv_strength = DRIVER_STRENGTH_33_OHM;
+ break;
+ case 66:
+ plat->drv_strength = DRIVER_STRENGTH_66_OHM;
+ break;
+ case 100:
+ plat->drv_strength = DRIVER_STRENGTH_100_OHM;
+ break;
+ case 40:
+ plat->drv_strength = DRIVER_STRENGTH_40_OHM;
+ break;
+ default:
+ dev_err(dev, "Invalid driver strength\n");
+ return -EINVAL;
+ }
+ }
+
+ mci->send_cmd = am654_sdhci_send_cmd;
+ mci->set_ios = am654_sdhci_set_ios;
+ mci->init = am654_sdhci_init;
+ mci->hw_dev = dev;
+
+ of_property_read_u32(np, "ti,strobe-sel", &plat->strb_sel);
+ of_property_read_u32(np, "ti,clkbuf-sel", &plat->clkbuf_sel);
+
+ plat->fails_without_test_cd = of_property_read_bool(np, "ti,fails-without-test-cd");
+
+ mci_of_parse(&plat->mci);
+
+ ret = sdhci_am654_get_otap_delay(plat);
+ if (ret)
+ return ret;
+
+ plat->sdhci.mci = mci;
+ sdhci_setup_host(&plat->sdhci);
+
+ dev->priv = plat;
+
+ return mci_register(&plat->mci);
+}
+
+static const struct of_device_id am654_sdhci_ids[] = {
+ {
+ .compatible = "ti,am654-sdhci-5.1",
+ .data = &am654_drv_data,
+ }, {
+ .compatible = "ti,j721e-sdhci-8bit",
+ .data = &j721e_8bit_drv_data,
+ }, {
+ .compatible = "ti,j721e-sdhci-4bit",
+ .data = &j721e_4bit_drv_data,
+ }, {
+ .compatible = "ti,am64-sdhci-8bit",
+ .data = &sdhci_am64_8bit_drvdata,
+ }, {
+ .compatible = "ti,am64-sdhci-4bit",
+ .data = &sdhci_am64_4bit_drvdata,
+ }, {
+ .compatible = "ti,am62-sdhci",
+ .data = &sdhci_am64_4bit_drvdata,
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, am654_sdhci_ids);
+
+static struct driver am654_sdhc_driver = {
+ .name = "am654-sdhci",
+ .probe = am654_sdhci_probe,
+ .of_compatible = DRV_OF_COMPAT(am654_sdhci_ids),
+};
+device_platform_driver(am654_sdhc_driver);
diff --git a/drivers/mci/arasan-sdhci.c b/drivers/mci/arasan-sdhci.c
index b43a4f8ddf..b7dd98049f 100644
--- a/drivers/mci/arasan-sdhci.c
+++ b/drivers/mci/arasan-sdhci.c
@@ -6,41 +6,77 @@
#include <init.h>
#include <linux/clk.h>
#include <mci.h>
+#include <mach/zynqmp/firmware-zynqmp.h>
#include "sdhci.h"
#define SDHCI_ARASAN_HCAP_CLK_FREQ_MASK 0xFF00
#define SDHCI_ARASAN_HCAP_CLK_FREQ_SHIFT 8
#define SDHCI_INT_ADMAE BIT(29)
-#define SDHCI_ARASAN_INT_DATA_MASK SDHCI_INT_XFER_COMPLETE | \
+#define SDHCI_ARASAN_INT_DATA_MASK (SDHCI_INT_XFER_COMPLETE | \
SDHCI_INT_DMA | \
SDHCI_INT_SPACE_AVAIL | \
SDHCI_INT_DATA_AVAIL | \
SDHCI_INT_DATA_TIMEOUT | \
SDHCI_INT_DATA_CRC | \
SDHCI_INT_DATA_END_BIT | \
- SDHCI_INT_ADMAE
+ SDHCI_INT_ADMAE)
-#define SDHCI_ARASAN_INT_CMD_MASK SDHCI_INT_CMD_COMPLETE | \
+#define SDHCI_ARASAN_INT_CMD_MASK (SDHCI_INT_CMD_COMPLETE | \
SDHCI_INT_TIMEOUT | \
SDHCI_INT_CRC | \
SDHCI_INT_END_BIT | \
- SDHCI_INT_INDEX
+ SDHCI_INT_INDEX)
#define SDHCI_ARASAN_BUS_WIDTH 4
#define TIMEOUT_VAL 0xE
+#define ZYNQMP_CLK_PHASES 10
+#define ZYNQMP_CLK_PHASE_UHS_SDR104 6
+#define ZYNQMP_CLK_PHASE_HS200 9
+/* Default settings for ZynqMP Clock Phases */
+#define ZYNQMP_ICLK_PHASE {0, 63, 63, 0, 63, 0, 0, 183, 54, 0, 0}
+#define ZYNQMP_OCLK_PHASE {0, 72, 60, 0, 60, 72, 135, 48, 72, 135, 0}
+
+/**
+ * struct sdhci_arasan_clk_data - Arasan Controller Clock Data.
+ *
+ * @sdcardclk_hw: Struct for the clock we might provide to a PHY.
+ * @sdcardclk: Pointer to normal 'struct clock' for sdcardclk_hw.
+ * @sampleclk_hw: Struct for the clock we might provide to a PHY.
+ * @sampleclk: Pointer to normal 'struct clock' for sampleclk_hw.
+ * @clk_phase_in: Array of Input Clock Phase Delays for all speed modes
+ * @clk_phase_out: Array of Output Clock Phase Delays for all speed modes
+ * @set_clk_delays: Function pointer for setting Clock Delays
+ * @clk_of_data: Platform specific runtime clock data storage pointer
+ */
+struct sdhci_arasan_clk_data {
+ struct clk_hw sdcardclk_hw;
+ struct clk *sdcardclk;
+ struct clk_hw sampleclk_hw;
+ struct clk *sampleclk;
+ int clk_phase_in[ZYNQMP_CLK_PHASES + 1];
+ int clk_phase_out[ZYNQMP_CLK_PHASES + 1];
+ void (*set_clk_delays)(struct sdhci *host);
+ void *clk_of_data;
+};
+
struct arasan_sdhci_host {
struct mci_host mci;
struct sdhci sdhci;
- void __iomem *ioaddr;
unsigned int quirks; /* Arasan deviations from spec */
+ struct sdhci_arasan_clk_data clk_data;
/* Controller does not have CD wired and will not function normally without */
#define SDHCI_ARASAN_QUIRK_FORCE_CDTEST BIT(0)
#define SDHCI_ARASAN_QUIRK_NO_1_8_V BIT(1)
+/*
+ * Some of the Arasan variations might not have timing requirements
+ * met at 25MHz for Default Speed mode, those controllers work at
+ * 19MHz instead
+ */
+#define SDHCI_ARASAN_QUIRK_CLOCK_25_BROKEN BIT(2)
};
-
static inline
struct arasan_sdhci_host *to_arasan_sdhci_host(struct mci_host *mci)
{
@@ -53,85 +89,86 @@ struct arasan_sdhci_host *sdhci_to_arasan(struct sdhci *sdhci)
return container_of(sdhci, struct arasan_sdhci_host, sdhci);
}
-static void arasan_sdhci_writel(struct sdhci *sdhci, int reg, u32 val)
+static int arasan_sdhci_card_present(struct mci_host *mci)
{
- struct arasan_sdhci_host *p = sdhci_to_arasan(sdhci);
-
- writel(val, p->ioaddr + reg);
-}
+ struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
+ u32 val;
-static void arasan_sdhci_writew(struct sdhci *sdhci, int reg, u16 val)
-{
- struct arasan_sdhci_host *p = sdhci_to_arasan(sdhci);
+ val = sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE);
- writew(val, p->ioaddr + reg);
+ return !!(val & SDHCI_CARD_DETECT);
}
-static void arasan_sdhci_writeb(struct sdhci *sdhci, int reg, u8 val)
+static int arasan_sdhci_card_write_protected(struct mci_host *mci)
{
- struct arasan_sdhci_host *p = sdhci_to_arasan(sdhci);
+ struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
+ u32 val;
+
+ val = sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE);
- writeb(val, p->ioaddr + reg);
+ return !(val & SDHCI_WRITE_PROTECT);
}
-static u32 arasan_sdhci_readl(struct sdhci *sdhci, int reg)
+static int arasan_sdhci_reset(struct arasan_sdhci_host *host, u8 mask)
{
- struct arasan_sdhci_host *p = sdhci_to_arasan(sdhci);
+ int ret;
- return readl(p->ioaddr + reg);
-}
+ ret = sdhci_reset(&host->sdhci, mask);
+ if (ret)
+ return ret;
-static u16 arasan_sdhci_readw(struct sdhci *sdhci, int reg)
-{
- struct arasan_sdhci_host *p = sdhci_to_arasan(sdhci);
+ if (host->quirks & SDHCI_ARASAN_QUIRK_FORCE_CDTEST) {
+ u8 ctrl;
+
+ ctrl = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL);
+ ctrl |= SDHCI_CTRL_CDTEST_INS | SDHCI_CTRL_CDTEST_INS;
+ sdhci_write8(&host->sdhci, SDHCI_HOST_CONTROL, ctrl);
+ }
- return readw(p->ioaddr + reg);
+ return 0;
}
-static u8 arasan_sdhci_readb(struct sdhci *sdhci, int reg)
+static void arasan_zynqmp_dll_reset(struct arasan_sdhci_host *host, u32 deviceid)
{
- struct arasan_sdhci_host *p = sdhci_to_arasan(sdhci);
+ u16 clk;
- return readb(p->ioaddr + reg);
-}
+ clk = sdhci_read16(&host->sdhci, SDHCI_CLOCK_CONTROL);
+ clk &= ~(SDHCI_CLOCK_CARD_EN | SDHCI_CLOCK_INT_EN);
+ sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, clk);
-static int arasan_sdhci_card_present(struct mci_host *mci)
-{
- struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
+ /* Issue DLL Reset */
+ zynqmp_pm_sd_dll_reset(deviceid, PM_DLL_RESET_PULSE);
+
+ clk = sdhci_read16(&host->sdhci, SDHCI_CLOCK_CONTROL);
- return !!(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & SDHCI_CARD_DETECT);
+ sdhci_enable_clk(&host->sdhci, clk);
}
-static int arasan_sdhci_card_write_protected(struct mci_host *mci)
+static int arasan_zynqmp_execute_tuning(struct mci_host *mci, u32 opcode)
{
struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
+ struct clk_hw *hw = &host->clk_data.sdcardclk_hw;
+ const char *clk_name = clk_hw_get_name(hw);
+ u32 device_id = !strcmp(clk_name, "clk_out_sd0") ? NODE_SD_0 :
+ NODE_SD_1;
+ int err;
- return !(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & SDHCI_WRITE_PROTECT);
-}
+ /* ZynqMP SD controller does not perform auto tuning in DDR50 mode */
+ if (mci->timing == MMC_TIMING_UHS_DDR50)
+ return 0;
-static int arasan_sdhci_reset(struct arasan_sdhci_host *host, u8 mask)
-{
- sdhci_write8(&host->sdhci, SDHCI_SOFTWARE_RESET, mask);
+ arasan_zynqmp_dll_reset(host, device_id);
- /* wait for reset completion */
- if (wait_on_timeout(100 * MSECOND,
- !(sdhci_read8(&host->sdhci, SDHCI_SOFTWARE_RESET) & mask))){
- dev_err(host->mci.hw_dev, "SDHCI reset timeout\n");
- return -ETIMEDOUT;
- }
+ err = sdhci_execute_tuning(&host->sdhci, opcode);
+ if (err)
+ return err;
- if (host->quirks & SDHCI_ARASAN_QUIRK_FORCE_CDTEST) {
- u8 ctrl;
-
- ctrl = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL);
- ctrl |= SDHCI_CARD_DETECT_TEST_LEVEL | SDHCI_CARD_DETECT_SIGNAL_SELECTION;
- sdhci_write8(&host->sdhci, ctrl, SDHCI_HOST_CONTROL);
- }
+ arasan_zynqmp_dll_reset(host, device_id);
return 0;
}
-static int arasan_sdhci_init(struct mci_host *mci, struct device_d *dev)
+static int arasan_sdhci_init(struct mci_host *mci, struct device *dev)
{
struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
int ret;
@@ -141,113 +178,66 @@ static int arasan_sdhci_init(struct mci_host *mci, struct device_d *dev)
return ret;
sdhci_write8(&host->sdhci, SDHCI_POWER_CONTROL,
- SDHCI_BUS_VOLTAGE_330 | SDHCI_BUS_POWER_EN);
+ SDHCI_BUS_VOLTAGE_330 | SDHCI_BUS_POWER_EN);
udelay(400);
sdhci_write32(&host->sdhci, SDHCI_INT_ENABLE,
- SDHCI_ARASAN_INT_DATA_MASK |
- SDHCI_ARASAN_INT_CMD_MASK);
- sdhci_write32(&host->sdhci, SDHCI_SIGNAL_ENABLE, 0x00);
+ SDHCI_ARASAN_INT_DATA_MASK | SDHCI_ARASAN_INT_CMD_MASK);
+ sdhci_write32(&host->sdhci, SDHCI_SIGNAL_ENABLE, 0);
return 0;
}
-#define SDHCI_MAX_DIV_SPEC_300 2046
-
-static u16 arasan_sdhci_get_clock_divider(struct arasan_sdhci_host *host,
- unsigned int reqclk)
+static void arasan_sdhci_set_clock(struct mci_host *mci, unsigned int clock)
{
- u16 div;
+ struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
+ struct sdhci_arasan_clk_data *clk_data = &host->clk_data;
+
+ if (host->quirks & SDHCI_ARASAN_QUIRK_CLOCK_25_BROKEN) {
+ /*
+ * Some of the Arasan variations might not have timing
+ * requirements met at 25MHz for Default Speed mode,
+ * those controllers work at 19MHz instead.
+ */
+ if (clock == 25000000)
+ clock = (25000000 * 19) / 25;
+ }
- for (div = 1; div < SDHCI_MAX_DIV_SPEC_300; div += 2)
- if ((host->mci.f_max / div) <= reqclk)
- break;
- div /= 2;
+ clk_set_phase(clk_data->sampleclk,
+ clk_data->clk_phase_in[mci->mci->host->timing]);
+ clk_set_phase(clk_data->sdcardclk,
+ clk_data->clk_phase_out[mci->mci->host->timing]);
- return div;
+ sdhci_set_clock(&host->sdhci, clock, host->sdhci.max_clk);
}
-#define SDHCI_FREQ_SEL_10_BIT(x) (((x) & 0x300) >> 2)
-
static void arasan_sdhci_set_ios(struct mci_host *mci, struct mci_ios *ios)
{
struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
u16 val;
- /* stop clock */
- sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, 0);
-
- if (ios->clock) {
- u64 start;
-
- /* set & start clock */
- val = arasan_sdhci_get_clock_divider(host, ios->clock);
- /* Bit 6 & 7 are upperbits of 10bit divider */
- val = SDHCI_FREQ_SEL(val) | SDHCI_FREQ_SEL_10_BIT(val);
- val |= SDHCI_INTCLOCK_EN;
- sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, val);
-
- start = get_time_ns();
- while (!(sdhci_read16(&host->sdhci, SDHCI_CLOCK_CONTROL) &
- SDHCI_INTCLOCK_STABLE)) {
- if (is_timeout(start, 20 * MSECOND)) {
- dev_err(host->mci.hw_dev,
- "SDHCI clock stable timeout\n");
- return;
- }
- }
- /* enable bus clock */
- sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL,
- val | SDHCI_SDCLOCK_EN);
- }
+ if (ios->clock)
+ arasan_sdhci_set_clock(mci, ios->clock);
- val = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL) &
- ~(SDHCI_DATA_WIDTH_4BIT | SDHCI_DATA_WIDTH_8BIT);
+ sdhci_set_bus_width(&host->sdhci, ios->bus_width);
- switch (ios->bus_width) {
- case MMC_BUS_WIDTH_8:
- val |= SDHCI_DATA_WIDTH_8BIT;
- break;
- case MMC_BUS_WIDTH_4:
- val |= SDHCI_DATA_WIDTH_4BIT;
- break;
- }
+ val = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL);
if (ios->clock > 26000000)
- val |= SDHCI_HIGHSPEED_EN;
+ val |= SDHCI_CTRL_HISPD;
else
- val &= ~SDHCI_HIGHSPEED_EN;
+ val &= ~SDHCI_CTRL_HISPD;
sdhci_write8(&host->sdhci, SDHCI_HOST_CONTROL, val);
}
-static int arasan_sdhci_wait_for_done(struct arasan_sdhci_host *host, u32 mask)
+static void print_error(struct arasan_sdhci_host *host, int cmdidx, int ret)
{
- u64 start = get_time_ns();
- u16 stat;
-
- do {
- stat = sdhci_read16(&host->sdhci, SDHCI_INT_NORMAL_STATUS);
- if (stat & SDHCI_INT_ERROR) {
- dev_err(host->mci.hw_dev, "SDHCI_INT_ERROR: 0x%08x\n",
- sdhci_read16(&host->sdhci, SDHCI_INT_ERROR_STATUS));
- return -EPERM;
- }
+ if (ret == -ETIMEDOUT)
+ return;
- if (is_timeout(start, 1000 * MSECOND)) {
- dev_err(host->mci.hw_dev,
- "SDHCI timeout while waiting for done\n");
- return -ETIMEDOUT;
- }
- } while ((stat & mask) != mask);
-
- return 0;
-}
-
-static void print_error(struct arasan_sdhci_host *host, int cmdidx)
-{
dev_err(host->mci.hw_dev,
- "error while transfering data for command %d\n", cmdidx);
+ "error while transferring data for command %d\n", cmdidx);
dev_err(host->mci.hw_dev, "state = 0x%08x , interrupt = 0x%08x\n",
sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE),
sdhci_read32(&host->sdhci, SDHCI_INT_NORMAL_STATUS));
@@ -258,90 +248,460 @@ static int arasan_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
{
struct arasan_sdhci_host *host = to_arasan_sdhci_host(mci);
u32 mask, command, xfer;
+ dma_addr_t dma;
int ret;
- /* Wait for idle before next command */
- mask = SDHCI_CMD_INHIBIT_CMD;
- if (cmd->cmdidx != MMC_CMD_STOP_TRANSMISSION)
- mask |= SDHCI_CMD_INHIBIT_DATA;
-
- ret = wait_on_timeout(10 * MSECOND,
- !(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & mask));
-
- if (ret) {
- dev_err(host->mci.hw_dev,
- "SDHCI timeout while waiting for idle\n");
+ ret = sdhci_wait_idle(&host->sdhci, cmd, data);
+ if (ret)
return ret;
- }
sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
mask = SDHCI_INT_CMD_COMPLETE;
- if (data)
+ if (cmd->resp_type & MMC_RSP_BUSY)
mask |= SDHCI_INT_XFER_COMPLETE;
- sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data, false, &command, &xfer);
+ sdhci_setup_data_dma(&host->sdhci, data, &dma);
+
+ sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data,
+ dma == SDHCI_NO_DMA ? false : true,
+ &command, &xfer);
sdhci_write8(&host->sdhci, SDHCI_TIMEOUT_CONTROL, TIMEOUT_VAL);
- sdhci_write16(&host->sdhci, SDHCI_TRANSFER_MODE, xfer);
- sdhci_write16(&host->sdhci, SDHCI_BLOCK_SIZE, SDHCI_DMA_BOUNDARY_512K |
- SDHCI_TRANSFER_BLOCK_SIZE(data->blocksize));
- sdhci_write16(&host->sdhci, SDHCI_BLOCK_COUNT, data->blocks);
+ if (xfer)
+ sdhci_write16(&host->sdhci, SDHCI_TRANSFER_MODE, xfer);
+ if (data) {
+ sdhci_write16(&host->sdhci, SDHCI_BLOCK_SIZE,
+ SDHCI_DMA_BOUNDARY_512K | SDHCI_TRANSFER_BLOCK_SIZE(data->blocksize));
+ sdhci_write16(&host->sdhci, SDHCI_BLOCK_COUNT, data->blocks);
+ }
sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, cmd->cmdarg);
sdhci_write16(&host->sdhci, SDHCI_COMMAND, command);
- ret = arasan_sdhci_wait_for_done(host, mask);
- if (ret == -EPERM)
+ /* CMD19/21 generate _only_ Buffer Read Ready interrupt */
+ if (cmd->cmdidx == MMC_SEND_TUNING_BLOCK || cmd->cmdidx == MMC_SEND_TUNING_BLOCK_HS200)
+ mask = SDHCI_INT_DATA_AVAIL;
+
+ ret = sdhci_wait_for_done(&host->sdhci, mask);
+ if (ret)
goto error;
- else if (ret)
- return ret;
sdhci_read_response(&host->sdhci, cmd);
- sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, mask);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE);
if (data)
- ret = sdhci_transfer_data(&host->sdhci, data);
+ ret = sdhci_transfer_data_dma(&host->sdhci, data, dma);
error:
if (ret) {
- print_error(host, cmd->cmdidx);
- arasan_sdhci_reset(host, BIT(1)); // SDHCI_RESET_CMD
- arasan_sdhci_reset(host, BIT(2)); // SDHCI_RESET_DATA
+ print_error(host, cmd->cmdidx, ret);
+ arasan_sdhci_reset(host, SDHCI_RESET_CMD);
+ arasan_sdhci_reset(host, SDHCI_RESET_DATA);
}
sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
+
return ret;
}
+static void sdhci_arasan_set_clk_delays(struct sdhci *host)
+{
+ struct arasan_sdhci_host *arasan_sdhci = sdhci_to_arasan(host);
+ struct mci_host *mci = &arasan_sdhci->mci;
+ struct sdhci_arasan_clk_data *clk_data = &arasan_sdhci->clk_data;
+
+ clk_set_phase(clk_data->sampleclk,
+ clk_data->clk_phase_in[mci->timing]);
+ clk_set_phase(clk_data->sdcardclk,
+ clk_data->clk_phase_out[mci->timing]);
+}
-static void arasan_sdhci_set_mci_caps(struct arasan_sdhci_host *host)
+static void arasan_dt_read_clk_phase(struct device *dev,
+ struct sdhci_arasan_clk_data *clk_data,
+ unsigned int timing, const char *prop)
{
- u16 caps = sdhci_read16(&host->sdhci, SDHCI_CAPABILITIES_1);
+ struct device_node *np = dev->of_node;
- if ((caps & SDHCI_HOSTCAP_VOLTAGE_180) &&
- !(host->quirks & SDHCI_ARASAN_QUIRK_NO_1_8_V))
- host->mci.voltages |= MMC_VDD_165_195;
- if (caps & SDHCI_HOSTCAP_VOLTAGE_300)
- host->mci.voltages |= MMC_VDD_29_30 | MMC_VDD_30_31;
- if (caps & SDHCI_HOSTCAP_VOLTAGE_330)
- host->mci.voltages |= MMC_VDD_32_33 | MMC_VDD_33_34;
+ u32 clk_phase[2] = {0};
+ int ret;
- if (caps & SDHCI_HOSTCAP_HIGHSPEED)
- host->mci.host_caps |= (MMC_CAP_MMC_HIGHSPEED_52MHZ |
- MMC_CAP_MMC_HIGHSPEED |
- MMC_CAP_SD_HIGHSPEED);
+ /*
+ * Read Tap Delay values from DT, if the DT does not contain the
+ * Tap Values then use the pre-defined values.
+ */
+ ret = of_property_read_u32_array(np, prop, &clk_phase[0], 2);
+ if (ret < 0) {
+ dev_dbg(dev, "Using predefined clock phase for %s = %d %d\n",
+ prop, clk_data->clk_phase_in[timing],
+ clk_data->clk_phase_out[timing]);
+ return;
+ }
- /* parse board supported bus width capabilities */
- mci_of_parse(&host->mci);
+ /* The values read are Input and Output Clock Delays in order */
+ clk_data->clk_phase_in[timing] = clk_phase[0];
+ clk_data->clk_phase_out[timing] = clk_phase[1];
+}
+
+/**
+ * sdhci_zynqmp_sampleclk_set_phase - Set the SD Input Clock Tap Delays
+ *
+ * @hw: Pointer to the hardware clock structure.
+ * @degrees: The clock phase shift between 0 - 359.
+ *
+ * Set the SD Input Clock Tap Delays for Input path
+ *
+ * Return: 0 on success and error value on error
+ */
+static int arasan_zynqmp_sampleclk_set_phase(struct clk_hw *hw, int degrees)
+{
+ struct sdhci_arasan_clk_data *clk_data =
+ container_of(hw, struct sdhci_arasan_clk_data, sampleclk_hw);
+ struct arasan_sdhci_host *sdhci_arasan =
+ container_of(clk_data, struct arasan_sdhci_host, clk_data);
+ struct mci_host *host = &sdhci_arasan->mci;
+ const char *clk_name = clk_hw_get_name(hw);
+ u32 node_id = !strcmp(clk_name, "clk_in_sd0") ? NODE_SD_0 : NODE_SD_1;
+ u8 tap_delay, tap_max = 0;
+ int ret;
+
+ /* This is applicable for SDHCI_SPEC_300 and above */
+ if (sdhci_arasan->sdhci.version < SDHCI_SPEC_300)
+ return 0;
+
+ /* Assert DLL Reset */
+ zynqmp_pm_sd_dll_reset(node_id, PM_DLL_RESET_ASSERT);
+
+ switch (host->timing) {
+ case MMC_TIMING_MMC_HS:
+ case MMC_TIMING_SD_HS:
+ case MMC_TIMING_UHS_DDR50:
+ case MMC_TIMING_MMC_DDR52:
+ /* For 50MHz clock, 120 Taps are available */
+ tap_max = 120;
+ break;
+ case MMC_TIMING_UHS_SDR50:
+ /* For 100MHz clock, 60 Taps are available */
+ tap_max = 60;
+ break;
+ case MMC_TIMING_UHS_SDR104:
+ case MMC_TIMING_MMC_HS200:
+ /* For 200MHz clock, 30 Taps are available */
+ tap_max = 30;
+ break;
+ default:
+ break;
+ }
+
+ tap_delay = (degrees * tap_max) / 360;
+
+ /* Set the Clock Phase */
+ ret = zynqmp_pm_set_sd_tapdelay(node_id, PM_TAPDELAY_INPUT, tap_delay);
+ if (ret)
+ pr_err("Error setting Input Tap Delay\n");
+
+ return ret;
+}
+
+static unsigned long arasan_zynqmp_sampleclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
+{
+ struct sdhci_arasan_clk_data *clk_data =
+ container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
+ struct arasan_sdhci_host *sdhci_arasan =
+ container_of(clk_data, struct arasan_sdhci_host, clk_data);
+ struct mci_host *host = &sdhci_arasan->mci;
+
+ return host->actual_clock;
+};
+
+/**
+ * sdhci_zynqmp_sdcardclk_set_phase - Set the SD Output Clock Tap Delays
+ *
+ * @hw: Pointer to the hardware clock structure.
+ * @degrees: The clock phase shift between 0 - 359.
+ *
+ * Set the SD Output Clock Tap Delays for Output path
+ *
+ * Return: 0 on success and error value on error
+ */
+static int arasan_zynqmp_sdcardclk_set_phase(struct clk_hw *hw, int degrees)
+{
+ struct sdhci_arasan_clk_data *clk_data =
+ container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
+ struct arasan_sdhci_host *sdhci_arasan =
+ container_of(clk_data, struct arasan_sdhci_host, clk_data);
+ struct mci_host *host = &sdhci_arasan->mci;
+ const char *clk_name = clk_hw_get_name(hw);
+ u32 node_id = !strcmp(clk_name, "clk_out_sd0") ? NODE_SD_0 : NODE_SD_1;
+ u8 tap_delay, tap_max = 0;
+ int ret;
- /* limit bus widths to controller capabilities */
- if (!(caps & SDHCI_HOSTCAP_8BIT))
- host->mci.host_caps &= ~MMC_CAP_8_BIT_DATA;
+ /* This is applicable for SDHCI_SPEC_300 and above */
+ if (sdhci_arasan->sdhci.version < SDHCI_SPEC_300)
+ return 0;
+
+ switch (host->timing) {
+ case MMC_TIMING_MMC_HS:
+ case MMC_TIMING_SD_HS:
+ case MMC_TIMING_UHS_DDR50:
+ case MMC_TIMING_MMC_DDR52:
+ /* For 50MHz clock, 30 Taps are available */
+ tap_max = 30;
+ break;
+ case MMC_TIMING_UHS_SDR50:
+ /* For 100MHz clock, 15 Taps are available */
+ tap_max = 15;
+ break;
+ case MMC_TIMING_UHS_SDR104:
+ case MMC_TIMING_MMC_HS200:
+ /* For 200MHz clock, 8 Taps are available */
+ tap_max = 8;
+ break;
+ default:
+ break;
+ }
+
+ tap_delay = (degrees * tap_max) / 360;
+
+ /* Set the Clock Phase */
+ ret = zynqmp_pm_set_sd_tapdelay(node_id, PM_TAPDELAY_OUTPUT, tap_delay);
+ if (ret)
+ pr_err("Error setting Output Tap Delay\n");
+
+ /* Release DLL Reset */
+ zynqmp_pm_sd_dll_reset(node_id, PM_DLL_RESET_RELEASE);
+
+ return ret;
}
-static int arasan_sdhci_probe(struct device_d *dev)
+static unsigned long arasan_zynqmp_sdcardclk_recalc_rate(struct clk_hw *hw,
+ unsigned long parent_rate)
{
- struct device_node *np = dev->device_node;
+ struct sdhci_arasan_clk_data *clk_data =
+ container_of(hw, struct sdhci_arasan_clk_data, sdcardclk_hw);
+ struct arasan_sdhci_host *sdhci_arasan =
+ container_of(clk_data, struct arasan_sdhci_host, clk_data);
+ struct mci_host *host = &sdhci_arasan->mci;
+
+ return host->actual_clock;
+};
+
+static const struct clk_ops clk_sampleclk_ops = {
+ .recalc_rate = arasan_zynqmp_sampleclk_recalc_rate,
+ .set_phase = arasan_zynqmp_sampleclk_set_phase,
+};
+
+static const struct clk_ops clk_sdcardclk_ops = {
+ .recalc_rate = arasan_zynqmp_sdcardclk_recalc_rate,
+ .set_phase = arasan_zynqmp_sdcardclk_set_phase,
+};
+
+/**
+ * arasan_sdhci_register_sampleclk - Register the sampleclk for a PHY to use
+ *
+ * @sdhci_arasan: Our private data structure.
+ * @clk_xin: Pointer to the functional clock
+ * @dev: Pointer to our struct device.
+ *
+ * Some PHY devices need to know what the actual card clock is. In order for
+ * them to find out, we'll provide a clock through the common clock framework
+ * for them to query.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int
+arasan_sdhci_register_sampleclk(struct sdhci_arasan_clk_data *clk_data,
+ struct clk *clk_xin,
+ struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct clk_init_data sampleclk_init = {};
+ const char *clk_name;
+ int ret;
+
+ ret = of_property_read_string_index(np, "clock-output-names", 1,
+ &sampleclk_init.name);
+ if (ret) {
+ dev_err(dev, "DT has #clock-cells but no clock-output-names\n");
+ return ret;
+ }
+
+ clk_name = __clk_get_name(clk_xin);
+ sampleclk_init.parent_names = &clk_name;
+ sampleclk_init.num_parents = 1;
+ sampleclk_init.ops = &clk_sampleclk_ops;
+
+ clk_data->sampleclk_hw.init = &sampleclk_init;
+ clk_data->sampleclk = clk_register(dev, &clk_data->sampleclk_hw);
+ if (IS_ERR(clk_data->sampleclk))
+ return PTR_ERR(clk_data->sampleclk);
+ clk_data->sampleclk_hw.init = NULL;
+
+ ret = of_clk_add_provider(np, of_clk_src_simple_get,
+ clk_data->sampleclk);
+ if (ret)
+ dev_err(dev, "Failed to add sample clock provider\n");
+
+ return ret;
+}
+
+/**
+ * sdhci_arasan_register_sdcardclk - Register the sdcardclk for a PHY to use
+ *
+ * @sdhci_arasan: Our private data structure.
+ * @clk_xin: Pointer to the functional clock
+ * @dev: Pointer to our struct device.
+ *
+ * Some PHY devices need to know what the actual card clock is. In order for
+ * them to find out, we'll provide a clock through the common clock framework
+ * for them to query.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int
+arasan_sdhci_register_sdcardclk(struct sdhci_arasan_clk_data *clk_data,
+ struct clk *clk_xin,
+ struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ struct clk_init_data sdcardclk_init = {};
+ const char *clk_name;
+ int ret;
+
+ ret = of_property_read_string_index(np, "clock-output-names", 0,
+ &sdcardclk_init.name);
+ if (ret) {
+ dev_err(dev, "DT has #clock-cells but no clock-output-names\n");
+ return ret;
+ }
+
+ clk_name = __clk_get_name(clk_xin);
+ sdcardclk_init.parent_names = &clk_name;
+ sdcardclk_init.ops = &clk_sdcardclk_ops;
+ sdcardclk_init.num_parents = 1;
+
+ clk_data->sdcardclk_hw.init = &sdcardclk_init;
+ clk_data->sdcardclk = clk_register(dev, &clk_data->sdcardclk_hw);
+ if (IS_ERR(clk_data->sdcardclk))
+ return PTR_ERR(clk_data->sdcardclk);
+ clk_data->sdcardclk_hw.init = NULL;
+
+ ret = of_clk_add_provider(np, of_clk_src_simple_get,
+ clk_data->sdcardclk);
+ if (ret)
+ dev_err(dev, "Failed to add sdcard clock provider\n");
+
+ return ret;
+}
+
+/**
+ * arasan_sdhci_register_sdclk - Register the sdcardclk for a PHY to use
+ *
+ * @sdhci_arasan: Our private data structure.
+ * @clk_xin: Pointer to the functional clock
+ * @dev: Pointer to our struct device.
+ *
+ * Some PHY devices need to know what the actual card clock is. In order for
+ * them to find out, we'll provide a clock through the common clock framework
+ * for them to query.
+ *
+ * Note: without seriously re-architecting SDHCI's clock code and testing on
+ * all platforms, there's no way to create a totally beautiful clock here
+ * with all clock ops implemented. Instead, we'll just create a clock that can
+ * be queried and set the CLK_GET_RATE_NOCACHE attribute to tell common clock
+ * framework that we're doing things behind its back. This should be sufficient
+ * to create nice clean device tree bindings and later (if needed) we can try
+ * re-architecting SDHCI if we see some benefit to it.
+ *
+ * Return: 0 on success and error value on error
+ */
+static int arasan_sdhci_register_sdclk(struct sdhci_arasan_clk_data *sdhci_arasan,
+ struct clk *clk_xin,
+ struct device *dev)
+{
+ struct device_node *np = dev->of_node;
+ u32 num_clks = 0;
+ int ret;
+
+ /* Providing a clock to the PHY is optional; no error if missing */
+ if (of_property_read_u32(np, "#clock-cells", &num_clks) < 0)
+ return 0;
+
+ ret = arasan_sdhci_register_sdcardclk(sdhci_arasan, clk_xin, dev);
+ if (ret)
+ return ret;
+
+ if (num_clks)
+ return arasan_sdhci_register_sampleclk(sdhci_arasan, clk_xin,
+ dev);
+
+ return 0;
+}
+
+/**
+ * arasan_dt_parse_clk_phases - Read Clock Delay values from DT
+ *
+ * @dev: Pointer to our struct device.
+ * @clk_data: Pointer to the Clock Data structure
+ *
+ * Called at initialization to parse the values of Clock Delays.
+ */
+static void arasan_dt_parse_clk_phases(struct device *dev,
+ struct sdhci_arasan_clk_data *clk_data)
+{
+ u32 mio_bank = 0;
+ int i;
+
+ /*
+ * This has been kept as a pointer and is assigned a function here.
+ * So that different controller variants can assign their own handling
+ * function.
+ */
+ clk_data->set_clk_delays = sdhci_arasan_set_clk_delays;
+
+ if (of_device_is_compatible(dev->of_node, "xlnx,zynqmp-8.9a")) {
+ u32 zynqmp_iclk_phase[ZYNQMP_CLK_PHASES + 1] = ZYNQMP_ICLK_PHASE;
+ u32 zynqmp_oclk_phase[ZYNQMP_CLK_PHASES + 1] = ZYNQMP_OCLK_PHASE;
+
+ of_property_read_u32(dev->of_node, "xlnx,mio-bank", &mio_bank);
+ if (mio_bank == 2) {
+ zynqmp_oclk_phase[ZYNQMP_CLK_PHASE_UHS_SDR104] = 90;
+ zynqmp_oclk_phase[ZYNQMP_CLK_PHASE_HS200] = 90;
+ }
+
+ for (i = 0; i <= ZYNQMP_CLK_PHASES; i++) {
+ clk_data->clk_phase_in[i] = zynqmp_iclk_phase[i];
+ clk_data->clk_phase_out[i] = zynqmp_oclk_phase[i];
+ }
+ }
+
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_LEGACY,
+ "clk-phase-legacy");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS,
+ "clk-phase-mmc-hs");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_SD_HS,
+ "clk-phase-sd-hs");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR12,
+ "clk-phase-uhs-sdr12");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR25,
+ "clk-phase-uhs-sdr25");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR50,
+ "clk-phase-uhs-sdr50");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_SDR104,
+ "clk-phase-uhs-sdr104");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_UHS_DDR50,
+ "clk-phase-uhs-ddr50");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_DDR52,
+ "clk-phase-mmc-ddr52");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS200,
+ "clk-phase-mmc-hs200");
+ arasan_dt_read_clk_phase(dev, clk_data, MMC_TIMING_MMC_HS400,
+ "clk-phase-mmc-hs400");
+}
+
+static int arasan_sdhci_probe(struct device *dev)
+{
+ struct device_node *np = dev->of_node;
struct arasan_sdhci_host *arasan_sdhci;
struct clk *clk_xin, *clk_ahb;
struct resource *iores;
@@ -355,7 +715,6 @@ static int arasan_sdhci_probe(struct device_d *dev)
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores))
return PTR_ERR(iores);
- arasan_sdhci->ioaddr = IOMEM(iores->start);
clk_ahb = clk_get(dev, "clk_ahb");
if (IS_ERR(clk_ahb)) {
@@ -387,12 +746,14 @@ static int arasan_sdhci_probe(struct device_d *dev)
if (of_property_read_bool(np, "no-1-8-v"))
arasan_sdhci->quirks |= SDHCI_ARASAN_QUIRK_NO_1_8_V;
- arasan_sdhci->sdhci.read32 = arasan_sdhci_readl;
- arasan_sdhci->sdhci.read16 = arasan_sdhci_readw;
- arasan_sdhci->sdhci.read8 = arasan_sdhci_readb;
- arasan_sdhci->sdhci.write32 = arasan_sdhci_writel;
- arasan_sdhci->sdhci.write16 = arasan_sdhci_writew;
- arasan_sdhci->sdhci.write8 = arasan_sdhci_writeb;
+ if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a")) {
+ if (IS_ENABLED(CONFIG_MCI_TUNING))
+ mci->execute_tuning = arasan_zynqmp_execute_tuning;
+ arasan_sdhci->quirks |= SDHCI_ARASAN_QUIRK_CLOCK_25_BROKEN;
+ }
+
+ arasan_sdhci->sdhci.base = IOMEM(iores->start);
+ arasan_sdhci->sdhci.mci = mci;
mci->send_cmd = arasan_sdhci_send_cmd;
mci->set_ios = arasan_sdhci_set_ios;
mci->init = arasan_sdhci_init;
@@ -400,10 +761,26 @@ static int arasan_sdhci_probe(struct device_d *dev)
mci->card_write_protected = arasan_sdhci_card_write_protected;
mci->hw_dev = dev;
- mci->f_max = clk_get_rate(clk_xin);
+ /*
+ * clk_rates on ZynqMP are rounded wrong. For HS200 clk_get_rate retunrs
+ * 199999998 instead of 200000000
+ */
+ if (of_device_is_compatible(np, "xlnx,zynqmp-8.9a"))
+ mci->f_max = 200000000;
+ else
+ mci->f_max = clk_get_rate(clk_xin);
+
mci->f_min = 50000000 / 256;
- arasan_sdhci_set_mci_caps(arasan_sdhci);
+ if (IS_ENABLED(CONFIG_ARCH_ZYNQMP))
+ arasan_sdhci_register_sdclk(&arasan_sdhci->clk_data, clk_xin, dev);
+
+ arasan_dt_parse_clk_phases(dev, &arasan_sdhci->clk_data);
+
+ /* parse board supported bus width capabilities */
+ mci_of_parse(&arasan_sdhci->mci);
+
+ sdhci_setup_host(&arasan_sdhci->sdhci);
dev->priv = arasan_sdhci;
@@ -414,8 +791,9 @@ static __maybe_unused struct of_device_id arasan_sdhci_compatible[] = {
{ .compatible = "arasan,sdhci-8.9a" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, arasan_sdhci_compatible);
-static struct driver_d arasan_sdhci_driver = {
+static struct driver arasan_sdhci_driver = {
.name = "arasan-sdhci",
.probe = arasan_sdhci_probe,
.of_compatible = DRV_OF_COMPAT(arasan_sdhci_compatible),
diff --git a/drivers/mci/atmel-mci-regs.h b/drivers/mci/atmel-mci-regs.h
index 2866e3eb77..44db8a9dff 100644
--- a/drivers/mci/atmel-mci-regs.h
+++ b/drivers/mci/atmel-mci-regs.h
@@ -1,12 +1,7 @@
-/*
- * Atmel MultiMedia Card Interface driver
- *
- * Copyright (C) 2004-2006 Atmel Corporation
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* SPDX-FileCopyrightText: 2004-2006 Atmel Corporation */
+
+/* Atmel MultiMedia Card Interface driver */
/*
* Superset of MCI IP registers integrated in Atmel AVR32 and AT91 Processors
@@ -149,6 +144,37 @@
# define ATMCI_PDC_CONNECTED 1
#endif
+struct atmel_mci_caps {
+ bool has_cfg_reg;
+ bool has_highspeed;
+ bool has_rwproof;
+ bool has_odd_clk_div;
+ bool need_reset_after_xfer;
+};
+
+struct atmel_mci {
+ struct mci_host mci;
+ void __iomem *regs;
+ struct device *hw_dev;
+ struct clk *clk;
+
+ u32 datasize;
+ struct mci_cmd *cmd;
+ struct mci_data *data;
+ unsigned slot_b;
+ int version;
+ struct atmel_mci_caps caps;
+
+ unsigned long bus_hz;
+ u32 mode_reg;
+ u32 cfg_reg;
+ u32 sdc_reg;
+ bool need_reset;
+ int detect_pin;
+};
+
+#define to_mci_host(mci) container_of(mci, struct atmel_mci, mci)
+
/*
* Fix sconfig's burst size according to atmel MCI. We need to convert them as:
* 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3.
@@ -163,4 +189,10 @@ static inline unsigned int atmci_convert_chksize(unsigned int maxburst)
return 0;
}
+void atmci_common_set_ios(struct atmel_mci *host, struct mci_ios *ios);
+int atmci_reset(struct mci_host *mci, struct device *mci_dev);
+int atmci_common_request(struct atmel_mci *host, struct mci_cmd *cmd,
+ struct mci_data *data);
+void atmci_get_cap(struct atmel_mci *host);
+
#endif /* __DRIVERS_MMC_ATMEL_MCI_H__ */
diff --git a/drivers/mci/atmel-sdhci-common.c b/drivers/mci/atmel-sdhci-common.c
new file mode 100644
index 0000000000..082ce842f7
--- /dev/null
+++ b/drivers/mci/atmel-sdhci-common.c
@@ -0,0 +1,345 @@
+// SPDX-License-Identifier: GPL-2.0-only AND BSD-1-Clause
+/*
+ * Copyright (c) 2015, Atmel Corporation
+ * Copyright (c) 2019, Ahmad Fatoum, Pengutronix
+ *
+ * Atmel's name may not be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ */
+
+#define pr_fmt(fmt) "atmel-sdhci: " fmt
+
+#include <common.h>
+#include <mci.h>
+#include <linux/bitfield.h>
+
+#include <mach/at91/early_udelay.h>
+
+#ifdef __PBL__
+#define udelay early_udelay
+#undef dev_err
+#define dev_err(d, ...) pr_err(__VA_ARGS__)
+#undef dev_warn
+#define dev_warn(d, ...) pr_warn(__VA_ARGS__)
+#endif
+
+#include "atmel-sdhci.h"
+
+#define AT91_SDHCI_MC1R 0x204
+#define AT91_SDHCI_MC1_FCD BIT(7)
+#define AT91_SDHCI_CALCR 0x240
+#define AT91_SDHCI_CALCR_EN BIT(0)
+#define AT91_SDHCI_CALCR_ALWYSON BIT(4)
+
+static inline struct at91_sdhci *to_priv(struct sdhci *sdhci)
+{
+ return container_of(sdhci, struct at91_sdhci, sdhci);
+}
+
+void at91_sdhci_host_capability(struct at91_sdhci *host,
+ unsigned *voltages)
+{
+ u32 caps;
+
+ caps = sdhci_read32(&host->sdhci, SDHCI_CAPABILITIES);
+
+ if (caps & SDHCI_CAN_VDD_330)
+ *voltages |= MMC_VDD_32_33 | MMC_VDD_33_34;
+ if (caps & SDHCI_CAN_VDD_300)
+ *voltages |= MMC_VDD_29_30 | MMC_VDD_30_31;
+ if (caps & SDHCI_CAN_VDD_180)
+ *voltages |= MMC_VDD_165_195;
+}
+
+bool at91_sdhci_is_card_inserted(struct at91_sdhci *host)
+{
+ struct sdhci *sdhci = &host->sdhci;
+ bool is_inserted = false;
+ u32 status_mask, reg;
+ int ret;
+
+ /* Enable (unmask) the Interrupt Status 'card inserted' bit */
+ status_mask = sdhci_read32(sdhci, SDHCI_INT_ENABLE);
+ status_mask |= SDHCI_INT_CARD_INSERT;
+ sdhci_write32(sdhci, SDHCI_INT_ENABLE, status_mask);
+
+ reg = sdhci_read32(sdhci, SDHCI_PRESENT_STATE);
+ if (reg & SDHCI_CARD_PRESENT) {
+ is_inserted = true;
+ goto exit;
+ }
+
+ /*
+ * Debouncing of the card detect pin is up to 13ms on sama5d2 rev B
+ * and later. Try to be safe and wait for up to 50ms.
+ */
+ ret = sdhci_read32_poll_timeout(sdhci, SDHCI_INT_STATUS, reg,
+ reg & SDHCI_INT_CARD_INSERT,
+ 50 * USEC_PER_MSEC);
+ if (ret == 0)
+ is_inserted = true;
+exit:
+ status_mask &= ~SDHCI_INT_CARD_INSERT;
+ sdhci_write32(sdhci, SDHCI_INT_ENABLE, status_mask);
+
+ status_mask = sdhci_read32(sdhci, SDHCI_INT_STATUS);
+ status_mask |= SDHCI_INT_CARD_INSERT;
+ sdhci_write32(sdhci, SDHCI_INT_STATUS, status_mask);
+
+ return is_inserted;
+}
+
+int at91_sdhci_send_command(struct at91_sdhci *host, struct mci_cmd *cmd,
+ struct mci_data *data)
+{
+ unsigned command, xfer;
+ struct sdhci *sdhci = &host->sdhci;
+ u32 mask;
+ int status;
+ int ret;
+
+ ret = sdhci_wait_idle_data(&host->sdhci, cmd);
+ if (ret)
+ return ret;
+
+ sdhci_write32(sdhci, SDHCI_INT_STATUS, ~0U);
+
+ mask = SDHCI_INT_CMD_COMPLETE;
+
+ sdhci_set_cmd_xfer_mode(sdhci, cmd, data, false, &command, &xfer);
+
+ if (data) {
+ sdhci_write8(sdhci, SDHCI_TIMEOUT_CONTROL, 0xe);
+ sdhci_write16(sdhci, SDHCI_BLOCK_SIZE,
+ SDHCI_DMA_BOUNDARY_512K
+ | SDHCI_TRANSFER_BLOCK_SIZE(data->blocksize));
+ sdhci_write16(sdhci, SDHCI_BLOCK_COUNT, data->blocks);
+ sdhci_write16(sdhci, SDHCI_TRANSFER_MODE, xfer);
+ if (cmd->resp_type & MMC_RSP_BUSY)
+ mask |= SDHCI_INT_XFER_COMPLETE;
+ } else if (cmd->resp_type & MMC_RSP_BUSY) {
+ sdhci_write16(sdhci, SDHCI_TIMEOUT_CONTROL, 0xe);
+ }
+
+ sdhci_write32(sdhci, SDHCI_ARGUMENT, cmd->cmdarg);
+ sdhci_write16(sdhci, SDHCI_COMMAND, command);
+
+ status = sdhci_wait_for_done(&host->sdhci, mask);
+ if (status < 0)
+ goto error;
+
+ sdhci_read_response(sdhci, cmd);
+ sdhci_write32(sdhci, SDHCI_INT_STATUS, mask);
+
+ if (data)
+ sdhci_transfer_data_pio(sdhci, data);
+
+ udelay(1000);
+
+ status = sdhci_read32(sdhci, SDHCI_INT_STATUS);
+ sdhci_write32(sdhci, SDHCI_INT_STATUS, ~0U);
+
+ return 0;
+
+error:
+ sdhci_write32(sdhci, SDHCI_INT_STATUS, ~0U);
+
+ sdhci_reset(sdhci, SDHCI_RESET_CMD);
+ sdhci_reset(sdhci, SDHCI_RESET_DATA);
+
+ return status;
+}
+
+static void at91_sdhci_set_power(struct at91_sdhci *host, unsigned vdd)
+{
+ struct sdhci *sdhci = &host->sdhci;
+ u8 pwr = 0;
+
+ switch (vdd) {
+ case MMC_VDD_165_195:
+ pwr = SDHCI_POWER_180;
+ break;
+ case MMC_VDD_29_30:
+ case MMC_VDD_30_31:
+ pwr = SDHCI_POWER_300;
+ break;
+ case MMC_VDD_32_33:
+ case MMC_VDD_33_34:
+ pwr = SDHCI_POWER_330;
+ break;
+ }
+
+ if (pwr == 0) {
+ sdhci_write8(sdhci, SDHCI_POWER_CONTROL, 0);
+ return;
+ }
+
+ pwr |= SDHCI_BUS_POWER_EN;
+
+ sdhci_write8(sdhci, SDHCI_POWER_CONTROL, pwr);
+}
+
+static int at91_sdhci_set_clock(struct at91_sdhci *host, unsigned clock)
+{
+
+ struct sdhci *sdhci = &host->sdhci;
+ unsigned clk = 0, clk_div;
+ unsigned reg;
+ u32 caps, caps_clk_mult;
+ int ret;
+
+ ret = sdhci_wait_idle_data(&host->sdhci, NULL);
+ if (ret)
+ return ret;
+
+ sdhci_write16(sdhci, SDHCI_CLOCK_CONTROL, 0);
+
+ if (clock == 0)
+ return 0;
+
+ caps = sdhci_read32(sdhci, SDHCI_CAPABILITIES_1);
+
+ caps_clk_mult = FIELD_GET(SDHCI_CLOCK_MUL_MASK, caps);
+
+ if (caps_clk_mult) {
+ for (clk_div = 1; clk_div <= 1024; clk_div++) {
+ if (host->caps_max_clock / clk_div <= clock)
+ break;
+ }
+ clk = SDHCI_PROG_CLOCK_MODE;
+ clk_div -= 1;
+ } else {
+ /* Version 3.00 divisors must be a multiple of 2. */
+ if (host->caps_max_clock <= clock) {
+ clk_div = 1;
+ } else {
+ for (clk_div = 2; clk_div < 2048; clk_div += 2) {
+ if (host->caps_max_clock / clk_div <= clock)
+ break;
+ }
+ }
+ clk_div >>= 1;
+ }
+
+ clk |= SDHCI_FREQ_SEL(clk_div);
+ clk |= ((clk_div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
+ << SDHCI_DIVIDER_HI_SHIFT;
+ clk |= SDHCI_CLOCK_INT_EN;
+
+ sdhci_write16(sdhci, SDHCI_CLOCK_CONTROL, clk);
+
+ ret = sdhci_read32_poll_timeout(sdhci, SDHCI_CLOCK_CONTROL, clk,
+ clk & SDHCI_CLOCK_INT_STABLE,
+ 20 * USEC_PER_MSEC);
+ if (ret) {
+ dev_warn(host->dev, "Timeout waiting for clock stable\n");
+ return ret;
+ }
+
+ clk |= SDHCI_CLOCK_CARD_EN;
+ sdhci_write16(sdhci, SDHCI_CLOCK_CONTROL, clk);
+
+ reg = sdhci_read8(sdhci, SDHCI_HOST_CONTROL);
+ if (clock > 26000000)
+ reg |= SDHCI_CTRL_HISPD;
+ else
+ reg &= ~SDHCI_CTRL_HISPD;
+
+ sdhci_write8(sdhci, SDHCI_HOST_CONTROL, reg);
+
+ return 0;
+}
+
+static int at91_sdhci_set_bus_width(struct at91_sdhci *host, unsigned bus_width)
+{
+ struct sdhci *sdhci = &host->sdhci;
+ unsigned reg;
+
+ reg = sdhci_read8(sdhci, SDHCI_HOST_CONTROL);
+
+ switch(bus_width) {
+ case MMC_BUS_WIDTH_8:
+ reg |= SDHCI_CTRL_8BITBUS;
+ break;
+ case MMC_BUS_WIDTH_4:
+ reg &= ~SDHCI_CTRL_8BITBUS;
+ reg |= SDHCI_CTRL_4BITBUS;
+ break;
+ default:
+ reg &= ~SDHCI_CTRL_8BITBUS;
+ reg &= ~SDHCI_CTRL_4BITBUS;
+ }
+
+ sdhci_write8(sdhci, SDHCI_HOST_CONTROL, reg);
+
+ return 0;
+}
+
+int at91_sdhci_set_ios(struct at91_sdhci *host, struct mci_ios *ios)
+{
+ int ret;
+
+ ret = at91_sdhci_set_clock(host, ios->clock);
+ if (ret)
+ return ret;
+
+ return at91_sdhci_set_bus_width(host, ios->bus_width);
+}
+
+int at91_sdhci_init(struct at91_sdhci *host, u32 maxclk,
+ bool force_cd, bool cal_always_on)
+{
+ struct sdhci *sdhci = &host->sdhci;
+ unsigned status_mask;
+
+ host->caps_max_clock = maxclk;
+
+ at91_sdhci_set_power(host, MMC_VDD_32_33);
+
+ status_mask = SDHCI_INT_CMD_COMPLETE
+ | SDHCI_INT_XFER_COMPLETE
+ | SDHCI_INT_SPACE_AVAIL
+ | SDHCI_INT_DATA_AVAIL;
+
+ status_mask |= SDHCI_INT_TIMEOUT
+ | SDHCI_INT_CRC
+ | SDHCI_INT_END_BIT
+ | SDHCI_INT_INDEX
+ | SDHCI_INT_DATA_TIMEOUT
+ | SDHCI_INT_DATA_CRC
+ | SDHCI_INT_DATA_END_BIT;
+
+ sdhci_write32(sdhci, SDHCI_INT_ENABLE, status_mask);
+
+ sdhci_write32(sdhci, SDHCI_SIGNAL_ENABLE, 0);
+
+ /*
+ * If the device attached to the mci bus is not removable, it is safer
+ * to set the Force Card Detect bit. People often don't connect the
+ * card detect signal and use this pin for another purpose. If the card
+ * detect pin is not muxed to SDHCI controller, a default value is
+ * used. This value can be different from a SoC revision to another
+ * one. Problems come when this default value is not card present. To
+ * avoid this case, if the device is non removable then the card
+ * detection procedure using the SDMCC_CD signal is bypassed.
+ * This bit is reset when a software reset for all command is performed
+ * so we need to implement our own reset function to set back this bit.
+ */
+ if (force_cd) {
+ u8 mc1r = sdhci_read8(sdhci, AT91_SDHCI_MC1R);
+ mc1r |= AT91_SDHCI_MC1_FCD;
+ sdhci_write8(sdhci, AT91_SDHCI_MC1R, mc1r);
+ }
+
+ if (cal_always_on) {
+ sdhci_write32(sdhci, AT91_SDHCI_CALCR,
+ AT91_SDHCI_CALCR_ALWYSON | AT91_SDHCI_CALCR_EN);
+ }
+
+ return 0;
+}
+
+void at91_sdhci_mmio_init(struct at91_sdhci *host, void __iomem *base)
+{
+ host->sdhci.base = base;
+}
diff --git a/drivers/mci/atmel-sdhci-pbl.c b/drivers/mci/atmel-sdhci-pbl.c
new file mode 100644
index 0000000000..f5a7279bff
--- /dev/null
+++ b/drivers/mci/atmel-sdhci-pbl.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: BSD-1-Clause
+/*
+ * Copyright (c) 2015, Atmel Corporation
+ * Copyright (c) 2019, Ahmad Fatoum, Pengutronix
+ *
+ * Atmel's name may not be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ */
+
+#include <common.h>
+#include <pbl/bio.h>
+#include <mci.h>
+#include <debug_ll.h>
+#include <mach/at91/xload.h>
+#include "atmel-sdhci.h"
+
+#include <mach/at91/early_udelay.h>
+
+#ifdef __PBL__
+#define udelay early_udelay
+#endif
+
+#define SECTOR_SIZE 512
+#define SUPPORT_MAX_BLOCKS 16U
+
+struct at91_sdhci_priv {
+ struct at91_sdhci host;
+ bool highcapacity_card;
+};
+
+static int sd_cmd_stop_transmission(struct at91_sdhci_priv *priv)
+{
+ struct mci_cmd cmd = {
+ .cmdidx = MMC_CMD_STOP_TRANSMISSION,
+ .resp_type = MMC_RSP_R1b,
+ };
+
+ return at91_sdhci_send_command(&priv->host, &cmd, NULL);
+}
+
+static int sd_cmd_read_multiple_block(struct at91_sdhci_priv *priv,
+ void *buf,
+ unsigned int start,
+ unsigned int block_count)
+{
+ u16 block_len = SECTOR_SIZE;
+ struct mci_data data;
+ struct mci_cmd cmd = {
+ .cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK,
+ .resp_type = MMC_RSP_R1,
+ .cmdarg = start,
+ };
+
+ if (!priv->highcapacity_card)
+ cmd.cmdarg *= block_len;
+
+ data.dest = buf;
+ data.flags = MMC_DATA_READ;
+ data.blocksize = block_len;
+ data.blocks = block_count;
+
+ return at91_sdhci_send_command(&priv->host, &cmd, &data);
+}
+
+static int at91_sdhci_bio_read(struct pbl_bio *bio, off_t start,
+ void *buf, unsigned int nblocks)
+{
+ struct at91_sdhci_priv *priv = bio->priv;
+ unsigned int blocks_done = 0;
+ unsigned int blocks;
+ unsigned int block_len = SECTOR_SIZE;
+ unsigned int blocks_read;
+ int ret;
+
+ /*
+ * Refer to the at91sam9g20 datasheet:
+ * Figure 35-10. Read Function Flow Diagram
+ */
+
+ while (blocks_done < nblocks) {
+ blocks = min(nblocks - blocks_done, SUPPORT_MAX_BLOCKS);
+
+ blocks_read = sd_cmd_read_multiple_block(priv, buf,
+ start + blocks_done,
+ blocks);
+
+ ret = sd_cmd_stop_transmission(priv);
+ if (ret)
+ return ret;
+
+ blocks_done += blocks_read;
+
+ if (blocks_read != blocks)
+ break;
+
+ buf += blocks * block_len;
+ }
+
+ return blocks_done;
+}
+
+static struct at91_sdhci_priv atmel_sdcard;
+
+int at91_sdhci_bio_init(struct pbl_bio *bio, void __iomem *base)
+{
+ struct at91_sdhci_priv *priv = &atmel_sdcard;
+ struct at91_sdhci *host = &priv->host;
+ struct mci_ios ios = { .bus_width = MMC_BUS_WIDTH_1, .clock = 25000000 };
+ int ret;
+
+ bio->priv = priv;
+ bio->read = at91_sdhci_bio_read;
+
+ at91_sdhci_mmio_init(host, base);
+
+ sdhci_reset(&host->sdhci, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+
+ ret = at91_sdhci_init(host, 240000000, true, true);
+ if (ret)
+ return ret;
+
+ ret = at91_sdhci_set_ios(host, &ios);
+
+ // FIXME can we determine this without leaving SD transfer mode?
+ priv->highcapacity_card = 1;
+
+ return 0;
+}
diff --git a/drivers/mci/atmel-sdhci.c b/drivers/mci/atmel-sdhci.c
new file mode 100644
index 0000000000..c124e736bb
--- /dev/null
+++ b/drivers/mci/atmel-sdhci.c
@@ -0,0 +1,172 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Atmel SDMMC controller driver.
+ *
+ * Copyright (C) 2015 Atmel,
+ * 2015 Ludovic Desroches <ludovic.desroches@atmel.com>
+ * 2020 Ahmad Fatoum <a.fatoum@pengutronix.de>
+ */
+
+#include <common.h>
+#include <init.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <of.h>
+#include <mci.h>
+
+#include "atmel-sdhci.h"
+
+#define ATMEL_SDHC_MIN_FREQ 400000
+#define ATMEL_SDHC_GCK_RATE 240000000
+
+struct at91_sdhci_priv {
+ struct at91_sdhci host;
+ struct mci_host mci;
+ struct clk *hclock, *gck, *mainck;
+ bool cal_always_on;
+ int gck_rate;
+};
+
+#define to_priv(h) container_of(h, struct at91_sdhci_priv, mci)
+
+static int at91_sdhci_mci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
+ struct mci_data *data)
+{
+ return at91_sdhci_send_command(&to_priv(mci)->host, cmd, data);
+}
+
+static void at91_sdhci_mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
+{
+ at91_sdhci_set_ios(&to_priv(mci)->host, ios);
+}
+
+static int at91_sdhci_mci_init(struct mci_host *mci, struct device *dev)
+{
+ struct at91_sdhci_priv *priv = to_priv(mci);
+ struct sdhci *sdhci = &priv->host.sdhci;
+ int ret;
+
+ priv->host.dev = dev;
+
+ ret = sdhci_reset(sdhci, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+ if (ret)
+ return ret;
+
+ return at91_sdhci_init(&priv->host, priv->gck_rate,
+ priv->mci.non_removable, priv->cal_always_on);
+}
+
+static int at91_sdhci_conf_clks(struct at91_sdhci_priv *priv)
+{
+ unsigned long real_gck_rate;
+ int ret;
+
+ /*
+ * The mult clock is provided by as a generated clock by the PMC
+ * controller. In order to set the rate of gck, we have to get the
+ * base clock rate and the clock mult from capabilities.
+ */
+ clk_enable(priv->hclock);
+ ret = clk_set_rate(priv->gck, ATMEL_SDHC_GCK_RATE);
+ if (ret < 0) {
+ clk_disable(priv->hclock);
+ return ret;
+ }
+
+ real_gck_rate = clk_get_rate(priv->gck);
+
+ clk_enable(priv->mainck);
+ clk_enable(priv->gck);
+
+ return clamp_t(int, real_gck_rate, ATMEL_SDHC_MIN_FREQ, INT_MAX);
+}
+
+static void at91_sdhci_set_mci_caps(struct at91_sdhci_priv *priv)
+{
+ struct mci_host *mci = &priv->mci;
+ at91_sdhci_host_capability(&priv->host, &mci->voltages);
+
+ if (mci->f_max >= 26000000)
+ mci->host_caps |= MMC_CAP_MMC_HIGHSPEED;
+ if (mci->f_max >= 52000000)
+ mci->host_caps |= MMC_CAP_MMC_HIGHSPEED_52MHZ;
+
+ mci_of_parse(mci);
+}
+
+static int at91_sdhci_card_present(struct mci_host *mci)
+{
+ return at91_sdhci_is_card_inserted(&to_priv(mci)->host);
+}
+
+static int at91_sdhci_probe(struct device *dev)
+{
+ struct at91_sdhci_priv *priv;
+ struct resource *iores;
+
+ priv = xzalloc(sizeof(*priv));
+ dev->priv = priv;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores)) {
+ dev_err(dev, "could not get iomem region\n");
+ return PTR_ERR(iores);
+ }
+
+ priv->mainck = clk_get(dev, "baseclk");
+ if (IS_ERR(priv->mainck)) {
+ dev_err(dev, "failed to get baseclk\n");
+ return PTR_ERR(priv->mainck);
+ }
+
+ priv->hclock = clk_get(dev, "hclock");
+ if (IS_ERR(priv->hclock)) {
+ dev_err(dev, "failed to get hclock\n");
+ return PTR_ERR(priv->hclock);
+ }
+
+ priv->gck = clk_get(dev, "multclk");
+ if (IS_ERR(priv->gck)) {
+ dev_err(dev, "failed to get multclk\n");
+ return PTR_ERR(priv->gck);
+ }
+
+ /*
+ * if SDCAL pin is wrongly connected, we must enable
+ * the analog calibration cell permanently.
+ */
+ priv->cal_always_on = of_property_read_bool(dev->of_node,
+ "microchip,sdcal-inverted");
+
+ at91_sdhci_mmio_init(&priv->host, IOMEM(iores->start));
+
+ priv->gck_rate = at91_sdhci_conf_clks(priv);
+ if (priv->gck_rate < 0)
+ return priv->gck_rate;
+
+ priv->mci.hw_dev = dev;
+ priv->mci.send_cmd = at91_sdhci_mci_send_cmd;
+ priv->mci.set_ios = at91_sdhci_mci_set_ios;
+ priv->mci.init = at91_sdhci_mci_init;
+ priv->mci.f_max = priv->gck_rate;
+ priv->mci.f_min = ATMEL_SDHC_MIN_FREQ;
+ priv->mci.card_present = at91_sdhci_card_present;
+
+ at91_sdhci_set_mci_caps(priv);
+
+ return mci_register(&priv->mci);
+}
+
+static const struct of_device_id at91_sdhci_dt_match[] = {
+ { .compatible = "atmel,sama5d2-sdhci" },
+ { .compatible = "microchip,sam9x60-sdhci" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, at91_sdhci_dt_match);
+
+static struct driver at91_sdhci_driver = {
+ .name = "sdhci-at91",
+ .of_compatible = DRV_OF_COMPAT(at91_sdhci_dt_match),
+ .probe = at91_sdhci_probe,
+};
+device_platform_driver(at91_sdhci_driver);
diff --git a/drivers/mci/atmel-sdhci.h b/drivers/mci/atmel-sdhci.h
new file mode 100644
index 0000000000..8f07de340d
--- /dev/null
+++ b/drivers/mci/atmel-sdhci.h
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// Copyright (c) 2020 Ahmad Fatoum, Pengutronix
+
+#ifndef ATMEL_SDHCI_H_
+#define ATMEL_SDHCI_H_
+
+#include <linux/types.h>
+#include <mci.h>
+
+#include "sdhci.h"
+
+struct at91_sdhci {
+ struct sdhci sdhci;
+ struct device *dev;
+ void __iomem *base;
+ u32 caps_max_clock;
+};
+
+int at91_sdhci_init(struct at91_sdhci *host, u32 maxclk,
+ bool force_cd, bool cal_always_on);
+void at91_sdhci_mmio_init(struct at91_sdhci *host, void __iomem *base);
+int at91_sdhci_send_command(struct at91_sdhci *host, struct mci_cmd *sd_cmd,
+ struct mci_data *data);
+bool at91_sdhci_is_card_inserted(struct at91_sdhci *host);
+void at91_sdhci_host_capability(struct at91_sdhci *host,
+ unsigned int *voltages);
+int at91_sdhci_set_ios(struct at91_sdhci *host, struct mci_ios *ios);
+
+#endif
diff --git a/drivers/mci/atmel_mci.c b/drivers/mci/atmel_mci.c
index 0d3b245ced..9021dba0f8 100644
--- a/drivers/mci/atmel_mci.c
+++ b/drivers/mci/atmel_mci.c
@@ -1,477 +1,37 @@
-/*
- * Atmel MCI driver
- *
- * Copyright (C) 2011 Hubert Feurstein <h.feurstein@gmail.com>
- *
- * based on imx.c by:
- * Copyright (C) 2009 Ilya Yanok, <yanok@emcraft.com>
- * Copyright (C) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
- * Copyright (C) 2006 Pavel Pisa, PiKRON <ppisa@pikron.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- */
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2011 Hubert Feurstein <h.feurstein@gmail.com>
+// SPDX-FileCopyrightText: 2009 Ilya Yanok <yanok@emcraft.com>
+// SPDX-FileCopyrightText: 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+// SPDX-FileCopyrightText: 2006 Pavel Pisa <ppisa@pikron.com>, PiKRON
+
+/* Atmel MCI driver */
#include <common.h>
-#include <init.h>
-#include <mci.h>
-#include <errno.h>
-#include <clock.h>
#include <gpio.h>
-#include <io.h>
-#include <mach/board.h>
#include <linux/clk.h>
-#include <linux/err.h>
+#include <mci.h>
#include <of_gpio.h>
+#include <platform_data/atmel-mci.h>
#include "atmel-mci-regs.h"
-struct atmel_mci_caps {
- bool has_cfg_reg;
- bool has_highspeed;
- bool has_rwproof;
- bool has_odd_clk_div;
- bool need_reset_after_xfer;
-};
-
-struct atmel_mci {
- struct mci_host mci;
- void __iomem *regs;
- struct device_d *hw_dev;
- struct clk *clk;
-
- u32 datasize;
- struct mci_cmd *cmd;
- struct mci_data *data;
- unsigned slot_b;
- int version;
- struct atmel_mci_caps caps;
-
- unsigned long bus_hz;
- u32 mode_reg;
- u32 cfg_reg;
- u32 sdc_reg;
- bool need_reset;
- int detect_pin;
-};
-
-#define to_mci_host(mci) container_of(mci, struct atmel_mci, mci)
-
-#define STATUS_ERROR_MASK (ATMCI_RINDE \
- | ATMCI_RDIRE \
- | ATMCI_RCRCE \
- | ATMCI_RENDE \
- | ATMCI_RTOE \
- | ATMCI_DCRCE \
- | ATMCI_DTOE \
- | ATMCI_OVRE \
- | ATMCI_UNRE)
-
-static void atmci_set_clk_rate(struct atmel_mci *host,
- unsigned int clock_min)
-{
- unsigned int clkdiv;
-
- if (!host->mode_reg) {
- clk_enable(host->clk);
- atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIEN);
- if (host->caps.has_cfg_reg)
- atmci_writel(host, ATMCI_CFG, host->cfg_reg);
- }
-
- if (host->caps.has_odd_clk_div) {
- clkdiv = DIV_ROUND_UP(host->bus_hz, clock_min) - 2;
- if (clkdiv > 511) {
- dev_dbg(host->hw_dev,
- "clock %u too slow; using %lu\n",
- clock_min, host->bus_hz / (511 + 2));
- clkdiv = 511;
- }
- host->mode_reg = ATMCI_MR_CLKDIV(clkdiv >> 1)
- | ATMCI_MR_CLKODD(clkdiv & 1);
- } else {
- clkdiv = DIV_ROUND_UP(host->bus_hz, 2 * clock_min) - 1;
- if (clkdiv > 255) {
- dev_dbg(host->hw_dev,
- "clock %u too slow; using %lu\n",
- clock_min, host->bus_hz / (2 * 256));
- clkdiv = 255;
- }
- host->mode_reg = ATMCI_MR_CLKDIV(clkdiv);
- }
-
- dev_dbg(host->hw_dev, "atmel_set_clk_rate: clkIn=%ld clkIos=%d divider=%d\n",
- host->bus_hz, clock_min, clkdiv);
-
- /*
- * WRPROOF and RDPROOF prevent overruns/underruns by
- * stopping the clock when the FIFO is full/empty.
- * This state is not expected to last for long.
- */
- if (host->caps.has_rwproof)
- host->mode_reg |= ATMCI_MR_RDPROOF | ATMCI_MR_WRPROOF;
-
- atmci_writel(host, ATMCI_MR, host->mode_reg);
-}
-
-static int atmci_poll_status(struct atmel_mci *host, u32 mask)
-{
- u32 stat;
- uint64_t start = get_time_ns();
-
- do {
- stat = atmci_readl(host, ATMCI_SR);
- if (stat & STATUS_ERROR_MASK)
- return stat;
- if (is_timeout(start, SECOND)) {
- dev_err(host->hw_dev, "timeout\n");
- host->need_reset = true;
- return ATMCI_RTOE | stat;
- }
- if (stat & mask)
- return 0;
- } while (1);
-}
-
-static int atmci_pull(struct atmel_mci *host, void *_buf, int bytes)
-{
- unsigned int stat;
- u32 *buf = _buf;
-
- while (bytes > 3) {
- stat = atmci_poll_status(host, ATMCI_RXRDY);
- if (stat)
- return stat;
-
- *buf++ = atmci_readl(host, ATMCI_RDR);
- bytes -= 4;
- }
-
- if (WARN_ON(bytes))
- return -EIO;
-
- return 0;
-}
-
-#ifdef CONFIG_MCI_WRITE
-static int atmci_push(struct atmel_mci *host, const void *_buf, int bytes)
-{
- unsigned int stat;
- const u32 *buf = _buf;
-
- while (bytes > 3) {
- stat = atmci_poll_status(host, ATMCI_TXRDY);
- if (stat)
- return stat;
-
- atmci_writel(host, ATMCI_TDR, *buf++);
- bytes -= 4;
- }
-
- stat = atmci_poll_status(host, ATMCI_TXRDY);
- if (stat)
- return stat;
-
- if (WARN_ON(bytes))
- return -EIO;
-
- return 0;
-}
-#endif /* CONFIG_MCI_WRITE */
-
-static int atmci_transfer_data(struct atmel_mci *host)
-{
- struct mci_data *data = host->data;
- int stat;
- unsigned long length;
-
- length = data->blocks * data->blocksize;
- host->datasize = 0;
-
- if (data->flags & MMC_DATA_READ) {
- stat = atmci_pull(host, data->dest, length);
- if (stat)
- return stat;
-
- stat = atmci_poll_status(host, ATMCI_NOTBUSY);
- if (stat)
- return stat;
-
- host->datasize += length;
- } else {
-#ifdef CONFIG_MCI_WRITE
- stat = atmci_push(host, (const void *)(data->src), length);
- if (stat)
- return stat;
-
- host->datasize += length;
- stat = atmci_poll_status(host, ATMCI_NOTBUSY);
- if (stat)
- return stat;
-#endif /* CONFIG_MCI_WRITE */
- }
- return 0;
-}
-
-static void atmci_finish_request(struct atmel_mci *host)
-{
- host->cmd = NULL;
- host->data = NULL;
-}
-
-static int atmci_finish_data(struct atmel_mci *host, unsigned int stat)
-{
- int data_error = 0;
-
- if (stat & STATUS_ERROR_MASK) {
- dev_err(host->hw_dev, "request failed (status=0x%08x)\n", stat);
- if (stat & ATMCI_DCRCE)
- data_error = -EILSEQ;
- else if (stat & (ATMCI_RTOE | ATMCI_DTOE))
- data_error = -ETIMEDOUT;
- else
- data_error = -EIO;
- }
-
- host->data = NULL;
-
- return data_error;
-}
-
-static void atmci_setup_data(struct atmel_mci *host, struct mci_data *data)
-{
- unsigned int nob = data->blocks;
- unsigned int blksz = data->blocksize;
- unsigned int datasize = nob * blksz;
-
- BUG_ON(data->blocksize & 3);
- BUG_ON(nob == 0);
-
- host->data = data;
-
- dev_dbg(host->hw_dev, "atmel_setup_data: nob=%d blksz=%d\n",
- nob, blksz);
-
- atmci_writel(host, ATMCI_BLKR, ATMCI_BCNT(nob)
- | ATMCI_BLKLEN(blksz));
-
- host->datasize = datasize;
-}
-
-static int atmci_read_response(struct atmel_mci *host, unsigned int stat)
-{
- struct mci_cmd *cmd = host->cmd;
- int i;
- u32 *resp;
-
- if (!cmd)
- return 0;
-
- resp = (u32 *)cmd->response;
-
- if (stat & (ATMCI_RTOE | ATMCI_DTOE)) {
- dev_err(host->hw_dev, "command/data timeout\n");
- return -ETIMEDOUT;
- } else if ((stat & ATMCI_RCRCE) && (cmd->resp_type & MMC_RSP_CRC)) {
- dev_err(host->hw_dev, "cmd crc error\n");
- return -EILSEQ;
- }
-
- if (cmd->resp_type & MMC_RSP_PRESENT) {
- if (cmd->resp_type & MMC_RSP_136) {
- for (i = 0; i < 4; i++)
- resp[i] = atmci_readl(host, ATMCI_RSPR);
- } else {
- resp[0] = atmci_readl(host, ATMCI_RSPR);
- }
- }
-
- return 0;
-}
-
-static int atmci_cmd_done(struct atmel_mci *host, unsigned int stat)
-{
- int datastat;
- int ret;
-
- ret = atmci_read_response(host, stat);
-
- if (ret) {
- atmci_finish_request(host);
- return ret;
- }
-
- if (!host->data) {
- atmci_finish_request(host);
- return 0;
- }
-
- datastat = atmci_transfer_data(host);
- ret = atmci_finish_data(host, datastat);
- atmci_finish_request(host);
- return ret;
-}
-
-static int atmci_start_cmd(struct atmel_mci *host, struct mci_cmd *cmd,
- unsigned int cmdat)
-{
- unsigned flags = 0;
- unsigned cmdval = 0;
-
- if (host->cmd != NULL)
- dev_err(host->hw_dev, "error!\n");
-
- if ((atmci_readl(host, ATMCI_SR) & ATMCI_CMDRDY) == 0) {
- dev_err(host->hw_dev, "mci not ready!\n");
- return -EBUSY;
- }
-
- host->cmd = cmd;
- cmdval = ATMCI_CMDR_CMDNB_MASK & cmd->cmdidx;
-
- switch (cmd->resp_type) {
- case MMC_RSP_R1: /* short CRC, OPCODE */
- case MMC_RSP_R1b:/* short CRC, OPCODE, BUSY */
- flags |= ATMCI_CMDR_RSPTYP_48BIT;
- break;
- case MMC_RSP_R2: /* long 136 bit + CRC */
- flags |= ATMCI_CMDR_RSPTYP_136BIT;
- break;
- case MMC_RSP_R3: /* short */
- flags |= ATMCI_CMDR_RSPTYP_48BIT;
- break;
- case MMC_RSP_NONE:
- flags |= ATMCI_CMDR_RSPTYP_NONE;
- break;
- default:
- dev_err(host->hw_dev, "unhandled response type 0x%x\n",
- cmd->resp_type);
- return -EINVAL;
- }
- cmdval |= ATMCI_CMDR_RSPTYP & flags;
- cmdval |= cmdat & ~(ATMCI_CMDR_CMDNB_MASK | ATMCI_CMDR_RSPTYP);
-
- atmci_writel(host, ATMCI_ARGR, cmd->cmdarg);
- atmci_writel(host, ATMCI_CMDR, cmdval);
-
- return 0;
-}
-
-static int atmci_card_present(struct mci_host *mci)
-{
- struct atmel_mci *host = to_mci_host(mci);
- int ret;
-
- /* No gpio, assume card is present */
- if (!gpio_is_valid(host->detect_pin))
- return 1;
-
- ret = gpio_get_value(host->detect_pin);
-
- return ret == 0 ? 1 : 0;
-}
-
-/** init the host interface */
-static int atmci_reset(struct mci_host *mci, struct device_d *mci_dev)
-{
- struct atmel_mci *host = to_mci_host(mci);
-
- clk_enable(host->clk);
- atmci_writel(host, ATMCI_DTOR, 0x7f);
- clk_disable(host->clk);
-
- return 0;
-}
-
/** change host interface settings */
static void atmci_set_ios(struct mci_host *mci, struct mci_ios *ios)
{
struct atmel_mci *host = to_mci_host(mci);
- dev_dbg(host->hw_dev, "atmel_mci_set_ios: bus_width=%d clk=%d\n",
- ios->bus_width, ios->clock);
-
- host->sdc_reg &= ~ATMCI_SDCBUS_MASK;
- switch (ios->bus_width) {
- case MMC_BUS_WIDTH_4:
- host->sdc_reg |= ATMCI_SDCBUS_4BIT;
- break;
- case MMC_BUS_WIDTH_8:
- host->sdc_reg |= ATMCI_SDCBUS_8BIT;
- break;
- case MMC_BUS_WIDTH_1:
- host->sdc_reg |= ATMCI_SDCBUS_1BIT;
- break;
- default:
- return;
- }
-
- if (ios->clock) {
- atmci_set_clk_rate(host, ios->clock);
-
- if (host->caps.has_cfg_reg) {
- /* setup High Speed mode in relation with card capacity */
- if (ios->timing == MMC_TIMING_SD_HS)
- host->cfg_reg |= ATMCI_CFG_HSMODE;
- else
- host->cfg_reg &= ~ATMCI_CFG_HSMODE;
-
- atmci_writel(host, ATMCI_CFG, host->cfg_reg);
- }
- } else {
- atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIDIS);
- if (host->mode_reg) {
- atmci_readl(host, ATMCI_MR);
- clk_disable(host->clk);
- }
- host->mode_reg = 0;
- }
-
- return;
+ atmci_common_set_ios(host, ios);
}
-/** handle a command */
-static int atmci_request(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
+static int atmci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
+ struct mci_data *data)
{
struct atmel_mci *host = to_mci_host(mci);
- u32 stat, cmdat = 0;
- int ret;
-
- if (host->need_reset || host->caps.need_reset_after_xfer) {
- atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
- atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIEN);
- atmci_writel(host, ATMCI_MR, host->mode_reg);
- if (host->caps.has_cfg_reg)
- atmci_writel(host, ATMCI_CFG, host->cfg_reg);
- host->need_reset = false;
- }
- atmci_writel(host, ATMCI_SDCR, host->sdc_reg);
- if (cmd->resp_type != MMC_RSP_NONE)
- cmdat |= ATMCI_CMDR_MAXLAT_64CYC;
-
- if (data) {
- atmci_setup_data(host, data);
-
- cmdat |= ATMCI_CMDR_START_XFER | ATMCI_CMDR_MULTI_BLOCK;
-
- if (data->flags & MMC_DATA_READ)
- cmdat |= ATMCI_CMDR_TRDIR_READ;
- }
-
- ret = atmci_start_cmd(host, cmd, cmdat);
- if (ret) {
- atmci_finish_request(host);
- return ret;
- }
-
- stat = atmci_poll_status(host, ATMCI_CMDRDY);
- return atmci_cmd_done(host, stat);
+ return atmci_common_request(host, cmd, data);
}
-static void atmci_info(struct device_d *mci_dev)
+static void atmci_info(struct device *mci_dev)
{
struct atmel_mci *host = mci_dev->priv;
@@ -492,55 +52,31 @@ static void atmci_info(struct device_d *mci_dev)
gpio_is_valid(host->detect_pin) ? "yes" : "no");
}
-/*
- * HSMCI (High Speed MCI) module is not fully compatible with MCI module.
- * HSMCI provides DMA support and a new config register but no more supports
- * PDC.
- */
-static void atmci_get_cap(struct atmel_mci *host)
+
+static int atmci_card_present(struct mci_host *mci)
{
- unsigned int version;
-
- version = atmci_readl(host, ATMCI_VERSION) & 0x00000fff;
- host->version = version;
-
- dev_info(host->hw_dev, "version: 0x%x\n", version);
-
- host->caps.has_cfg_reg = 0;
- host->caps.has_highspeed = 0;
- host->caps.need_reset_after_xfer = 1;
-
- switch (version & 0xf00) {
- case 0x600:
- case 0x500:
- host->caps.has_odd_clk_div = 1;
- case 0x400:
- case 0x300:
- host->caps.has_cfg_reg = 1;
- host->caps.has_highspeed = 1;
- case 0x200:
- host->caps.has_rwproof = 1;
- case 0x100:
- host->caps.need_reset_after_xfer = 0;
- case 0x0:
- break;
- default:
- dev_warn(host->hw_dev,
- "Unmanaged mci version, set minimum capabilities\n");
- break;
- }
+ struct atmel_mci *host = to_mci_host(mci);
+ int ret;
+
+ /* No gpio, assume card is present */
+ if (!gpio_is_valid(host->detect_pin))
+ return 1;
+
+ ret = gpio_get_value(host->detect_pin);
+
+ return ret == 0 ? 1 : 0;
}
-static int atmci_probe(struct device_d *hw_dev)
+static int atmci_probe(struct device *hw_dev)
{
struct resource *iores;
struct atmel_mci *host;
- struct device_node *np = hw_dev->device_node;
+ struct device_node *np = hw_dev->of_node;
struct atmel_mci_platform_data *pd = hw_dev->platform_data;
int ret;
host = xzalloc(sizeof(*host));
- host->mci.send_cmd = atmci_request;
+ host->mci.send_cmd = atmci_send_cmd;
host->mci.set_ios = atmci_set_ios;
host->mci.init = atmci_reset;
host->mci.card_present = atmci_card_present;
@@ -569,8 +105,8 @@ static int atmci_probe(struct device_d *hw_dev)
for_each_child_of_node(np, cnp) {
if (of_property_read_u32(cnp, "reg", &slot_id)) {
- dev_warn(hw_dev, "reg property is missing for %s\n",
- cnp->full_name);
+ dev_warn(hw_dev, "reg property is missing for %pOF\n",
+ cnp);
continue;
}
@@ -615,6 +151,7 @@ static int atmci_probe(struct device_d *hw_dev)
clk_enable(host->clk);
atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_PWSDIS);
atmci_writel(host, ATMCI_IDR, ~0UL);
host->bus_hz = clk_get_rate(host->clk);
clk_disable(host->clk);
@@ -657,8 +194,9 @@ static __maybe_unused struct of_device_id atmci_compatible[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, atmci_compatible);
-static struct driver_d atmci_driver = {
+static struct driver atmci_driver = {
.name = "atmel_mci",
.probe = atmci_probe,
.of_compatible = DRV_OF_COMPAT(atmci_compatible),
diff --git a/drivers/mci/atmel_mci_common.c b/drivers/mci/atmel_mci_common.c
new file mode 100644
index 0000000000..7b11e9134e
--- /dev/null
+++ b/drivers/mci/atmel_mci_common.c
@@ -0,0 +1,475 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2011 Hubert Feurstein <h.feurstein@gmail.com>
+// SPDX-FileCopyrightText: 2009 Ilya Yanok <yanok@emcraft.com>
+// SPDX-FileCopyrightText: 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+// SPDX-FileCopyrightText: 2006 Pavel Pisa <ppisa@pikron.com>, PiKRON
+
+#include <common.h>
+#include <init.h>
+#include <linux/clk.h>
+#include <linux/iopoll.h>
+#include <mci.h>
+
+#include "atmel-mci-regs.h"
+
+#ifdef __PBL__
+#define udelay early_udelay
+#undef dev_err
+#define dev_err(d, ...) pr_err(__VA_ARGS__)
+#undef dev_warn
+#define dev_warn(d, ...) pr_warn(__VA_ARGS__)
+#undef dev_dbg
+#define dev_dbg(d, ...) pr_debug(__VA_ARGS__)
+#undef dev_info
+#define dev_info(d, ...) pr_info(__VA_ARGS__)
+#undef clk_enable
+#define clk_enable(...)
+#undef clk_disable
+#define clk_disable(...)
+#endif
+
+#define STATUS_ERROR_MASK (ATMCI_RINDE \
+ | ATMCI_RDIRE \
+ | ATMCI_RCRCE \
+ | ATMCI_RENDE \
+ | ATMCI_RTOE \
+ | ATMCI_DCRCE \
+ | ATMCI_DTOE \
+ | ATMCI_OVRE \
+ | ATMCI_UNRE)
+
+static void atmci_set_clk_rate(struct atmel_mci *host,
+ unsigned int clock_min)
+{
+ unsigned int clkdiv;
+
+ if (!host->mode_reg) {
+ clk_enable(host->clk);
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIEN);
+ if (host->caps.has_cfg_reg)
+ atmci_writel(host, ATMCI_CFG, host->cfg_reg);
+ }
+
+ if (host->caps.has_odd_clk_div) {
+ clkdiv = DIV_ROUND_UP(host->bus_hz, clock_min) - 2;
+ if (clkdiv > 511) {
+ dev_dbg(host->hw_dev,
+ "clock %u too slow; using %lu\n",
+ clock_min, host->bus_hz / (511 + 2));
+ clkdiv = 511;
+ }
+ host->mode_reg = ATMCI_MR_CLKDIV(clkdiv >> 1)
+ | ATMCI_MR_CLKODD(clkdiv & 1);
+ } else {
+ clkdiv = DIV_ROUND_UP(host->bus_hz, 2 * clock_min) - 1;
+ if (clkdiv > 255) {
+ dev_dbg(host->hw_dev,
+ "clock %u too slow; using %lu\n",
+ clock_min, host->bus_hz / (2 * 256));
+ clkdiv = 255;
+ }
+
+ /*
+ * Older Atmels without CLKODD have the block length
+ * in the upper 16 bits of both MCI_MR and MCI_BLKR
+ *
+ * To avoid intermittent zeroing of the block length,
+ * just hardcode 512 here and have atmci_setup_data()
+ * change it as necessary.
+ */
+
+ host->mode_reg = ATMCI_MR_CLKDIV(clkdiv) | ATMCI_BLKLEN(512);
+ }
+
+ dev_dbg(host->hw_dev, "atmel_set_clk_rate: clkIn=%ld clkIos=%d divider=%d\n",
+ host->bus_hz, clock_min, clkdiv);
+
+ /*
+ * WRPROOF and RDPROOF prevent overruns/underruns by
+ * stopping the clock when the FIFO is full/empty.
+ * This state is not expected to last for long.
+ */
+ if (host->caps.has_rwproof)
+ host->mode_reg |= ATMCI_MR_RDPROOF | ATMCI_MR_WRPROOF;
+
+ atmci_writel(host, ATMCI_MR, host->mode_reg);
+}
+
+static int atmci_poll_status(struct atmel_mci *host, u32 mask)
+{
+ u32 stat;
+ int ret;
+
+ ret = read_poll_timeout(atmci_readl, stat, (stat & mask), USEC_PER_SEC,
+ host, ATMCI_SR);
+ if (ret < 0) {
+ dev_err(host->hw_dev, "timeout\n");
+ host->need_reset = true;
+ return ATMCI_RTOE | stat;
+ }
+
+ if (stat & STATUS_ERROR_MASK)
+ return stat;
+
+ return 0;
+}
+
+static int atmci_pull(struct atmel_mci *host, void *_buf, int bytes)
+{
+ unsigned int stat;
+ u32 *buf = _buf;
+
+ while (bytes > 3) {
+ stat = atmci_poll_status(host, ATMCI_RXRDY);
+ if (stat)
+ return stat;
+
+ *buf++ = atmci_readl(host, ATMCI_RDR);
+ bytes -= 4;
+ }
+
+ if (WARN_ON(bytes))
+ return -EIO;
+
+ return 0;
+}
+
+#ifdef CONFIG_MCI_WRITE
+static int atmci_push(struct atmel_mci *host, const void *_buf, int bytes)
+{
+ unsigned int stat;
+ const u32 *buf = _buf;
+
+ while (bytes > 3) {
+ stat = atmci_poll_status(host, ATMCI_TXRDY);
+ if (stat)
+ return stat;
+
+ atmci_writel(host, ATMCI_TDR, *buf++);
+ bytes -= 4;
+ }
+
+ stat = atmci_poll_status(host, ATMCI_TXRDY);
+ if (stat)
+ return stat;
+
+ if (WARN_ON(bytes))
+ return -EIO;
+
+ return 0;
+}
+#endif /* CONFIG_MCI_WRITE */
+
+static int atmci_transfer_data(struct atmel_mci *host)
+{
+ struct mci_data *data = host->data;
+ int stat;
+ unsigned long length;
+
+ length = data->blocks * data->blocksize;
+ host->datasize = 0;
+
+ if (data->flags & MMC_DATA_READ) {
+ stat = atmci_pull(host, data->dest, length);
+ if (stat)
+ return stat;
+
+ stat = atmci_poll_status(host, ATMCI_NOTBUSY);
+ if (stat)
+ return stat;
+
+ host->datasize += length;
+ } else {
+#ifdef CONFIG_MCI_WRITE
+ stat = atmci_push(host, (const void *)(data->src), length);
+ if (stat)
+ return stat;
+
+ host->datasize += length;
+ stat = atmci_poll_status(host, ATMCI_NOTBUSY);
+ if (stat)
+ return stat;
+#endif /* CONFIG_MCI_WRITE */
+ }
+ return 0;
+}
+
+static void atmci_finish_request(struct atmel_mci *host)
+{
+ host->cmd = NULL;
+ host->data = NULL;
+}
+
+static int atmci_finish_data(struct atmel_mci *host, unsigned int stat)
+{
+ int data_error = 0;
+
+ if (stat & STATUS_ERROR_MASK) {
+ dev_err(host->hw_dev, "request failed (status=0x%08x)\n", stat);
+ if (stat & ATMCI_DCRCE)
+ data_error = -EILSEQ;
+ else if (stat & (ATMCI_RTOE | ATMCI_DTOE))
+ data_error = -ETIMEDOUT;
+ else
+ data_error = -EIO;
+ }
+
+ host->data = NULL;
+
+ return data_error;
+}
+
+static void atmci_setup_data(struct atmel_mci *host, struct mci_data *data)
+{
+ unsigned int nob = data->blocks;
+ unsigned int blksz = data->blocksize;
+ unsigned int datasize = nob * blksz;
+
+ BUG_ON(data->blocksize & 3);
+ BUG_ON(nob == 0);
+
+ host->data = data;
+
+ dev_vdbg(host->hw_dev, "atmel_setup_data: nob=%d blksz=%d\n",
+ nob, blksz);
+
+ atmci_writel(host, ATMCI_BLKR, ATMCI_BCNT(nob)
+ | ATMCI_BLKLEN(blksz));
+
+ host->datasize = datasize;
+}
+
+static int atmci_read_response(struct atmel_mci *host, unsigned int stat)
+{
+ struct mci_cmd *cmd = host->cmd;
+ int i;
+ u32 *resp;
+
+ if (!cmd)
+ return 0;
+
+ resp = (u32 *)cmd->response;
+
+ if (stat & (ATMCI_RTOE | ATMCI_DTOE)) {
+ dev_err(host->hw_dev, "command/data timeout\n");
+ return -ETIMEDOUT;
+ } else if ((stat & ATMCI_RCRCE) && (cmd->resp_type & MMC_RSP_CRC)) {
+ dev_err(host->hw_dev, "cmd crc error\n");
+ return -EILSEQ;
+ }
+
+ if (cmd->resp_type & MMC_RSP_PRESENT) {
+ if (cmd->resp_type & MMC_RSP_136) {
+ for (i = 0; i < 4; i++)
+ resp[i] = atmci_readl(host, ATMCI_RSPR);
+ } else {
+ resp[0] = atmci_readl(host, ATMCI_RSPR);
+ }
+ }
+
+ return 0;
+}
+
+static int atmci_cmd_done(struct atmel_mci *host, unsigned int stat)
+{
+ int datastat;
+ int ret;
+
+ ret = atmci_read_response(host, stat);
+
+ if (ret) {
+ atmci_finish_request(host);
+ return ret;
+ }
+
+ if (!host->data) {
+ atmci_finish_request(host);
+ return 0;
+ }
+
+ datastat = atmci_transfer_data(host);
+ ret = atmci_finish_data(host, datastat);
+ atmci_finish_request(host);
+ return ret;
+}
+
+static int atmci_start_cmd(struct atmel_mci *host, struct mci_cmd *cmd,
+ unsigned int cmdat)
+{
+ unsigned flags = 0;
+ unsigned cmdval = 0;
+
+ if (host->cmd != NULL)
+ dev_err(host->hw_dev, "error!\n");
+
+ if ((atmci_readl(host, ATMCI_SR) & ATMCI_CMDRDY) == 0) {
+ dev_err(host->hw_dev, "mci not ready!\n");
+ return -EBUSY;
+ }
+
+ host->cmd = cmd;
+ cmdval = ATMCI_CMDR_CMDNB_MASK & cmd->cmdidx;
+
+ switch (cmd->resp_type) {
+ case MMC_RSP_R1: /* short CRC, OPCODE */
+ case MMC_RSP_R1b:/* short CRC, OPCODE, BUSY */
+ flags |= ATMCI_CMDR_RSPTYP_48BIT;
+ break;
+ case MMC_RSP_R2: /* long 136 bit + CRC */
+ flags |= ATMCI_CMDR_RSPTYP_136BIT;
+ break;
+ case MMC_RSP_R3: /* short */
+ flags |= ATMCI_CMDR_RSPTYP_48BIT;
+ break;
+ case MMC_RSP_NONE:
+ flags |= ATMCI_CMDR_RSPTYP_NONE;
+ break;
+ default:
+ dev_dbg(host->hw_dev, "unhandled response type 0x%x\n",
+ cmd->resp_type);
+ return -EINVAL;
+ }
+ cmdval |= ATMCI_CMDR_RSPTYP & flags;
+ cmdval |= cmdat & ~(ATMCI_CMDR_CMDNB_MASK | ATMCI_CMDR_RSPTYP);
+
+ atmci_writel(host, ATMCI_ARGR, cmd->cmdarg);
+ atmci_writel(host, ATMCI_CMDR, cmdval);
+
+ return 0;
+}
+
+/** init the host interface */
+int atmci_reset(struct mci_host *mci, struct device *mci_dev)
+{
+ struct atmel_mci *host = to_mci_host(mci);
+
+ clk_enable(host->clk);
+ atmci_writel(host, ATMCI_DTOR, 0x7f);
+ clk_disable(host->clk);
+
+ return 0;
+}
+
+/** change host interface settings */
+void atmci_common_set_ios(struct atmel_mci *host, struct mci_ios *ios)
+{
+ dev_dbg(host->hw_dev, "atmel_mci_set_ios: bus_width=%d clk=%d\n",
+ ios->bus_width, ios->clock);
+
+ host->sdc_reg &= ~ATMCI_SDCBUS_MASK;
+ switch (ios->bus_width) {
+ case MMC_BUS_WIDTH_4:
+ host->sdc_reg |= ATMCI_SDCBUS_4BIT;
+ break;
+ case MMC_BUS_WIDTH_8:
+ host->sdc_reg |= ATMCI_SDCBUS_8BIT;
+ break;
+ case MMC_BUS_WIDTH_1:
+ host->sdc_reg |= ATMCI_SDCBUS_1BIT;
+ break;
+ default:
+ return;
+ }
+
+ if (ios->clock) {
+ atmci_set_clk_rate(host, ios->clock);
+
+ if (host->caps.has_cfg_reg) {
+ /* setup High Speed mode in relation with card capacity */
+ if (ios->timing == MMC_TIMING_SD_HS)
+ host->cfg_reg |= ATMCI_CFG_HSMODE;
+ else
+ host->cfg_reg &= ~ATMCI_CFG_HSMODE;
+
+ atmci_writel(host, ATMCI_CFG, host->cfg_reg);
+ }
+ } else {
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIDIS);
+ if (host->mode_reg) {
+ atmci_readl(host, ATMCI_MR);
+ clk_disable(host->clk);
+ }
+ host->mode_reg = 0;
+ }
+
+ return;
+}
+
+/** handle a command */
+int atmci_common_request(struct atmel_mci *host, struct mci_cmd *cmd,
+ struct mci_data *data)
+{
+ u32 stat, cmdat = 0;
+ int ret;
+
+ if (host->need_reset || host->caps.need_reset_after_xfer) {
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_SWRST);
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_MCIEN);
+ atmci_writel(host, ATMCI_MR, host->mode_reg);
+ if (host->caps.has_cfg_reg)
+ atmci_writel(host, ATMCI_CFG, host->cfg_reg);
+ host->need_reset = false;
+ }
+ atmci_writel(host, ATMCI_SDCR, host->sdc_reg);
+
+ if (cmd->resp_type != MMC_RSP_NONE)
+ cmdat |= ATMCI_CMDR_MAXLAT_64CYC;
+
+ if (data) {
+ atmci_setup_data(host, data);
+
+ cmdat |= ATMCI_CMDR_START_XFER | ATMCI_CMDR_MULTI_BLOCK;
+
+ if (data->flags & MMC_DATA_READ)
+ cmdat |= ATMCI_CMDR_TRDIR_READ;
+ }
+
+ ret = atmci_start_cmd(host, cmd, cmdat);
+ if (ret) {
+ atmci_finish_request(host);
+ return ret;
+ }
+
+ stat = atmci_poll_status(host, ATMCI_CMDRDY);
+ return atmci_cmd_done(host, stat);
+}
+
+
+/*
+ * HSMCI (High Speed MCI) module is not fully compatible with MCI module.
+ * HSMCI provides DMA support and a new config register but no more supports
+ * PDC.
+ */
+void atmci_get_cap(struct atmel_mci *host)
+{
+ unsigned int version;
+
+ version = atmci_readl(host, ATMCI_VERSION) & 0x00000fff;
+ host->version = version;
+
+ dev_dbg(host->hw_dev, "version: 0x%x\n", version);
+
+ host->caps.has_cfg_reg = 0;
+ host->caps.has_highspeed = 0;
+ host->caps.need_reset_after_xfer = 1;
+
+ switch (version & 0xf00) {
+ case 0x600:
+ case 0x500:
+ host->caps.has_odd_clk_div = 1;
+ case 0x400:
+ case 0x300:
+ host->caps.has_cfg_reg = 1;
+ host->caps.has_highspeed = 1;
+ case 0x200:
+ host->caps.has_rwproof = 1;
+ case 0x100:
+ host->caps.need_reset_after_xfer = 0;
+ case 0x0:
+ break;
+ default:
+ dev_warn(host->hw_dev,
+ "Unmanaged mci version, set minimum capabilities\n");
+ break;
+ }
+}
diff --git a/drivers/mci/atmel_mci_pbl.c b/drivers/mci/atmel_mci_pbl.c
new file mode 100644
index 0000000000..bd4faa4de5
--- /dev/null
+++ b/drivers/mci/atmel_mci_pbl.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <common.h>
+#include <mach/at91/xload.h>
+#include <mci.h>
+
+#include "atmel-mci-regs.h"
+
+#define SECTOR_SIZE 512
+#define SUPPORT_MAX_BLOCKS 16U
+
+struct atmel_mci_priv {
+ struct atmel_mci host;
+ bool highcapacity_card;
+};
+
+static struct atmel_mci_priv atmci_sdcard;
+
+static int atmel_mci_pbl_stop_transmission(struct atmel_mci_priv *priv)
+{
+ struct mci_cmd cmd = {
+ .cmdidx = MMC_CMD_STOP_TRANSMISSION,
+ .resp_type = MMC_RSP_R1b,
+ };
+
+ return atmci_common_request(&priv->host, &cmd, NULL);
+}
+
+static int at91_mci_sd_cmd_read_multiple_block(struct atmel_mci_priv *priv,
+ void *buf,
+ unsigned int start,
+ unsigned int block_count)
+{
+ u16 block_len = SECTOR_SIZE;
+ struct mci_data data;
+ struct mci_cmd cmd = {
+ .cmdidx = MMC_CMD_READ_MULTIPLE_BLOCK,
+ .resp_type = MMC_RSP_R1,
+ .cmdarg = start,
+ };
+
+ if (!priv->highcapacity_card)
+ cmd.cmdarg *= block_len;
+
+ data.dest = buf;
+ data.flags = MMC_DATA_READ;
+ data.blocksize = block_len;
+ data.blocks = block_count;
+
+ return atmci_common_request(&priv->host, &cmd, &data);
+}
+
+static int at91_mci_bio_read(struct pbl_bio *bio, off_t start,
+ void *buf, unsigned int nblocks)
+{
+ struct atmel_mci_priv *priv = bio->priv;
+ unsigned int blocks_done = 0;
+ unsigned int blocks;
+ unsigned int block_len = SECTOR_SIZE;
+ unsigned int blocks_read;
+ int ret;
+
+ while (blocks_done < nblocks) {
+ blocks = min(nblocks - blocks_done, SUPPORT_MAX_BLOCKS);
+
+ blocks_read = at91_mci_sd_cmd_read_multiple_block(priv, buf,
+ start + blocks_done,
+ blocks);
+
+ ret = atmel_mci_pbl_stop_transmission(priv);
+ if (ret)
+ return ret;
+
+ blocks_done += blocks_read;
+
+ if (blocks_read != blocks)
+ break;
+
+ buf += blocks * block_len;
+ }
+
+ return blocks_done;
+}
+
+int at91_mci_bio_init(struct pbl_bio *bio, void __iomem *base,
+ unsigned int clock, unsigned int slot)
+{
+ struct atmel_mci_priv *priv = &atmci_sdcard;
+ struct atmel_mci *host = &priv->host;
+ struct mci_ios ios = { .bus_width = MMC_BUS_WIDTH_4, .clock = 25000000 };
+
+ /* PBL will get MCI controller in disabled state. We need to reconfigure
+ * it. */
+ bio->priv = priv;
+ bio->read = at91_mci_bio_read;
+
+ host->regs = base;
+
+ atmci_get_cap(host);
+
+ host->bus_hz = clock;
+
+ host->slot_b = slot;
+ if (host->slot_b)
+ host->sdc_reg = ATMCI_SDCSEL_SLOT_B;
+ else
+ host->sdc_reg = ATMCI_SDCSEL_SLOT_A;
+
+ atmci_writel(host, ATMCI_CR, ATMCI_CR_PWSDIS);
+ atmci_writel(host, ATMCI_DTOR, 0x7f);
+
+ atmci_common_set_ios(host, &ios);
+
+ priv->highcapacity_card = 1;
+
+ return 0;
+}
+
+void at91_mci_bio_set_highcapacity(bool highcapacity_card)
+{
+ atmci_sdcard.highcapacity_card = highcapacity_card;
+}
diff --git a/drivers/mci/bcm2835-sdhost.c b/drivers/mci/bcm2835-sdhost.c
index 1d3a6c0969..2b1336a7d3 100644
--- a/drivers/mci/bcm2835-sdhost.c
+++ b/drivers/mci/bcm2835-sdhost.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-only
/*
* This code is ported from U-Boot by Lucas Stach <l.stach@pengutronix.de> and
* has the following contributors listed in the original license header:
@@ -135,7 +135,7 @@ static inline struct bcm2835_host *to_bcm2835_host(struct mci_host *mci)
return container_of(mci, struct bcm2835_host, mci);
}
-static int bcm2835_sdhost_init(struct mci_host *mci, struct device_d *dev)
+static int bcm2835_sdhost_init(struct mci_host *mci, struct device *dev)
{
struct bcm2835_host *host = to_bcm2835_host(mci);
u32 temp;
@@ -579,14 +579,7 @@ static void bcm2835_set_ios(struct mci_host *mci, struct mci_ios *ios)
writel(hcfg, host->regs + SDHCFG);
}
-static int bcm2835_sdhost_detect(struct device_d *dev)
-{
- struct bcm2835_host *host = dev->priv;
-
- return mci_detect_card(&host->mci);
-}
-
-static int bcm2835_sdhost_probe(struct device_d *dev)
+static int bcm2835_sdhost_probe(struct device *dev)
{
struct bcm2835_host *host;
struct resource *iores;
@@ -617,9 +610,6 @@ static int bcm2835_sdhost_probe(struct device_d *dev)
mci->set_ios = bcm2835_set_ios;
mci->send_cmd = bcm2835_send_cmd;
- dev->priv = host;
- dev->detect = bcm2835_sdhost_detect,
-
mci_of_parse(mci);
return mci_register(mci);
@@ -629,8 +619,9 @@ static __maybe_unused struct of_device_id bcm2835_sdhost_compatible[] = {
{ .compatible = "brcm,bcm2835-sdhost" },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, bcm2835_sdhost_compatible);
-static struct driver_d bcm2835_sdhost_driver = {
+static struct driver bcm2835_sdhost_driver = {
.name = "bcm2835-sdhost",
.probe = bcm2835_sdhost_probe,
.of_compatible = DRV_OF_COMPAT(bcm2835_sdhost_compatible),
diff --git a/drivers/mci/dove-sdhci.c b/drivers/mci/dove-sdhci.c
index bccda53994..d37046ad31 100644
--- a/drivers/mci/dove-sdhci.c
+++ b/drivers/mci/dove-sdhci.c
@@ -1,22 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
/*
* Marvell Dove SDHCI MCI driver
*
* Pengutronix, Michael Grzeschik <mgr@pengutronix.de>
* Sebastian Hesselbarth <sebastian.hesselbarth@gmail.com>
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <clock.h>
@@ -32,55 +20,12 @@
struct dove_sdhci {
struct mci_host mci;
- void __iomem *base;
struct sdhci sdhci;
};
#define priv_from_mci_host(h) \
container_of(h, struct dove_sdhci, mci);
-static void dove_sdhci_writel(struct sdhci *sdhci, int reg, u32 val)
-{
- struct dove_sdhci *p = container_of(sdhci, struct dove_sdhci, sdhci);
-
- writel(val, p->base + reg);
-}
-
-static void dove_sdhci_writew(struct sdhci *sdhci, int reg, u16 val)
-{
- struct dove_sdhci *p = container_of(sdhci, struct dove_sdhci, sdhci);
-
- writew(val, p->base + reg);
-}
-
-static void dove_sdhci_writeb(struct sdhci *sdhci, int reg, u8 val)
-{
- struct dove_sdhci *p = container_of(sdhci, struct dove_sdhci, sdhci);
-
- writeb(val, p->base + reg);
-}
-
-static u32 dove_sdhci_readl(struct sdhci *sdhci, int reg)
-{
- struct dove_sdhci *p = container_of(sdhci, struct dove_sdhci, sdhci);
-
- return readl(p->base + reg);
-}
-
-static u16 dove_sdhci_readw(struct sdhci *sdhci, int reg)
-{
- struct dove_sdhci *p = container_of(sdhci, struct dove_sdhci, sdhci);
-
- return readw(p->base + reg);
-}
-
-static u8 dove_sdhci_readb(struct sdhci *sdhci, int reg)
-{
- struct dove_sdhci *p = container_of(sdhci, struct dove_sdhci, sdhci);
-
- return readb(p->base + reg);
-}
-
static int dove_sdhci_wait_for_done(struct dove_sdhci *host, u16 mask)
{
u16 status;
@@ -112,39 +57,26 @@ static int dove_sdhci_wait_for_done(struct dove_sdhci *host, u16 mask)
static int dove_sdhci_mci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
struct mci_data *data)
{
- u16 val;
u32 command, xfer;
- u64 start;
int ret;
unsigned int num_bytes = 0;
struct dove_sdhci *host = priv_from_mci_host(mci);
- sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
-
- /* Do not wait for CMD_INHIBIT_DAT on stop commands */
- if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
- val = SDHCI_CMD_INHIBIT_CMD;
- else
- val = SDHCI_CMD_INHIBIT_CMD | SDHCI_CMD_INHIBIT_DATA;
+ ret = sdhci_wait_idle_data(&host->sdhci, cmd);
+ if (ret)
+ return ret;
- /* Wait for bus idle */
- start = get_time_ns();
- while (1) {
- if (!(sdhci_read16(&host->sdhci, SDHCI_PRESENT_STATE) & val))
- break;
- if (is_timeout(start, 10 * MSECOND)) {
- dev_err(host->mci.hw_dev, "SDHCI timeout while waiting for idle\n");
- return -ETIMEDOUT;
- }
- }
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
/* setup transfer data */
if (data) {
num_bytes = data->blocks * data->blocksize;
if (data->flags & MMC_DATA_READ)
- sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS, (u32)data->dest);
+ sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS,
+ lower_32_bits(virt_to_phys(data->dest)));
else
- sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS, (u32)data->src);
+ sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS,
+ lower_32_bits(virt_to_phys(data->src)));
sdhci_write16(&host->sdhci, SDHCI_BLOCK_SIZE, SDHCI_DMA_BOUNDARY_512K |
SDHCI_TRANSFER_BLOCK_SIZE(data->blocksize));
sdhci_write16(&host->sdhci, SDHCI_BLOCK_COUNT, data->blocks);
@@ -152,10 +84,12 @@ static int dove_sdhci_mci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
if (data->flags & MMC_DATA_WRITE)
- dma_sync_single_for_device((unsigned long)data->src,
+ dma_sync_single_for_device(host->mci.hw_dev,
+ lower_32_bits(virt_to_phys(data->src)),
num_bytes, DMA_TO_DEVICE);
else
- dma_sync_single_for_device((unsigned long)data->dest,
+ dma_sync_single_for_device(host->mci.hw_dev,
+ lower_32_bits(virt_to_phys(data->dest)),
num_bytes, DMA_FROM_DEVICE);
}
@@ -181,11 +115,13 @@ static int dove_sdhci_mci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
if (data) {
if (data->flags & MMC_DATA_WRITE)
- dma_sync_single_for_cpu((unsigned long)data->src,
+ dma_sync_single_for_cpu(host->mci.hw_dev,
+ lower_32_bits(virt_to_phys(data->src)),
num_bytes, DMA_TO_DEVICE);
else
- dma_sync_single_for_cpu((unsigned long)data->dest,
- num_bytes, DMA_FROM_DEVICE);
+ dma_sync_single_for_cpu(host->mci.hw_dev,
+ lower_32_bits(virt_to_phys(data->dest)),
+ num_bytes, DMA_FROM_DEVICE);
ret = dove_sdhci_wait_for_done(host, SDHCI_INT_XFER_COMPLETE);
if (ret) {
@@ -209,7 +145,7 @@ static u16 dove_sdhci_get_clock_divider(struct dove_sdhci *host, u32 reqclk)
{
u16 div;
- for (div = 1; div < SDHCI_SPEC_200_MAX_CLK_DIVIDER; div *= 2)
+ for (div = 1; div < SDHCI_MAX_DIV_SPEC_200; div *= 2)
if ((host->mci.f_max / div) <= reqclk)
break;
div /= 2;
@@ -236,33 +172,35 @@ static void dove_sdhci_mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
/* set bus width */
val = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL) &
- ~(SDHCI_DATA_WIDTH_4BIT | SDHCI_DATA_WIDTH_8BIT);
+ ~(SDHCI_CTRL_4BITBUS | SDHCI_CTRL_8BITBUS);
switch (ios->bus_width) {
case MMC_BUS_WIDTH_8:
- val |= SDHCI_DATA_WIDTH_8BIT;
+ val |= SDHCI_CTRL_8BITBUS;
break;
case MMC_BUS_WIDTH_4:
- val |= SDHCI_DATA_WIDTH_4BIT;
+ val |= SDHCI_CTRL_4BITBUS;
+ break;
+ case MMC_BUS_WIDTH_1:
break;
}
if (ios->clock > 26000000)
- val |= SDHCI_HIGHSPEED_EN;
+ val |= SDHCI_CTRL_HISPD;
else
- val &= ~SDHCI_HIGHSPEED_EN;
+ val &= ~SDHCI_CTRL_HISPD;
sdhci_write8(&host->sdhci, SDHCI_HOST_CONTROL, val);
/* set bus clock */
sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, 0);
val = dove_sdhci_get_clock_divider(host, ios->clock);
- val = SDHCI_INTCLOCK_EN | SDHCI_FREQ_SEL(val);
+ val = SDHCI_CLOCK_INT_EN | SDHCI_FREQ_SEL(val);
sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, val);
/* wait for internal clock stable */
start = get_time_ns();
while (!(sdhci_read16(&host->sdhci, SDHCI_CLOCK_CONTROL) &
- SDHCI_INTCLOCK_STABLE)) {
+ SDHCI_CLOCK_INT_STABLE)) {
if (is_timeout(start, 20 * MSECOND)) {
dev_err(host->mci.hw_dev, "SDHCI clock stable timeout\n");
return;
@@ -270,10 +208,10 @@ static void dove_sdhci_mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
}
/* enable bus clock */
- sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, val | SDHCI_SDCLOCK_EN);
+ sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, val | SDHCI_CLOCK_CARD_EN);
}
-static int dove_sdhci_mci_init(struct mci_host *mci, struct device_d *dev)
+static int dove_sdhci_mci_init(struct mci_host *mci, struct device *dev)
{
u64 start;
struct dove_sdhci *host = priv_from_mci_host(mci);
@@ -302,19 +240,18 @@ static int dove_sdhci_mci_init(struct mci_host *mci, struct device_d *dev)
static void dove_sdhci_set_mci_caps(struct dove_sdhci *host)
{
- u16 caps[2];
+ u32 caps;
- caps[0] = sdhci_read16(&host->sdhci, SDHCI_CAPABILITIES);
- caps[1] = sdhci_read16(&host->sdhci, SDHCI_CAPABILITIES_1);
+ caps = sdhci_read32(&host->sdhci, SDHCI_CAPABILITIES);
- if (caps[1] & SDHCI_HOSTCAP_VOLTAGE_180)
+ if (caps & SDHCI_CAN_VDD_180)
host->mci.voltages |= MMC_VDD_165_195;
- if (caps[1] & SDHCI_HOSTCAP_VOLTAGE_300)
+ if (caps & SDHCI_CAN_VDD_300)
host->mci.voltages |= MMC_VDD_29_30 | MMC_VDD_30_31;
- if (caps[1] & SDHCI_HOSTCAP_VOLTAGE_330)
+ if (caps & SDHCI_CAN_VDD_330)
host->mci.voltages |= MMC_VDD_32_33 | MMC_VDD_33_34;
- if (caps[1] & SDHCI_HOSTCAP_HIGHSPEED)
+ if (caps & SDHCI_CAN_DO_HISPD)
host->mci.host_caps |= (MMC_CAP_MMC_HIGHSPEED_52MHZ |
MMC_CAP_MMC_HIGHSPEED |
MMC_CAP_SD_HIGHSPEED);
@@ -323,23 +260,17 @@ static void dove_sdhci_set_mci_caps(struct dove_sdhci *host)
mci_of_parse(&host->mci);
/* limit bus widths to controller capabilities */
- if ((caps[1] & SDHCI_HOSTCAP_8BIT) == 0)
+ if ((caps & SDHCI_CAN_DO_8BIT) == 0)
host->mci.host_caps &= ~MMC_CAP_8_BIT_DATA;
}
-static int dove_sdhci_detect(struct device_d *dev)
-{
- struct dove_sdhci *host = dev->priv;
- return mci_detect_card(&host->mci);
-}
-
-static int dove_sdhci_probe(struct device_d *dev)
+static int dove_sdhci_probe(struct device *dev)
{
struct dove_sdhci *host;
int ret;
host = xzalloc(sizeof(*host));
- host->base = dev_request_mem_region(dev, 0);
+ host->sdhci.base = dev_request_mem_region(dev, 0);
host->mci.max_req_size = 0x8000;
host->mci.hw_dev = dev;
host->mci.send_cmd = dove_sdhci_mci_send_cmd;
@@ -347,14 +278,6 @@ static int dove_sdhci_probe(struct device_d *dev)
host->mci.init = dove_sdhci_mci_init;
host->mci.f_max = 50000000;
host->mci.f_min = host->mci.f_max / 256;
- host->sdhci.read32 = dove_sdhci_readl;
- host->sdhci.read16 = dove_sdhci_readw;
- host->sdhci.read8 = dove_sdhci_readb;
- host->sdhci.write32 = dove_sdhci_writel;
- host->sdhci.write16 = dove_sdhci_writew;
- host->sdhci.write8 = dove_sdhci_writeb;
- dev->priv = host;
- dev->detect = dove_sdhci_detect;
dove_sdhci_set_mci_caps(host);
@@ -368,8 +291,9 @@ static struct of_device_id dove_sdhci_dt_ids[] = {
{ .compatible = "marvell,dove-sdhci", },
{ }
};
+MODULE_DEVICE_TABLE(of, dove_sdhci_dt_ids);
-static struct driver_d dove_sdhci_driver = {
+static struct driver dove_sdhci_driver = {
.name = "dove-sdhci",
.probe = dove_sdhci_probe,
.of_compatible = DRV_OF_COMPAT(dove_sdhci_dt_ids),
diff --git a/drivers/mci/dw_mmc.c b/drivers/mci/dw_mmc.c
index f035317ef2..c49e839c94 100644
--- a/drivers/mci/dw_mmc.c
+++ b/drivers/mci/dw_mmc.c
@@ -1,20 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2013 Altera Corporation <www.altera.com>
+// SPDX-FileCopyrightText: 2012 SAMSUNG Electronics
+
/*
- * Copyright (C) 2013 Altera Corporation <www.altera.com>
- *
- * (C) Copyright 2012 SAMSUNG Electronics
* Jaehoon Chung <jh80.chung@samsung.com>
* Rajeshawari Shinde <rajeshwari.s@samsung.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <common.h>
@@ -27,23 +17,26 @@
#include <io.h>
#include <platform_data/dw_mmc.h>
#include <linux/bitops.h>
+#include <linux/reset.h>
#include <linux/clk.h>
#include <linux/err.h>
-#include <asm-generic/errno.h>
+#include <errno.h>
#include "dw_mmc.h"
struct dwmci_host {
struct mci_host mci;
+ struct device *dev;
struct clk *clk_biu, *clk_ciu;
void *ioaddr;
unsigned int fifo_size_bytes;
struct dwmci_idmac *idmac;
- unsigned long clkrate;
+ u32 clkrate;
int ciu_div;
u32 fifoth_val;
u32 pwren_value;
+ dma_addr_t idmac_dma;
};
struct dwmci_idmac {
@@ -120,12 +113,12 @@ static int dwmci_prepare_data_pio(struct dwmci_host *host,
}
static int dwmci_prepare_data_dma(struct dwmci_host *host,
- struct mci_data *data)
+ struct mci_data *data, dma_addr_t dma)
{
unsigned long ctrl;
unsigned int i = 0, flags, cnt, blk_cnt;
- unsigned long data_start, start_addr;
struct dwmci_idmac *desc = host->idmac;
+ dma_addr_t desc_dma = host->idmac_dma;
blk_cnt = data->blocks;
@@ -134,13 +127,7 @@ static int dwmci_prepare_data_dma(struct dwmci_host *host,
dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET);
- data_start = (uint32_t)desc;
- dwmci_writel(host, DWMCI_DBADDR, (uint32_t)desc);
-
- if (data->flags & MMC_DATA_READ)
- start_addr = (uint32_t)data->dest;
- else
- start_addr = (uint32_t)data->src;
+ dwmci_writel(host, DWMCI_DBADDR, desc_dma);
do {
flags = DWMCI_IDMAC_OWN | DWMCI_IDMAC_CH;
@@ -153,10 +140,12 @@ static int dwmci_prepare_data_dma(struct dwmci_host *host,
cnt = data->blocksize * 8;
}
+ desc_dma += sizeof(*desc);
+
desc->flags = flags;
desc->cnt = cnt;
- desc->addr = start_addr + (i * PAGE_SIZE);
- desc->next_addr = (uint32_t)(desc + 1);
+ desc->addr = dma + (i * PAGE_SIZE);
+ desc->next_addr = desc_dma;
dev_dbg(host->mci.hw_dev, "desc@ 0x%p 0x%08x 0x%08x 0x%08x 0x%08x\n",
desc, flags, cnt, desc->addr, desc->next_addr);
@@ -183,12 +172,12 @@ static int dwmci_prepare_data_dma(struct dwmci_host *host,
}
static int dwmci_prepare_data(struct dwmci_host *host,
- struct mci_data *data)
+ struct mci_data *data, dma_addr_t dma)
{
if (dwmci_use_pio(host))
return dwmci_prepare_data_pio(host, data);
else
- return dwmci_prepare_data_dma(host, data);
+ return dwmci_prepare_data_dma(host, data, dma);
}
static int dwmci_set_transfer_mode(struct dwmci_host *host,
@@ -283,6 +272,7 @@ dwmci_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
uint64_t start;
int ret;
unsigned int num_bytes = 0;
+ dma_addr_t dma = 0;
start = get_time_ns();
while (1) {
@@ -298,16 +288,20 @@ dwmci_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_ALL);
if (data) {
+
num_bytes = data->blocks * data->blocksize;
if (data->flags & MMC_DATA_WRITE)
- dma_sync_single_for_device((unsigned long)data->src,
- num_bytes, DMA_TO_DEVICE);
+ dma = dma_map_single(host->dev, (void *)data->src, num_bytes,
+ DMA_TO_DEVICE);
else
- dma_sync_single_for_device((unsigned long)data->dest,
- num_bytes, DMA_FROM_DEVICE);
+ dma = dma_map_single(host->dev, data->dest, num_bytes,
+ DMA_FROM_DEVICE);
+
+ if (dma_mapping_error(host->dev, dma))
+ return -EFAULT;
- ret = dwmci_prepare_data(host, data);
+ ret = dwmci_prepare_data(host, data, dma);
if (ret)
return ret;
}
@@ -411,11 +405,11 @@ dwmci_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
dwmci_writel(host, DWMCI_CTRL, ctrl);
if (data->flags & MMC_DATA_WRITE)
- dma_sync_single_for_cpu((unsigned long)data->src,
- num_bytes, DMA_TO_DEVICE);
+ dma_unmap_single(host->dev, dma, num_bytes,
+ DMA_TO_DEVICE);
else
- dma_sync_single_for_cpu((unsigned long)data->dest,
- num_bytes, DMA_FROM_DEVICE);
+ dma_unmap_single(host->dev, dma, num_bytes,
+ DMA_FROM_DEVICE);
}
}
@@ -502,7 +496,7 @@ static int dwmci_card_present(struct mci_host *mci)
return 1;
}
-static int dwmci_init(struct mci_host *mci, struct device_d *dev)
+static int dwmci_init(struct mci_host *mci, struct device *dev)
{
struct dwmci_host *host = to_dwmci_host(mci);
uint32_t fifo_size;
@@ -534,8 +528,8 @@ static int dwmci_init(struct mci_host *mci, struct device_d *dev)
/*
* If fifo-depth property is set, use this value
*/
- if (!of_property_read_u32(host->mci.hw_dev->device_node,
- "fifo-depth", &fifo_size)) {
+ if (!of_property_read_u32(host->mci.hw_dev->of_node,
+ "fifo-depth", &fifo_size)) {
host->fifo_size_bytes = fifo_size;
dev_dbg(host->mci.hw_dev, "Using fifo-depth=%u\n",
host->fifo_size_bytes);
@@ -553,21 +547,18 @@ static int dwmci_init(struct mci_host *mci, struct device_d *dev)
return 0;
}
-static int dw_mmc_detect(struct device_d *dev)
-{
- struct dwmci_host *host = dev->priv;
-
- return mci_detect_card(&host->mci);
-}
-
-static int dw_mmc_probe(struct device_d *dev)
+static int dw_mmc_probe(struct device *dev)
{
+ struct reset_control *rst;
struct resource *iores;
struct dwmci_host *host;
struct dw_mmc_platform_data *pdata = dev->platform_data;
host = xzalloc(sizeof(*host));
+ dma_set_mask(dev, DMA_BIT_MASK(32));
+ host->dev = dev;
+
host->clk_biu = clk_get(dev, "biu");
if (IS_ERR(host->clk_biu))
return PTR_ERR(host->clk_biu);
@@ -579,13 +570,24 @@ static int dw_mmc_probe(struct device_d *dev)
clk_enable(host->clk_biu);
clk_enable(host->clk_ciu);
+ rst = reset_control_get_optional(dev, "reset");
+ if (IS_ERR(rst)) {
+ dev_warn(dev, "error claiming reset: %pe\n", rst);
+ } else if (rst) {
+ reset_control_assert(rst);
+ udelay(10);
+ reset_control_deassert(rst);
+ }
+
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores))
return PTR_ERR(iores);
host->ioaddr = IOMEM(iores->start);
host->idmac = dma_alloc_coherent(sizeof(*host->idmac) * DW_MMC_NUM_IDMACS,
- DMA_ADDRESS_BROKEN);
+ &host->idmac_dma);
+ if (!host->idmac)
+ return -ENOMEM;
host->mci.send_cmd = dwmci_cmd;
host->mci.set_ios = dwmci_set_ios;
@@ -601,23 +603,26 @@ static int dw_mmc_probe(struct device_d *dev)
host->ciu_div = pdata->ciu_div;
host->mci.host_caps &= ~MMC_CAP_BIT_DATA_MASK;
host->mci.host_caps |= pdata->bus_width_caps;
- } else if (dev->device_node) {
- of_property_read_u32(dev->device_node, "dw-mshc-ciu-div",
- &host->ciu_div);
+ } else if (dev->of_node) {
+ of_property_read_u32(dev->of_node, "dw-mshc-ciu-div",
+ &host->ciu_div);
}
/* divider is 0 based in pdata and 1 based in our private struct */
host->ciu_div++;
- if (of_device_is_compatible(dev->device_node,
- "rockchip,rk2928-dw-mshc"))
+ if (of_device_is_compatible(dev->of_node,
+ "rockchip,rk2928-dw-mshc"))
host->pwren_value = 0;
else
host->pwren_value = 1;
- dev->detect = dw_mmc_detect;
+ if (of_device_is_compatible(dev->of_node, "starfive,jh7100-dw-mshc"))
+ of_property_read_u32(dev->of_node, "clock-frequency",
+ &host->clkrate);
+ if (!host->clkrate)
+ host->clkrate = clk_get_rate(host->clk_ciu);
- host->clkrate = clk_get_rate(host->clk_ciu);
host->mci.f_min = host->clkrate / 510 / host->ciu_div;
if (host->mci.f_min < 200000)
host->mci.f_min = 200000;
@@ -625,8 +630,6 @@ static int dw_mmc_probe(struct device_d *dev)
mci_of_parse(&host->mci);
- dev->priv = host;
-
return mci_register(&host->mci);
}
@@ -638,11 +641,16 @@ static __maybe_unused struct of_device_id dw_mmc_compatible[] = {
}, {
.compatible = "rockchip,rk3288-dw-mshc",
}, {
+ .compatible = "snps,dw-mshc",
+ }, {
+ .compatible = "starfive,jh7100-dw-mshc",
+ }, {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, dw_mmc_compatible);
-static struct driver_d dw_mmc_driver = {
+static struct driver dw_mmc_driver = {
.name = "dw_mmc",
.probe = dw_mmc_probe,
.of_compatible = DRV_OF_COMPAT(dw_mmc_compatible),
diff --git a/drivers/mci/dw_mmc.h b/drivers/mci/dw_mmc.h
index 23b0f0fe26..23fa116d75 100644
--- a/drivers/mci/dw_mmc.h
+++ b/drivers/mci/dw_mmc.h
@@ -1,20 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* SPDX-FileCopyrightText: 2013 Altera Corporation <www.altera.com> */
+/* SPDX-FileCopyrightText: 2012 SAMSUNG Electronics */
+
/*
- * Copyright (C) 2013 Altera Corporation <www.altera.com>
- *
- * (C) Copyright 2012 SAMSUNG Electronics
* Jaehoon Chung <jh80.chung@samsung.com>
* Rajeshawari Shinde <rajeshwari.s@samsung.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#ifndef __DW_MMC_H__
diff --git a/drivers/mci/dwcmshc-sdhci.c b/drivers/mci/dwcmshc-sdhci.c
new file mode 100644
index 0000000000..7b367e02ee
--- /dev/null
+++ b/drivers/mci/dwcmshc-sdhci.c
@@ -0,0 +1,402 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2019 Yann Sionneau, Kalray Inc.
+// SPDX-FileCopyrightText: 2023 Jules Maselbas, Kalray Inc.
+
+#include <clock.h>
+#include <common.h>
+#include <init.h>
+#include <io.h>
+#include <dma.h>
+#include <malloc.h>
+#include <mci.h>
+#include <of_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+
+#include "sdhci.h"
+
+#define tx_delay_static_cfg(delay) (delay << 5)
+#define tx_tuning_clk_sel(delay) (delay)
+
+#define DWCMSHC_GPIO_OUT 0x34 /* offset from vendor specific area */
+#define CARD_STATUS_MASK (0x1e00)
+#define CARD_STATUS_TRAN (4 << 9)
+
+static int do_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data);
+
+struct dwcmshc_host {
+ struct mci_host mci;
+ struct sdhci sdhci;
+ int vendor_specific_area;
+ const struct dwcmshc_callbacks *cb;
+};
+
+struct dwcmshc_callbacks {
+ void (*init)(struct mci_host *mci, struct device *dev);
+};
+
+static inline struct dwcmshc_host *priv_from_mci_host(struct mci_host *h)
+{
+ return container_of(h, struct dwcmshc_host, mci);
+}
+
+static void mci_setup_cmd(struct mci_cmd *p, unsigned int cmd, unsigned int arg,
+ unsigned int response)
+{
+ p->cmdidx = cmd;
+ p->cmdarg = arg;
+ p->resp_type = response;
+}
+
+static int do_abort_sequence(struct mci_host *mci, struct mci_cmd *current_cmd)
+{
+ int ret = 0;
+ struct dwcmshc_host *host = priv_from_mci_host(mci);
+ struct mci_cmd cmd;
+ u64 start;
+
+ mci_setup_cmd(&cmd, MMC_CMD_STOP_TRANSMISSION, 0, MMC_RSP_R1b);
+ ret = do_send_cmd(mci, &cmd, NULL);
+ if (ret) {
+ dev_err(host->mci.hw_dev, "Abort failed at first cmd12!\n");
+ goto out;
+ }
+ mci_setup_cmd(&cmd, MMC_CMD_SEND_STATUS, mci->mci->rca << 16,
+ MMC_RSP_R1);
+ ret = do_send_cmd(mci, &cmd, NULL);
+ if (ret) {
+ dev_err(host->mci.hw_dev, "Abort failed at first cmd13!\n");
+ goto out;
+ }
+
+ if ((cmd.response[0] & CARD_STATUS_MASK) == CARD_STATUS_TRAN)
+ goto out; /* All is OK! */
+
+ mci_setup_cmd(&cmd, MMC_CMD_STOP_TRANSMISSION, 0, MMC_RSP_R1b);
+ ret = do_send_cmd(mci, &cmd, NULL);
+ if (ret) {
+ dev_err(host->mci.hw_dev, "Abort failed at second cmd12!\n");
+ goto out;
+ }
+
+ mci_setup_cmd(&cmd, MMC_CMD_SEND_STATUS, mci->mci->rca << 16,
+ MMC_RSP_R1);
+ ret = do_send_cmd(mci, &cmd, NULL);
+ if (ret) {
+ dev_err(host->mci.hw_dev, "Abort failed at second cmd13!\n");
+ goto out;
+ }
+
+ if ((cmd.response[0] & CARD_STATUS_MASK) != CARD_STATUS_TRAN) {
+ dev_err(host->mci.hw_dev,
+ "Abort sequence failed to put card in TRAN state!\n");
+ ret = -EIO;
+ goto out;
+ }
+
+out:
+ /* Perform SW reset if in abort sequence */
+ sdhci_write8(&host->sdhci, SDHCI_SOFTWARE_RESET,
+ SDHCI_RESET_DATA | SDHCI_RESET_CMD);
+ start = get_time_ns();
+ while (sdhci_read8(&host->sdhci, SDHCI_SOFTWARE_RESET) != 0) {
+ if (is_timeout(start, 50 * MSECOND)) {
+ dev_err(host->mci.hw_dev,
+ "SDHCI data reset timeout\n");
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int dwcmshc_mci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
+ struct mci_data *data)
+{
+ if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
+ return do_abort_sequence(mci, cmd);
+ return do_send_cmd(mci, cmd, data);
+}
+
+static int do_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
+ struct mci_data *data)
+{
+ struct dwcmshc_host *host = priv_from_mci_host(mci);
+ dma_addr_t dma = SDHCI_NO_DMA;
+ u32 mask, command, xfer;
+ int ret;
+
+ /* Do not wait for CMD_INHIBIT_DAT on stop commands */
+ mask = SDHCI_CMD_INHIBIT_CMD;
+ if (cmd->cmdidx != MMC_CMD_STOP_TRANSMISSION)
+ mask |= SDHCI_CMD_INHIBIT_DATA;
+
+ /* Wait for bus idle */
+ ret = wait_on_timeout(50 * MSECOND,
+ !(sdhci_read16(&host->sdhci, SDHCI_PRESENT_STATE) & mask));
+ if (ret) {
+ dev_err(host->mci.hw_dev, "SDHCI timeout while waiting for idle\n");
+ return -ETIMEDOUT;
+ }
+
+ if (data)
+ dev_dbg(host->mci.hw_dev, "cmd %d arg %d bcnt %d bsize %d\n",
+ cmd->cmdidx, cmd->cmdarg, data->blocks, data->blocksize);
+ else
+ dev_dbg(host->mci.hw_dev, "cmd %d arg %d\n", cmd->cmdidx, cmd->cmdarg);
+
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
+
+ sdhci_setup_data_dma(&host->sdhci, data, &dma);
+
+ sdhci_write8(&host->sdhci, SDHCI_TIMEOUT_CONTROL, 0xe);
+
+ sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data,
+ dma == SDHCI_NO_DMA ? false : true,
+ &command, &xfer);
+
+ sdhci_write16(&host->sdhci, SDHCI_TRANSFER_MODE, xfer);
+
+ sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, cmd->cmdarg);
+ sdhci_write16(&host->sdhci, SDHCI_COMMAND, command);
+
+ ret = sdhci_wait_for_done(&host->sdhci, SDHCI_INT_CMD_COMPLETE);
+ if (ret)
+ goto error;
+
+ sdhci_read_response(&host->sdhci, cmd);
+
+ ret = sdhci_transfer_data(&host->sdhci, data, dma);
+error:
+ if (ret) {
+ sdhci_reset(&host->sdhci, SDHCI_RESET_CMD);
+ sdhci_reset(&host->sdhci, SDHCI_RESET_DATA);
+ }
+
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
+ return ret;
+}
+
+static u16 dwcmshc_get_clock_divider(struct dwcmshc_host *host, u32 reqclk)
+{
+ u16 div;
+
+ for (div = 1; div < SDHCI_MAX_DIV_SPEC_300; div += 2)
+ if ((host->sdhci.max_clk / div) <= reqclk)
+ break;
+ div /= 2;
+
+ return div;
+}
+
+static void dwcmshc_mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
+{
+ struct dwcmshc_host *host = priv_from_mci_host(mci);
+ u16 val;
+
+ dev_dbg(host->mci.hw_dev,
+ "%s: clock = %u, bus-width = %d, timing = %02x\n",
+ __func__, ios->clock, ios->bus_width, ios->timing);
+
+ /* stop clock */
+ sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, 0);
+
+ if (!ios->clock)
+ return;
+
+ /* enable bus power */
+ val = SDHCI_BUS_VOLTAGE_330;
+ sdhci_write8(&host->sdhci, SDHCI_POWER_CONTROL, val | SDHCI_BUS_POWER_EN);
+ udelay(400);
+
+ /* set bus width */
+ sdhci_set_bus_width(&host->sdhci, ios->bus_width);
+
+ val = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL);
+ if (ios->clock > 26000000)
+ val |= SDHCI_CTRL_HISPD;
+ else
+ val &= ~SDHCI_CTRL_HISPD;
+ sdhci_write8(&host->sdhci, SDHCI_HOST_CONTROL, val);
+
+ /* set bus clock */
+ sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, 0);
+ val = dwcmshc_get_clock_divider(host, ios->clock);
+ val = SDHCI_CLOCK_INT_EN | SDHCI_FREQ_SEL(val) | ((val & 0x300) >> 2);
+ sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, val);
+
+ /* wait for internal clock stable */
+ if (wait_on_timeout(20 * MSECOND,
+ sdhci_read16(&host->sdhci, SDHCI_CLOCK_CONTROL)
+ & SDHCI_CLOCK_INT_STABLE)) {
+ dev_err(host->mci.hw_dev, "SDHCI clock stable timeout\n");
+ return;
+ }
+
+ /* enable bus clock */
+ sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, val | SDHCI_CLOCK_CARD_EN);
+}
+
+static int dwcmshc_mci_init(struct mci_host *mci, struct device *dev)
+{
+ struct dwcmshc_host *host = priv_from_mci_host(mci);
+ u16 ctrl2;
+
+ /* reset mshci controller */
+ sdhci_write8(&host->sdhci, SDHCI_SOFTWARE_RESET, SDHCI_RESET_ALL);
+
+ /* wait for reset completion */
+ if (wait_on_timeout(100 * MSECOND,
+ (sdhci_read8(&host->sdhci, SDHCI_SOFTWARE_RESET)
+ & SDHCI_RESET_ALL) == 0)) {
+ dev_err(dev, "SDHCI reset timeout\n");
+ return -ETIMEDOUT;
+ }
+
+ sdhci_write16(&host->sdhci, SDHCI_INT_ERROR_ENABLE, ~0);
+ sdhci_write16(&host->sdhci, SDHCI_INT_ENABLE, ~0);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
+ sdhci_write32(&host->sdhci, SDHCI_SIGNAL_ENABLE, ~0);
+
+ sdhci_enable_v4_mode(&host->sdhci);
+
+ dev_dbg(host->mci.hw_dev, "host version4: %s\n",
+ ctrl2 & SDHCI_CTRL_V4_MODE ? "enabled" : "disabled");
+
+ if (host->cb && host->cb->init)
+ host->cb->init(mci, dev);
+
+ return 0;
+}
+
+static int dwcmshc_detect(struct device *dev)
+{
+ struct dwcmshc_host *host = dev->priv;
+
+ return mci_detect_card(&host->mci);
+}
+
+static int dwcmshc_mci_card_present(struct mci_host *mci)
+{
+ struct dwcmshc_host *host = priv_from_mci_host(mci);
+ u32 pstate;
+
+ pstate = sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE);
+ return pstate & SDHCI_CARD_PRESENT;
+}
+
+static void dwcmshc_set_dma_mask(struct device *dev)
+{
+ struct dwcmshc_host *host = dev->priv;
+
+ if (host->sdhci.caps & SDHCI_CAN_64BIT_V4)
+ dma_set_mask(dev, DMA_BIT_MASK(64));
+ else
+ dma_set_mask(dev, DMA_BIT_MASK(32));
+}
+
+static int dwcmshc_probe(struct device *dev)
+{
+ const struct dwcmshc_callbacks *dwcmshc_cb =
+ of_device_get_match_data(dev);
+ struct dwcmshc_host *host;
+ struct resource *iores;
+ struct mci_host *mci;
+ struct clk *clk;
+ int ret;
+
+ host = xzalloc(sizeof(*host));
+ mci = &host->mci;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores)) {
+ ret = PTR_ERR(iores);
+ goto err_mem_req;
+ }
+
+ clk = clk_get(dev, NULL);
+ if (IS_ERR(clk)) {
+ ret = PTR_ERR(clk);
+ goto err_clk_get;
+ }
+ clk_enable(clk);
+
+ host->sdhci.base = IOMEM(iores->start);
+ host->sdhci.mci = mci;
+ host->sdhci.max_clk = clk_get_rate(clk);
+ host->cb = dwcmshc_cb;
+
+ mci->hw_dev = dev;
+ mci->init = dwcmshc_mci_init;
+ mci->set_ios = dwcmshc_mci_set_ios;
+ mci->send_cmd = dwcmshc_mci_send_cmd;
+ mci->card_present = dwcmshc_mci_card_present;
+
+ sdhci_setup_host(&host->sdhci);
+
+ mci->max_req_size = 0x8000;
+ /*
+ * Let's first initialize f_max to the DT clock freq
+ * Then mci_of_parse can override if with the content
+ * of the 'max-frequency' DT property if it is present.
+ * Then we can finish by computing the f_min.
+ */
+ mci->f_max = clk_get_rate(clk);
+ mci_of_parse(&host->mci);
+ mci->f_min = mci->f_max / SDHCI_MAX_DIV_SPEC_300;
+
+ dev->priv = host;
+ dev->detect = dwcmshc_detect;
+
+ dwcmshc_set_dma_mask(dev);
+
+ dev_dbg(host->mci.hw_dev, "host controller version: %u\n",
+ host->sdhci.version);
+
+ host->vendor_specific_area = sdhci_read32(&host->sdhci,
+ SDHCI_P_VENDOR_SPEC_AREA);
+ host->vendor_specific_area &= SDHCI_P_VENDOR_SPEC_AREA_MASK;
+
+ ret = mci_register(&host->mci);
+ if (ret)
+ goto err_register;
+
+ return ret;
+
+err_register:
+ clk_disable(clk);
+ clk_put(clk);
+err_clk_get:
+ release_region(iores);
+err_mem_req:
+ free(host);
+
+ return ret;
+}
+
+static void dwcmshc_coolidgev2_init(struct mci_host *mci, struct device *dev)
+{
+ struct dwcmshc_host *host = priv_from_mci_host(mci);
+
+ /* configure TX delay to set correct setup/hold for Coolidge V2 */
+ sdhci_write32(&host->sdhci,
+ host->vendor_specific_area + DWCMSHC_GPIO_OUT,
+ tx_delay_static_cfg(0xf) | tx_tuning_clk_sel(4));
+}
+
+struct dwcmshc_callbacks kalray_coolidgev2_callbacks = {
+ .init = dwcmshc_coolidgev2_init,
+};
+
+static struct of_device_id dwcmshc_dt_ids[] = {
+ { .compatible = "snps,dwcmshc-sdhci", },
+ { .compatible = "kalray,coolidge-v2-dwcmshc-sdhci", .data = &kalray_coolidgev2_callbacks },
+ { }
+};
+
+static struct driver dwcmshc_driver = {
+ .name = "dwcmshc-sdhci",
+ .probe = dwcmshc_probe,
+ .of_compatible = DRV_OF_COMPAT(dwcmshc_dt_ids),
+};
+device_platform_driver(dwcmshc_driver);
diff --git a/drivers/mci/imx-esdhc-common.c b/drivers/mci/imx-esdhc-common.c
index c9d589468f..3d93889143 100644
--- a/drivers/mci/imx-esdhc-common.c
+++ b/drivers/mci/imx-esdhc-common.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-only
#include <common.h>
#include <io.h>
@@ -10,48 +10,41 @@
#define PRSSTAT_DAT0 0x01000000
-struct fsl_esdhc_dma_transfer {
- dma_addr_t dma;
- unsigned int size;
- enum dma_data_direction dir;
-};
-
-static u32 esdhc_op_read32_le(struct sdhci *sdhci, int reg)
+static u32 esdhc_op_read32_be(struct sdhci *sdhci, int reg)
{
struct fsl_esdhc_host *host = sdhci_to_esdhc(sdhci);
- return readl(host->regs + reg);
+ return in_be32(host->sdhci.base + reg);
}
-static u32 esdhc_op_read32_be(struct sdhci *sdhci, int reg)
+static void esdhc_op_write32_be(struct sdhci *sdhci, int reg, u32 val)
{
struct fsl_esdhc_host *host = sdhci_to_esdhc(sdhci);
- return in_be32(host->regs + reg);
+ out_be32(host->sdhci.base + reg, val);
}
-static void esdhc_op_write32_le(struct sdhci *sdhci, int reg, u32 val)
+static u16 esdhc_op_read16_be(struct sdhci *sdhci, int reg)
{
struct fsl_esdhc_host *host = sdhci_to_esdhc(sdhci);
- writel(val, host->regs + reg);
+ return in_be16(host->sdhci.base + reg);
}
-static void esdhc_op_write32_be(struct sdhci *sdhci, int reg, u32 val)
+static void esdhc_op_write16_be(struct sdhci *sdhci, int reg, u16 val)
{
struct fsl_esdhc_host *host = sdhci_to_esdhc(sdhci);
- out_be32(host->regs + reg, val);
+ out_be16(host->sdhci.base + reg, val);
}
void esdhc_populate_sdhci(struct fsl_esdhc_host *host)
{
if (host->socdata->flags & ESDHC_FLAG_BIGENDIAN) {
+ host->sdhci.read16 = esdhc_op_read16_be;
+ host->sdhci.write16 = esdhc_op_write16_be;
host->sdhci.read32 = esdhc_op_read32_be;
host->sdhci.write32 = esdhc_op_write32_be;
- } else {
- host->sdhci.read32 = esdhc_op_read32_le;
- host->sdhci.write32 = esdhc_op_write32_le;
}
}
@@ -59,71 +52,28 @@ static bool esdhc_use_pio_mode(void)
{
return IN_PBL || IS_ENABLED(CONFIG_MCI_IMX_ESDHC_PIO);
}
+
static int esdhc_setup_data(struct fsl_esdhc_host *host, struct mci_data *data,
- struct fsl_esdhc_dma_transfer *tr)
+ dma_addr_t *dma)
{
u32 wml_value;
- void *ptr;
-
- if (!esdhc_use_pio_mode()) {
- wml_value = data->blocksize/4;
- if (data->flags & MMC_DATA_READ) {
- if (wml_value > 0x10)
- wml_value = 0x10;
+ wml_value = data->blocksize / 4;
+ if (wml_value > 0x80)
+ wml_value = 0x80;
- esdhc_clrsetbits32(host, IMX_SDHCI_WML, WML_RD_WML_MASK, wml_value);
- } else {
- if (wml_value > 0x80)
- wml_value = 0x80;
+ if (data->flags & MMC_DATA_READ)
+ esdhc_clrsetbits32(host, IMX_SDHCI_WML, WML_RD_WML_MASK, wml_value);
+ else
+ esdhc_clrsetbits32(host, IMX_SDHCI_WML, WML_WR_WML_MASK,
+ wml_value << 16);
- esdhc_clrsetbits32(host, IMX_SDHCI_WML, WML_WR_WML_MASK,
- wml_value << 16);
- }
-
- tr->size = data->blocks * data->blocksize;
-
- if (data->flags & MMC_DATA_WRITE) {
- ptr = (void *)data->src;
- tr->dir = DMA_TO_DEVICE;
- } else {
- ptr = data->dest;
- tr->dir = DMA_FROM_DEVICE;
- }
-
- tr->dma = dma_map_single(host->dev, ptr, tr->size, tr->dir);
- if (dma_mapping_error(host->dev, tr->dma))
- return -EFAULT;
-
-
- sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS, tr->dma);
- }
-
- sdhci_write32(&host->sdhci, SDHCI_BLOCK_SIZE__BLOCK_COUNT, data->blocks << 16 | data->blocksize);
-
- return 0;
-}
-
-static int esdhc_do_data(struct fsl_esdhc_host *host, struct mci_data *data,
- struct fsl_esdhc_dma_transfer *tr)
-{
- u32 irqstat;
+ host->sdhci.sdma_boundary = 0;
if (esdhc_use_pio_mode())
- return sdhci_transfer_data(&host->sdhci, data);
-
- do {
- irqstat = sdhci_read32(&host->sdhci, SDHCI_INT_STATUS);
-
- if (irqstat & DATA_ERR)
- return -EIO;
-
- if (irqstat & SDHCI_INT_DATA_TIMEOUT)
- return -ETIMEDOUT;
- } while (!(irqstat & SDHCI_INT_XFER_COMPLETE) &&
- (sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & SDHCI_DATA_LINE_ACTIVE));
-
- dma_unmap_single(host->dev, tr->dma, tr->size, tr->dir);
+ sdhci_setup_data_pio(&host->sdhci, data);
+ else
+ sdhci_setup_data_dma(&host->sdhci, data, dma);
return 0;
}
@@ -156,7 +106,9 @@ static void __udelay(int us)
#define udelay(n) __udelay(n)
#undef dev_err
+#undef dev_dbg
#define dev_err(d, ...) pr_err(__VA_ARGS__)
+#define dev_dbg(d, ...) pr_debug(__VA_ARGS__)
#endif
@@ -173,7 +125,7 @@ int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd,
{
u32 xfertyp, mixctrl, command;
u32 irqstat;
- struct fsl_esdhc_dma_transfer tr = { 0 };
+ dma_addr_t dma = SDHCI_NO_DMA;
int ret;
sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, -1);
@@ -183,13 +135,13 @@ int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd,
/* Set up for a data transfer if we have one */
if (data) {
- ret = esdhc_setup_data(host, data, &tr);
+ ret = esdhc_setup_data(host, data, &dma);
if (ret)
return ret;
}
sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data,
- !esdhc_use_pio_mode(), &command, &xfertyp);
+ dma != SDHCI_NO_DMA, &command, &xfertyp);
if ((host->socdata->flags & ESDHC_FLAG_MULTIBLK_NO_INT) &&
(cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION))
@@ -203,6 +155,7 @@ int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd,
mixctrl = xfertyp;
/* Keep the bits 22-25 of the register as is */
mixctrl |= (sdhci_read32(&host->sdhci, IMX_SDHCI_MIXCTRL) & (0xF << 22));
+ mixctrl |= mci_timing_is_ddr(host->sdhci.timing) ? MIX_CTRL_DDREN : 0;
sdhci_write32(&host->sdhci, IMX_SDHCI_MIXCTRL, mixctrl);
}
@@ -219,7 +172,6 @@ int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd,
}
irqstat = sdhci_read32(&host->sdhci, SDHCI_INT_STATUS);
- sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, irqstat);
if (irqstat & CMD_ERR)
return -EIO;
@@ -246,7 +198,11 @@ int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd,
/* Wait until all of the blocks are transferred */
if (data) {
- ret = esdhc_do_data(host, data, &tr);
+ if (esdhc_use_pio_mode())
+ ret = sdhci_transfer_data_pio(&host->sdhci, data);
+ else
+ ret = sdhci_transfer_data_dma(&host->sdhci, data, dma);
+
if (ret)
return ret;
}
diff --git a/drivers/mci/imx-esdhc-pbl.c b/drivers/mci/imx-esdhc-pbl.c
index caaf1ac9b5..5b1d9a3cf4 100644
--- a/drivers/mci/imx-esdhc-pbl.c
+++ b/drivers/mci/imx-esdhc-pbl.c
@@ -1,13 +1,4 @@
-/*
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; version 2.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- */
+// SPDX-License-Identifier: GPL-2.0-only
#define pr_fmt(fmt) "xload-esdhc: " fmt
@@ -15,15 +6,22 @@
#include <io.h>
#include <mci.h>
#include <linux/sizes.h>
-#include <asm-generic/sections.h>
+#include <asm/sections.h>
#include <asm/cache.h>
-#include <mach/xload.h>
+#include <mach/imx/xload.h>
+#include <firmware.h>
+#include <asm/atf_common.h>
#ifdef CONFIG_ARCH_IMX
-#include <mach/atf.h>
-#include <mach/imx6-regs.h>
-#include <mach/imx8mq-regs.h>
-#include <mach/imx8mm-regs.h>
-#include <mach/imx-header.h>
+#include <mach/imx/atf.h>
+#include <mach/imx/imx6-regs.h>
+#include <mach/imx/imx7-regs.h>
+#include <mach/imx/imx8mq-regs.h>
+#include <mach/imx/imx8mm-regs.h>
+#include <mach/imx/imx-header.h>
+#endif
+#ifdef CONFIG_ARCH_LAYERSCAPE
+#include <mach/layerscape/xload.h>
+#include <mach/layerscape/layerscape.h>
#endif
#include "sdhci.h"
#include "imx-esdhc.h"
@@ -33,6 +31,40 @@
#define esdhc_send_cmd __esdhc_send_cmd
+static u8 ext_csd[512] __aligned(64);
+
+static int esdhc_send_ext_csd(struct fsl_esdhc_host *host)
+{
+ struct mci_cmd cmd;
+ struct mci_data data;
+
+ cmd.cmdidx = MMC_CMD_SEND_EXT_CSD;
+ cmd.cmdarg = 0;
+ cmd.resp_type = MMC_RSP_R1;
+
+ data.dest = ext_csd;
+ data.blocks = 1;
+ data.blocksize = sizeof(ext_csd);
+ data.flags = MMC_DATA_READ;
+
+ return esdhc_send_cmd(host, &cmd, &data);
+}
+
+static bool __maybe_unused esdhc_bootpart_active(struct fsl_esdhc_host *host)
+{
+ unsigned bootpart;
+
+ int ret = esdhc_send_ext_csd(host);
+ if (ret)
+ return false;
+
+ bootpart = (ext_csd[EXT_CSD_PARTITION_CONFIG] >> 3) & 0x7;
+ if (bootpart == 1 || bootpart == 2)
+ return true;
+
+ return false;
+}
+
static int esdhc_read_blocks(struct fsl_esdhc_host *host, void *dst, size_t len)
{
struct mci_cmd cmd;
@@ -75,122 +107,24 @@ static int esdhc_read_blocks(struct fsl_esdhc_host *host, void *dst, size_t len)
}
#ifdef CONFIG_ARCH_IMX
-static int esdhc_search_header(struct fsl_esdhc_host *host,
- struct imx_flash_header_v2 **header_pointer,
- void *buffer, u32 *offset)
+static int imx_read_blocks(void *dest, size_t len, void *priv)
{
- int ret;
- int i, header_count = 1;
- void *buf = buffer;
- struct imx_flash_header_v2 *hdr;
-
- for (i = 0; i < header_count; i++) {
- ret = esdhc_read_blocks(host, buf,
- *offset + SZ_1K + SECTOR_SIZE);
- if (ret)
- return ret;
-
- hdr = buf + *offset + SZ_1K;
-
- if (!is_imx_flash_header_v2(hdr)) {
- pr_debug("IVT header not found on SD card. "
- "Found tag: 0x%02x length: 0x%04x "
- "version: %02x\n",
- hdr->header.tag, hdr->header.length,
- hdr->header.version);
- return -EINVAL;
- }
-
- if (IS_ENABLED(CONFIG_ARCH_IMX8MQ) &&
- hdr->boot_data.plugin & PLUGIN_HDMI_IMAGE) {
- /*
- * In images that include signed HDMI
- * firmware, first v2 header would be
- * dedicated to that and would not contain any
- * useful for us information. In order for us
- * to pull the rest of the bootloader image
- * in, we need to re-read header from SD/MMC,
- * this time skipping anything HDMI firmware
- * related.
- */
- *offset += hdr->boot_data.size + hdr->header.length;
- header_count++;
- }
- }
- *header_pointer = hdr;
- return 0;
+ return esdhc_read_blocks(priv, dest, len);
}
static int
esdhc_load_image(struct fsl_esdhc_host *host, ptrdiff_t address,
- ptrdiff_t entry, u32 offset, bool start)
+ ptrdiff_t entry, u32 offset, u32 ivt_offset, bool start)
{
-
- void *buf = (void *)address;
- struct imx_flash_header_v2 *hdr = NULL;
- int ret, len;
- void __noreturn (*bb)(void);
- unsigned int ofs;
-
- len = imx_image_size();
- len = ALIGN(len, SECTOR_SIZE);
-
- ret = esdhc_search_header(host, &hdr, buf, &offset);
- if (ret)
- return ret;
-
- pr_debug("Check ok, loading image\n");
-
- ofs = offset + hdr->entry - hdr->boot_data.start;
-
- if (entry != address) {
- /*
- * Passing entry different from address is interpreted
- * as a request to place the image such that its entry
- * point would be exactly at 'entry', that is:
- *
- * buf + ofs = entry
- *
- * solving the above for 'buf' gvies us the
- * adjustement that needs to be made:
- *
- * buf = entry - ofs
- *
- */
- if (WARN_ON(entry - ofs < address)) {
- /*
- * We want to make sure we won't try to place
- * the start of the image before the beginning
- * of the memory buffer we were given in
- * address.
- */
- return -EINVAL;
- }
-
- buf = (void *)(entry - ofs);
- }
-
- ret = esdhc_read_blocks(host, buf, offset + len);
- if (ret) {
- pr_err("Loading image failed with %d\n", ret);
- return ret;
- }
-
- pr_debug("Image loaded successfully\n");
-
- if (!start)
- return 0;
-
- bb = buf + ofs;
-
- sync_caches_for_execution();
-
- bb();
+ return imx_load_image(address, entry, offset, ivt_offset, start,
+ SECTOR_SIZE, imx_read_blocks, host);
}
static void imx_esdhc_init(struct fsl_esdhc_host *host,
struct esdhc_soc_data *data)
{
+ u32 mixctrl;
+
data->flags = ESDHC_FLAG_USDHC;
host->socdata = data;
esdhc_populate_sdhci(host);
@@ -200,6 +134,10 @@ static void imx_esdhc_init(struct fsl_esdhc_host *host,
FIELD_PREP(WML_WR_WML_MASK, SECTOR_WML) |
FIELD_PREP(WML_RD_BRST_LEN, 16) |
FIELD_PREP(WML_RD_WML_MASK, SECTOR_WML));
+
+ mixctrl = sdhci_read32(&host->sdhci, IMX_SDHCI_MIXCTRL);
+ if (mixctrl & MIX_CTRL_DDREN)
+ host->sdhci.timing = MMC_TIMING_MMC_DDR52;
}
static int imx8m_esdhc_init(struct fsl_esdhc_host *host,
@@ -208,14 +146,14 @@ static int imx8m_esdhc_init(struct fsl_esdhc_host *host,
{
switch (instance) {
case 0:
- host->regs = IOMEM(MX8M_USDHC1_BASE_ADDR);
+ host->sdhci.base = IOMEM(MX8M_USDHC1_BASE_ADDR);
break;
case 1:
- host->regs = IOMEM(MX8M_USDHC2_BASE_ADDR);
+ host->sdhci.base = IOMEM(MX8M_USDHC2_BASE_ADDR);
break;
case 2:
/* Only exists on i.MX8MM, not on i.MX8MQ */
- host->regs = IOMEM(MX8MM_USDHC3_BASE_ADDR);
+ host->sdhci.base = IOMEM(MX8MM_USDHC3_BASE_ADDR);
break;
default:
return -EINVAL;
@@ -241,20 +179,20 @@ static int imx8m_esdhc_init(struct fsl_esdhc_host *host,
int imx6_esdhc_start_image(int instance)
{
struct esdhc_soc_data data;
- struct fsl_esdhc_host host;
+ struct fsl_esdhc_host host = { 0 };
switch (instance) {
case 0:
- host.regs = IOMEM(MX6_USDHC1_BASE_ADDR);
+ host.sdhci.base = IOMEM(MX6_USDHC1_BASE_ADDR);
break;
case 1:
- host.regs = IOMEM(MX6_USDHC2_BASE_ADDR);
+ host.sdhci.base = IOMEM(MX6_USDHC2_BASE_ADDR);
break;
case 2:
- host.regs = IOMEM(MX6_USDHC3_BASE_ADDR);
+ host.sdhci.base = IOMEM(MX6_USDHC3_BASE_ADDR);
break;
case 3:
- host.regs = IOMEM(MX6_USDHC4_BASE_ADDR);
+ host.sdhci.base = IOMEM(MX6_USDHC4_BASE_ADDR);
break;
default:
return -EINVAL;
@@ -262,26 +200,62 @@ int imx6_esdhc_start_image(int instance)
imx_esdhc_init(&host, &data);
- return esdhc_load_image(&host, 0x10000000, 0x10000000, 0, true);
+ return esdhc_load_image(&host, 0x10000000, 0x10000000, 0, SZ_1K, true);
}
/**
- * imx8m_esdhc_load_image - Load and optionally start an image from USDHC controller
+ * imx7_esdhc_start_image - Load and start an image from USDHC controller
* @instance: The USDHC controller instance (0..2)
- * @start: Whether to directly start the loaded image
*
* This uses esdhc_start_image() to load an image from SD/MMC. It is
* assumed that the image is the currently running barebox image (This
* information is used to calculate the length of the image). The
* image is started afterwards.
*
- * Return: If successful, this function does not return (if directly started)
- * or 0. A negative error code is returned when this function fails.
+ * Return: If successful, this function does not return. A negative error
+ * code is returned when this function fails.
+ */
+int imx7_esdhc_start_image(int instance)
+{
+ struct esdhc_soc_data data;
+ struct fsl_esdhc_host host = { 0 };
+
+ switch (instance) {
+ case 0:
+ host.sdhci.base = IOMEM(MX7_USDHC1_BASE_ADDR);
+ break;
+ case 1:
+ host.sdhci.base = IOMEM(MX7_USDHC2_BASE_ADDR);
+ break;
+ case 2:
+ host.sdhci.base = IOMEM(MX7_USDHC3_BASE_ADDR);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ imx_esdhc_init(&host, &data);
+
+ return esdhc_load_image(&host, 0x80000000, 0x80000000, 0, SZ_1K, true);
+}
+
+/**
+ * imx8m_esdhc_load_image - Load and optionally start an image from USDHC controller
+ * @instance: The USDHC controller instance (0..2)
+ * @bl33: Where to load the bl33 barebox image
+ *
+ * This uses esdhc_start_image() to load an image from SD/MMC. It is
+ * assumed that the image is the currently running barebox image (This
+ * information is used to calculate the length of the image). The
+ * image is not started afterwards.
+ *
+ * Return: If image successfully loaded, returns 0.
+ * A negative error code is returned when this function fails.
*/
-int imx8m_esdhc_load_image(int instance, bool start)
+int imx8m_esdhc_load_image(int instance, void *bl33)
{
struct esdhc_soc_data data;
- struct fsl_esdhc_host host;
+ struct fsl_esdhc_host host = { 0 };
int ret;
ret = imx8m_esdhc_init(&host, &data, instance);
@@ -289,11 +263,78 @@ int imx8m_esdhc_load_image(int instance, bool start)
return ret;
return esdhc_load_image(&host, MX8M_DDR_CSD1_BASE_ADDR,
- MX8MQ_ATF_BL33_BASE_ADDR, SZ_32K, start);
+ (ptrdiff_t)bl33, SZ_32K, SZ_1K,
+ false);
}
+
+/**
+ * imx8mp_esdhc_load_image - Load and optionally start an image from USDHC controller
+ * @instance: The USDHC controller instance (0..2)
+ * @bl33: Where to load the bl33 barebox image
+ *
+ * This uses esdhc_start_image() to load an image from SD/MMC. It is
+ * assumed that the image is the currently running barebox image (This
+ * information is used to calculate the length of the image). The
+ * image is not started afterwards.
+ *
+ * Return: If image successfully loaded, returns 0.
+ * A negative error code is returned when this function fails.
+ */
+int imx8mp_esdhc_load_image(int instance, void *bl33)
+{
+ struct esdhc_soc_data data;
+ struct fsl_esdhc_host host = { 0 };
+ u32 offset;
+ int ret;
+
+ ret = imx8m_esdhc_init(&host, &data, instance);
+ if (ret)
+ return ret;
+
+ offset = esdhc_bootpart_active(&host)? 0 : SZ_32K;
+
+ return esdhc_load_image(&host, MX8M_DDR_CSD1_BASE_ADDR,
+ (ptrdiff_t)bl33, offset, 0, false);
+}
+
+int imx8mn_esdhc_load_image(int instance, void *bl33)
+ __alias(imx8mp_esdhc_load_image);
#endif
-#ifdef CONFIG_ARCH_LS1046
+#ifdef CONFIG_ARCH_LAYERSCAPE
+
+static int layerscape_esdhc_load_image(struct fsl_esdhc_host *host, void *adr, unsigned long size,
+ uint32_t div_val)
+{
+ uint32_t val;
+ int ret;
+
+ esdhc_populate_sdhci(host);
+ sdhci_write32(&host->sdhci, IMX_SDHCI_WML, 0);
+
+ /*
+ * The ROM leaves us here with a clock frequency of around 400kHz. Speed
+ * this up a bit. FIXME: The resulting frequency has not yet been verified
+ * to work on all cards.
+ */
+ val = sdhci_read32(&host->sdhci, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET);
+ val &= ~0x0000fff0;
+ val |= div_val;
+ sdhci_write32(&host->sdhci, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, val);
+
+ sdhci_write32(&host->sdhci, ESDHC_DMA_SYSCTL,
+ ESDHC_SYSCTL_DMA_SNOOP | ESDHC_SYSCTL_PERIPHERAL_CLK_SEL);
+
+ ret = esdhc_read_blocks(host, adr, size);
+ if (ret) {
+ pr_err("%s: reading blocks failed with: %d\n", __func__, ret);
+ return ret;
+ }
+
+ sync_caches_for_execution();
+
+ return 0;
+}
/*
* The image on the SD card starts at 0x1000. We reserved 128KiB for the PBL,
@@ -314,46 +355,76 @@ int imx8m_esdhc_load_image(int instance, bool start)
int ls1046a_esdhc_start_image(unsigned long r0, unsigned long r1, unsigned long r2)
{
int ret;
- uint32_t val;
struct esdhc_soc_data data = {
- .flags = ESDHC_FLAG_BIGENDIAN,
+ .flags = ESDHC_FLAG_MULTIBLK_NO_INT | ESDHC_FLAG_BIGENDIAN,
};
struct fsl_esdhc_host host = {
- .regs = IOMEM(0x01560000),
+ .sdhci.base = IOMEM(0x01560000),
.socdata = &data,
};
- unsigned long sdram = 0x80000000;
+ void *sdram = (void *)0x80000000;
+ unsigned long size = ALIGN(barebox_image_size + LS1046A_SD_IMAGE_OFFSET, 512);
void (*barebox)(unsigned long, unsigned long, unsigned long) =
- (void *)(sdram + LS1046A_SD_IMAGE_OFFSET);
+ (sdram + LS1046A_SD_IMAGE_OFFSET);
- esdhc_populate_sdhci(&host);
- sdhci_write32(&host.sdhci, IMX_SDHCI_WML, 0);
+ ret = layerscape_esdhc_load_image(&host, sdram, size, (8 << 8) | (3 << 4));
+ if (ret)
+ return ret;
- /*
- * The ROM leaves us here with a clock frequency of around 400kHz. Speed
- * this up a bit. FIXME: The resulting frequency has not yet been verified
- * to work on all cards.
- */
- val = sdhci_read32(&host.sdhci, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET);
- val &= ~0x0000fff0;
- val |= (8 << 8) | (3 << 4);
- sdhci_write32(&host.sdhci, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET, val);
+ printf("Starting barebox\n");
- sdhci_write32(&host.sdhci, ESDHC_DMA_SYSCTL, ESDHC_SYSCTL_DMA_SNOOP);
+ barebox(r0, r1, r2);
- ret = esdhc_read_blocks(&host, (void *)sdram,
- ALIGN(barebox_image_size + LS1046A_SD_IMAGE_OFFSET, 512));
- if (ret) {
- pr_err("%s: reading blocks failed with: %d\n", __func__, ret);
+ return -EINVAL;
+}
+
+static int ls1028a_esdhc_start_image(void __iomem *base, struct dram_regions_info *dram_info)
+{
+ struct esdhc_soc_data data = {
+ .flags = ESDHC_FLAG_MULTIBLK_NO_INT,
+ };
+ struct fsl_esdhc_host host = {
+ .sdhci.base = base,
+ .socdata = &data,
+ };
+ void *sdram = (void *)0x80000000;
+ void (*bl31)(void) = (void *)LS1028A_TFA_RESERVED_START;
+ size_t bl31_size;
+ void *bl31_image;
+ struct bl2_to_bl31_params_mem_v2 *params;
+ unsigned long size = ALIGN(barebox_image_size + LS1046A_SD_IMAGE_OFFSET, 512);
+ void (*barebox)(unsigned long, unsigned long, unsigned long) =
+ (sdram + LS1046A_SD_IMAGE_OFFSET);
+ int ret;
+
+ ret = layerscape_esdhc_load_image(&host, sdram, size, 8 << 4);
+ if (ret)
return ret;
- }
- sync_caches_for_execution();
+ get_builtin_firmware_ext(ls1028a_bl31_bin, barebox, &bl31_image, &bl31_size);
+ memcpy(bl31, bl31_image, bl31_size);
- printf("Starting barebox\n");
+ /* Setup an initial stack for EL2 */
+ asm volatile("msr sp_el2, %0" : : "r" ((unsigned long)barebox - 16) : "cc");
- barebox(r0, r1, r2);
+ params = bl2_plat_get_bl31_params_v2(0, (uintptr_t)barebox, 0);
+ params->bl31_ep_info.args.arg3 = (unsigned long)dram_info;
+
+ printf("Starting bl31\n");
+
+ bl31_entry_v2((uintptr_t)bl31, &params->bl_params, NULL);
return -EINVAL;
}
+
+int ls1028a_esdhc1_start_image(struct dram_regions_info *dram_info)
+{
+ return ls1028a_esdhc_start_image(IOMEM(0x2140000), dram_info);
+}
+
+int ls1028a_esdhc2_start_image(struct dram_regions_info *dram_info)
+{
+ return ls1028a_esdhc_start_image(IOMEM(0x2150000), dram_info);
+}
+
#endif
diff --git a/drivers/mci/imx-esdhc.c b/drivers/mci/imx-esdhc.c
index dc8fab73d3..fb52c7b893 100644
--- a/drivers/mci/imx-esdhc.c
+++ b/drivers/mci/imx-esdhc.c
@@ -1,25 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2007,2010 Freescale Semiconductor, Inc
+// SPDX-FileCopyrightText: 2003 Kyle Harris <kharris@nexus-tech.net>, Nexus Technologies
+
/*
- * Copyright 2007,2010 Freescale Semiconductor, Inc
* Andy Fleming
- *
- * Based vaguely on the pxa mmc code:
- * (C) Copyright 2003
- * Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
+
#include <config.h>
#include <common.h>
#include <dma.h>
@@ -35,6 +21,8 @@
#include <platform_data/mmc-esdhc-imx.h>
#include <gpio.h>
#include <of_device.h>
+#include <mach/imx/generic.h>
+#include <mach/imx/imx53-regs.h>
#include "sdhci.h"
#include "imx-esdhc.h"
@@ -57,33 +45,35 @@ esdhc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd, struct mci_data *data)
return __esdhc_send_cmd(host, cmd, data);
}
-static void set_sysctl(struct mci_host *mci, u32 clock)
+static void set_sysctl(struct mci_host *mci, u32 clock, bool ddr)
{
- int div, pre_div;
+ int div, pre_div, ddr_pre_div = 1;
struct fsl_esdhc_host *host = to_fsl_esdhc(mci);
int sdhc_clk = clk_get_rate(host->clk);
u32 clk;
unsigned long cur_clock;
- /*
- * With eMMC and imx53 (sdhc_clk=200MHz) a pre_div of 1 results in
- * pre_div=1,div=4 (=50MHz)
- * which is valid and should work, but somehow doesn't.
- * Starting with pre_div=2 gives
- * pre_div=2, div=2 (=50MHz)
- * and works fine.
- */
- pre_div = 2;
+ if (esdhc_is_usdhc(host) && ddr)
+ ddr_pre_div = 2;
+
+ if (esdhc_is_layerscape(host))
+ sdhc_clk >>= 1;
+
+ /* For i.MX53 eSDHCv3, SYSCTL.SDCLKFS may not be set to 0. */
+ if (cpu_is_mx53() && host->sdhci.base == (void *)MX53_ESDHC3_BASE_ADDR)
+ pre_div = 2;
+ else
+ pre_div = 1;
if (sdhc_clk == clock)
pre_div = 1;
else if (sdhc_clk / 16 > clock)
for (; pre_div < 256; pre_div *= 2)
- if ((sdhc_clk / pre_div) <= (clock * 16))
+ if ((sdhc_clk / (pre_div * ddr_pre_div)) <= (clock * 16))
break;
for (div = 1; div <= 16; div++)
- if ((sdhc_clk / (div * pre_div)) <= clock)
+ if ((sdhc_clk / (div * pre_div * ddr_pre_div)) <= clock)
break;
cur_clock = sdhc_clk / pre_div / div;
@@ -117,12 +107,70 @@ static void set_sysctl(struct mci_host *mci, u32 clock)
10 * MSECOND);
}
+static void usdhc_set_timing(struct fsl_esdhc_host *host, enum mci_timing timing)
+{
+ u32 mixctrl;
+
+ mixctrl = sdhci_read32(&host->sdhci, IMX_SDHCI_MIXCTRL);
+ mixctrl &= ~MIX_CTRL_DDREN;
+
+ switch (timing) {
+ case MMC_TIMING_UHS_DDR50:
+ case MMC_TIMING_MMC_DDR52:
+ mixctrl |= MIX_CTRL_DDREN;
+ sdhci_write32(&host->sdhci, IMX_SDHCI_MIXCTRL, mixctrl);
+ break;
+ default:
+ sdhci_write32(&host->sdhci, IMX_SDHCI_MIXCTRL, mixctrl);
+ }
+
+ host->sdhci.timing = timing;
+}
+
+static void layerscape_set_timing(struct fsl_esdhc_host *host, enum mci_timing timing)
+{
+ esdhc_clrbits32(host, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
+ SYSCTL_CKEN);
+
+ switch (timing) {
+ case MMC_TIMING_UHS_DDR50:
+ case MMC_TIMING_MMC_DDR52:
+ esdhc_clrsetbits32(host, SDHCI_ACMD12_ERR__HOST_CONTROL2,
+ SDHCI_ACMD12_ERR__HOST_CONTROL2_UHSM,
+ FIELD_PREP(SDHCI_ACMD12_ERR__HOST_CONTROL2_UHSM, 4));
+ break;
+ default:
+ esdhc_clrbits32(host, SDHCI_ACMD12_ERR__HOST_CONTROL2,
+ SDHCI_ACMD12_ERR__HOST_CONTROL2_UHSM);
+ break;
+ }
+
+ esdhc_setbits32(host, SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET,
+ SYSCTL_CKEN);
+
+ host->sdhci.timing = timing;
+}
+
static void esdhc_set_ios(struct mci_host *mci, struct mci_ios *ios)
{
struct fsl_esdhc_host *host = to_fsl_esdhc(mci);
+ /*
+ * call esdhc_set_timing() before update the clock rate,
+ * This is because current we support DDR and SDR timing,
+ * Once the DDR_EN bit is set, the card clock will be
+ * divide by 2 automatically. So need to do this before
+ * setting clock rate.
+ */
+ if (host->sdhci.timing != ios->timing) {
+ if (esdhc_is_usdhc(host))
+ usdhc_set_timing(host, ios->timing);
+ else if (esdhc_is_layerscape(host))
+ layerscape_set_timing(host, ios->timing);
+ }
+
/* Set the clock speed */
- set_sysctl(mci, ios->clock);
+ set_sysctl(mci, ios->clock, mci_timing_is_ddr(ios->timing));
/* Set the bus width */
esdhc_clrbits32(host, SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL,
@@ -200,7 +248,7 @@ static int esdhc_reset(struct fsl_esdhc_host *host)
return 0;
}
-static int esdhc_init(struct mci_host *mci, struct device_d *dev)
+static int esdhc_init(struct mci_host *mci, struct device *dev)
{
struct fsl_esdhc_host *host = to_fsl_esdhc(mci);
int ret;
@@ -215,12 +263,13 @@ static int esdhc_init(struct mci_host *mci, struct device_d *dev)
/* RSTA doesn't reset MMC_BOOT register, so manually reset it */
sdhci_write32(&host->sdhci, SDHCI_MMC_BOOT, 0);
- /* Enable cache snooping */
- if (host->socdata->flags & ESDHC_FLAG_CACHE_SNOOPING)
- esdhc_setbits32(host, ESDHC_DMA_SYSCTL, ESDHC_SYSCTL_DMA_SNOOP);
+ if (esdhc_is_layerscape(host))
+ esdhc_setbits32(host, ESDHC_DMA_SYSCTL,
+ ESDHC_SYSCTL_DMA_SNOOP | /* Enable cache snooping */
+ ESDHC_SYSCTL_PERIPHERAL_CLK_SEL);
/* Set the initial clock speed */
- set_sysctl(mci, 400000);
+ set_sysctl(mci, 400000, false);
sdhci_write32(&host->sdhci, SDHCI_INT_ENABLE, SDHCI_INT_CMD_COMPLETE |
SDHCI_INT_XFER_COMPLETE | SDHCI_INT_CARD_INT |
@@ -239,19 +288,11 @@ static int esdhc_init(struct mci_host *mci, struct device_d *dev)
return ret;
}
-static int fsl_esdhc_detect(struct device_d *dev)
-{
- struct fsl_esdhc_host *host = dev->priv;
-
- return mci_detect_card(&host->mci);
-}
-
-static int fsl_esdhc_probe(struct device_d *dev)
+static int fsl_esdhc_probe(struct device *dev)
{
struct resource *iores;
struct fsl_esdhc_host *host;
struct mci_host *mci;
- u32 caps;
int ret;
unsigned long rate;
struct esdhc_platform_data *pdata = dev->platform_data;
@@ -286,35 +327,29 @@ static int fsl_esdhc_probe(struct device_d *dev)
ret = PTR_ERR(iores);
goto err_clk_disable;
}
- host->regs = IOMEM(iores->start);
+ host->sdhci.base = IOMEM(iores->start);
esdhc_populate_sdhci(host);
- caps = sdhci_read32(&host->sdhci, SDHCI_CAPABILITIES);
-
- if (caps & ESDHC_HOSTCAPBLT_VS18)
- mci->voltages |= MMC_VDD_165_195;
- if (caps & ESDHC_HOSTCAPBLT_VS30)
- mci->voltages |= MMC_VDD_29_30 | MMC_VDD_30_31;
- if (caps & ESDHC_HOSTCAPBLT_VS33)
- mci->voltages |= MMC_VDD_32_33 | MMC_VDD_33_34;
-
if (pdata) {
mci->host_caps = pdata->caps;
if (pdata->devname)
mci->devname = pdata->devname;
}
- if (caps & ESDHC_HOSTCAPBLT_HSS)
- mci->host_caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
-
host->mci.send_cmd = esdhc_send_cmd;
host->mci.set_ios = esdhc_set_ios;
host->mci.init = esdhc_init;
host->mci.card_present = esdhc_card_present;
host->mci.hw_dev = dev;
+ host->sdhci.mci = &host->mci;
- dev->detect = fsl_esdhc_detect;
+ ret = sdhci_setup_host(&host->sdhci);
+ if (ret)
+ goto err_clk_disable;
+
+ if (esdhc_is_usdhc(host) || esdhc_is_layerscape(host))
+ mci->host_caps |= MMC_CAP_MMC_3_3V_DDR | MMC_CAP_MMC_1_8V_DDR;
rate = clk_get_rate(host->clk);
host->mci.f_min = rate >> 12;
@@ -328,8 +363,6 @@ static int fsl_esdhc_probe(struct device_d *dev)
mci_of_parse(&host->mci);
- dev->priv = host;
-
ret = mci_register(&host->mci);
if (ret)
goto err_release_res;
@@ -375,9 +408,13 @@ static struct esdhc_soc_data usdhc_imx6sx_data = {
.clkidx = "per",
};
-static struct esdhc_soc_data esdhc_ls_data = {
+static struct esdhc_soc_data esdhc_ls_be_data = {
.flags = ESDHC_FLAG_MULTIBLK_NO_INT | ESDHC_FLAG_BIGENDIAN |
- ESDHC_FLAG_CACHE_SNOOPING,
+ ESDHC_FLAG_LAYERSCAPE,
+};
+
+static struct esdhc_soc_data esdhc_ls_le_data = {
+ .flags = ESDHC_FLAG_MULTIBLK_NO_INT | ESDHC_FLAG_LAYERSCAPE,
};
static __maybe_unused struct of_device_id fsl_esdhc_compatible[] = {
@@ -390,9 +427,13 @@ static __maybe_unused struct of_device_id fsl_esdhc_compatible[] = {
{ .compatible = "fsl,imx6sx-usdhc", .data = &usdhc_imx6sx_data },
{ .compatible = "fsl,imx8mq-usdhc", .data = &usdhc_imx6sx_data },
{ .compatible = "fsl,imx8mm-usdhc", .data = &usdhc_imx6sx_data },
- { .compatible = "fsl,ls1046a-esdhc",.data = &esdhc_ls_data },
+ { .compatible = "fsl,imx8mn-usdhc", .data = &usdhc_imx6sx_data },
+ { .compatible = "fsl,imx8mp-usdhc", .data = &usdhc_imx6sx_data },
+ { .compatible = "fsl,ls1028a-esdhc",.data = &esdhc_ls_le_data },
+ { .compatible = "fsl,ls1046a-esdhc",.data = &esdhc_ls_be_data },
{ /* sentinel */ }
};
+MODULE_DEVICE_TABLE(of, fsl_esdhc_compatible);
static struct platform_device_id imx_esdhc_ids[] = {
{
@@ -406,7 +447,7 @@ static struct platform_device_id imx_esdhc_ids[] = {
}
};
-static struct driver_d fsl_esdhc_driver = {
+static struct driver fsl_esdhc_driver = {
.name = "imx-esdhc",
.probe = fsl_esdhc_probe,
.of_compatible = DRV_OF_COMPAT(fsl_esdhc_compatible),
diff --git a/drivers/mci/imx-esdhc.h b/drivers/mci/imx-esdhc.h
index 4bf890edf9..eff556f2ff 100644
--- a/drivers/mci/imx-esdhc.h
+++ b/drivers/mci/imx-esdhc.h
@@ -1,23 +1,7 @@
-/*
- * FSL SD/MMC Defines
- *-------------------------------------------------------------------
- *
- * Copyright 2007-2008,2010 Freescale Semiconductor, Inc
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
- *-------------------------------------------------------------------
- *
- */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* SPDX-FileCopyrightText: 2007-2008,2010 Freescale Semiconductor, Inc */
+
+/* FSL SD/MMC Defines */
#ifndef __FSL_ESDHC_H__
#define __FSL_ESDHC_H__
@@ -53,23 +37,32 @@
#define BLKATTR_SIZE(x) (x & 0x1fff)
#define MAX_BLK_CNT 0x7fff /* so malloc will have enough room with 32M */
-#define ESDHC_HOSTCAPBLT_VS18 0x04000000
-#define ESDHC_HOSTCAPBLT_VS30 0x02000000
-#define ESDHC_HOSTCAPBLT_VS33 0x01000000
-#define ESDHC_HOSTCAPBLT_SRS 0x00800000
-#define ESDHC_HOSTCAPBLT_DMAS 0x00400000
-#define ESDHC_HOSTCAPBLT_HSS 0x00200000
-
#define PIO_TIMEOUT 100000
+#define SDHCI_ACMD12_ERR__HOST_CONTROL2_UHSM GENMASK(18, 16) /* Layerscape specific */
+
#define IMX_SDHCI_WML 0x44
#define IMX_SDHCI_MIXCTRL 0x48
+/* Imported from Linux Kernel drivers/mmc/host/sdhci-esdhc-imx.c */
+#define MIX_CTRL_DDREN BIT(3)
+#define MIX_CTRL_DTDSEL_READ BIT(4)
+#define MIX_CTRL_AC23EN BIT(7)
+#define MIX_CTRL_EXE_TUNE BIT(22)
+#define MIX_CTRL_SMPCLK_SEL BIT(23)
+#define MIX_CTRL_AUTO_TUNE_EN BIT(24)
+#define MIX_CTRL_FBCLK_SEL BIT(25)
+#define MIX_CTRL_HS400_EN BIT(26)
+#define MIX_CTRL_HS400_ES BIT(27)
+/* Bits 3 and 6 are not SDHCI standard definitions */
+#define MIX_CTRL_SDHCI_MASK 0xb7
+/* Tuning bits */
+#define MIX_CTRL_TUNING_MASK 0x03c00000
#define IMX_SDHCI_DLL_CTRL 0x60
#define IMX_SDHCI_MIX_CTRL_FBCLK_SEL BIT(25)
#define ESDHC_DMA_SYSCTL 0x40c /* Layerscape specific */
#define ESDHC_SYSCTL_DMA_SNOOP BIT(6)
-
+#define ESDHC_SYSCTL_PERIPHERAL_CLK_SEL BIT(19)
/*
* The CMDTYPE of the CMD register (offset 0xE) should be set to
@@ -112,8 +105,8 @@
#define ESDHC_FLAG_HS400 BIT(9)
/* Need to access registers in bigendian mode */
#define ESDHC_FLAG_BIGENDIAN BIT(10)
-/* Enable cache snooping */
-#define ESDHC_FLAG_CACHE_SNOOPING BIT(11)
+/* Layerscape variant ls1046a, ls1028a, ls1088a, revisit for ls1012a */
+#define ESDHC_FLAG_LAYERSCAPE BIT(11)
struct esdhc_soc_data {
u32 flags;
@@ -123,8 +116,7 @@ struct esdhc_soc_data {
struct fsl_esdhc_host {
struct mci_host mci;
struct clk *clk;
- struct device_d *dev;
- void __iomem *regs;
+ struct device *dev;
const struct esdhc_soc_data *socdata;
struct sdhci sdhci;
};
@@ -134,6 +126,11 @@ static inline int esdhc_is_usdhc(struct fsl_esdhc_host *data)
return !!(data->socdata->flags & ESDHC_FLAG_USDHC);
}
+static inline int esdhc_is_layerscape(struct fsl_esdhc_host *data)
+{
+ return !!(data->socdata->flags & ESDHC_FLAG_LAYERSCAPE);
+}
+
static inline struct fsl_esdhc_host *sdhci_to_esdhc(struct sdhci *sdhci)
{
return container_of(sdhci, struct fsl_esdhc_host, sdhci);
diff --git a/drivers/mci/imx.c b/drivers/mci/imx.c
index 354daba05d..48a3378335 100644
--- a/drivers/mci/imx.c
+++ b/drivers/mci/imx.c
@@ -1,19 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2009 Ilya Yanok <yanok@emcraft.com>
+// SPDX-FileCopyrightText: 2008 Sascha Hauer <s.hauer@pengutronix.de>, Pengutronix
+// SPDX-FileCopyrightText: 2006 Pavel Pisa <ppisa@pikron.com>, PiKRON
+
/*
- * This is a driver for the SDHC controller found in Freescale MX2/MX3
- * SoCs. It is basically the same hardware as found on MX1 (imxmmc.c).
- * Unlike the hardware found on MX1, this hardware just works and does
- * not need all the quirks found in imxmmc.c, hence the separate driver.
- *
- * Copyright (C) 2009 Ilya Yanok, <yanok@emcraft.com>
- * Copyright (C) 2008 Sascha Hauer, Pengutronix <s.hauer@pengutronix.de>
- * Copyright (C) 2006 Pavel Pisa, PiKRON <ppisa@pikron.com>
- *
- * derived from pxamci.c by Russell King
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
+ * This is a driver for the SDHC controller found in Freescale MX2/MX3
+ * SoCs. It is basically the same hardware as found on MX1 (imxmmc.c).
+ * Unlike the hardware found on MX1, this hardware just works and does
+ * not need all the quirks found in imxmmc.c, hence the separate driver.
*
+ * Derived from pxamci.c by Russell King
*/
#include <config.h>
@@ -469,7 +465,7 @@ static void mxcmci_set_ios(struct mci_host *mci, struct mci_ios *ios)
host->clock = ios->clock;
}
-static int mxcmci_init(struct mci_host *mci, struct device_d *dev)
+static int mxcmci_init(struct mci_host *mci, struct device *dev)
{
struct mxcmci_host *host = to_mxcmci(mci);
@@ -490,7 +486,7 @@ static int mxcmci_init(struct mci_host *mci, struct device_d *dev)
return 0;
}
-static int mxcmci_probe(struct device_d *dev)
+static int mxcmci_probe(struct device *dev)
{
struct resource *iores;
struct mxcmci_host *host;
@@ -531,8 +527,9 @@ static __maybe_unused struct of_device_id mxcmci_compatible[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, mxcmci_compatible);
-static struct driver_d mxcmci_driver = {
+static struct driver mxcmci_driver = {
.name = DRIVER_NAME,
.probe = mxcmci_probe,
.of_compatible = DRV_OF_COMPAT(mxcmci_compatible),
diff --git a/drivers/mci/mci-bcm2835.c b/drivers/mci/mci-bcm2835.c
index b18d681870..3546cc3a32 100644
--- a/drivers/mci/mci-bcm2835.c
+++ b/drivers/mci/mci-bcm2835.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Raspberry PI MCI driver
*
@@ -9,22 +10,6 @@
* timing workarounds) obviously extracted from the Linux kernel at:
* https://github.com/raspberrypi/linux.git rpi-3.6.y
*
- * The Linux kernel code has the following (c) and license, which is hence
- * propagated to here:
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
- *
* Author: Wilhelm Lundgren <wilhelm.lundgren@cybercom.com>
*/
@@ -45,7 +30,7 @@ static int twoticks_delay;
struct bcm2835_mci_host {
struct mci_host mci;
void __iomem *regs;
- struct device_d *hw_dev;
+ struct device *hw_dev;
int bus_width;
u32 clock;
u32 max_clock;
@@ -84,16 +69,20 @@ static u32 bcm2835_sdhci_read32(struct sdhci *sdhci, int reg)
return readl(host->regs + reg);
}
-static u32 bcm2835_mci_wait_command_done(struct bcm2835_mci_host *host)
+static int bcm2835_mci_wait_command_done(struct bcm2835_mci_host *host)
{
u32 interrupt = 0;
+ uint64_t start;
+ start = get_time_ns();
while (true) {
interrupt = sdhci_read32(&host->sdhci, SDHCI_INT_STATUS);
if (interrupt & SDHCI_INT_INDEX)
return -EPERM;
if (interrupt & SDHCI_INT_CMD_COMPLETE)
break;
+ if (is_timeout(start, SECOND))
+ return -ETIMEDOUT;
}
return 0;
}
@@ -127,8 +116,8 @@ static void bcm2835_mci_reset_emmc(struct bcm2835_mci_host *host, u32 reset,
*/
static int bcm2835_mci_request(struct mci_host *mci, struct mci_cmd *cmd,
struct mci_data *data) {
- u32 command, block_data = 0, ret = 0, transfer_mode = 0;
- u32 wait_inhibit_mask = SDHCI_CMD_INHIBIT_CMD | SDHCI_CMD_INHIBIT_DATA;
+ u32 command, block_data = 0, transfer_mode = 0;
+ int ret;
struct bcm2835_mci_host *host = to_bcm2835_host(mci);
sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data, false,
@@ -139,15 +128,9 @@ static int bcm2835_mci_request(struct mci_host *mci, struct mci_cmd *cmd,
block_data |= data->blocksize;
}
- /* We shouldn't wait for data inihibit for stop commands, even
- though they might use busy signaling */
- if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
- wait_inhibit_mask = SDHCI_CMD_INHIBIT_CMD;
-
- /*Wait for old command*/
- while (sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE)
- & wait_inhibit_mask)
- ;
+ ret = sdhci_wait_idle_data(&host->sdhci, cmd);
+ if (ret)
+ return ret;
sdhci_write32(&host->sdhci, SDHCI_INT_ENABLE, 0xFFFFFFFF);
sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, 0xFFFFFFFF);
@@ -158,7 +141,7 @@ static int bcm2835_mci_request(struct mci_host *mci, struct mci_cmd *cmd,
command << 16 | transfer_mode);
ret = bcm2835_mci_wait_command_done(host);
- if (ret) {
+ if (ret && ret != -ETIMEDOUT) {
dev_err(host->hw_dev, "Error while executing command %d\n",
cmd->cmdidx);
dev_err(host->hw_dev, "Status: 0x%X, Interrupt: 0x%X\n",
@@ -172,7 +155,7 @@ static int bcm2835_mci_request(struct mci_host *mci, struct mci_cmd *cmd,
}
if (!ret && data)
- ret = sdhci_transfer_data(&host->sdhci, data);
+ ret = sdhci_transfer_data_pio(&host->sdhci, data);
sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, 0xFFFFFFFF);
if (ret) {
@@ -242,6 +225,13 @@ static void bcm2835_mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL);
switch (ios->bus_width) {
+ case MMC_BUS_WIDTH_8:
+ sdhci_write32(&host->sdhci,
+ SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL,
+ (current_val & ~CONTROL0_4DATA) | CONTROL0_8DATA);
+ host->bus_width = 2;
+ dev_dbg(host->hw_dev, "Changing bus width to 8\n");
+ break;
case MMC_BUS_WIDTH_4:
sdhci_write32(&host->sdhci,
SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL,
@@ -311,7 +301,7 @@ static void bcm2835_mci_set_ios(struct mci_host *mci, struct mci_ios *ios)
host->bus_width, host->clock);
}
-static int bcm2835_mci_reset(struct mci_host *mci, struct device_d *mci_dev)
+static int bcm2835_mci_reset(struct mci_host *mci, struct device *mci_dev)
{
struct bcm2835_mci_host *host;
u32 ret = 0;
@@ -334,7 +324,7 @@ static int bcm2835_mci_reset(struct mci_host *mci, struct device_d *mci_dev)
sdhci_write32(&host->sdhci,
SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL,
- 0x00);
+ (SDHCI_BUS_VOLTAGE_330 | SDHCI_BUS_POWER_EN) << 8);
sdhci_write32(&host->sdhci, SDHCI_ACMD12_ERR__HOST_CONTROL2,
0x00);
sdhci_write32(&host->sdhci,
@@ -360,20 +350,10 @@ static int bcm2835_mci_reset(struct mci_host *mci, struct device_d *mci_dev)
sdhci_write32(&host->sdhci, SDHCI_INT_STATUS,
0xFFFFFFFF);
- /*Now write command 0 and see if we get response*/
- sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, 0x0);
- sdhci_write32(&host->sdhci, SDHCI_TRANSFER_MODE__COMMAND, 0x0);
- return bcm2835_mci_wait_command_done(host);
-}
-
-static int bcm2835_mci_detect(struct device_d *dev)
-{
- struct bcm2835_mci_host *host = dev->priv;
-
- return mci_detect_card(&host->mci);
+ return 0;
}
-static int bcm2835_mci_probe(struct device_d *hw_dev)
+static int bcm2835_mci_probe(struct device *hw_dev)
{
struct resource *iores;
struct bcm2835_mci_host *host;
@@ -422,9 +402,6 @@ static int bcm2835_mci_probe(struct device_d *hw_dev)
host->mci.f_min = MIN_FREQ;
host->mci.f_max = host->max_clock;
- hw_dev->priv = host;
- hw_dev->detect = bcm2835_mci_detect,
-
/*
* The Arasan has a bugette whereby it may lose the content of
* successive writes to registers that are within two SD-card clock
@@ -447,18 +424,17 @@ static __maybe_unused struct of_device_id bcm2835_mci_compatible[] = {
{
.compatible = "brcm,bcm2835-sdhci",
}, {
+ .compatible = "brcm,bcm2711-emmc2",
+ }, {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, bcm2835_mci_compatible);
-static struct driver_d bcm2835_mci_driver = {
+static struct driver bcm2835_mci_driver = {
.name = "bcm2835_mci",
.probe = bcm2835_mci_probe,
.of_compatible = DRV_OF_COMPAT(bcm2835_mci_compatible),
};
-static int bcm2835_mci_add(void)
-{
- return platform_driver_register(&bcm2835_mci_driver);
-}
-device_initcall(bcm2835_mci_add);
+device_platform_driver(bcm2835_mci_driver);
diff --git a/drivers/mci/mci-bcm2835.h b/drivers/mci/mci-bcm2835.h
index 2c95e03c64..71448642ad 100644
--- a/drivers/mci/mci-bcm2835.h
+++ b/drivers/mci/mci-bcm2835.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
#define BCM2835_MCI_SLOTISR_VER 0xfc
#define MIN_FREQ 400000
@@ -9,6 +10,7 @@
#define CONTROL0_HISPEED (1 << 2)
#define CONTROL0_4DATA (1 << 1)
+#define CONTROL0_8DATA (1 << 5)
#define CONTROL1_DATARST (1 << 26)
#define CONTROL1_CMDRST (1 << 25)
diff --git a/drivers/mci/mci-core.c b/drivers/mci/mci-core.c
index 34aaee54f9..083d2f4ed1 100644
--- a/drivers/mci/mci-core.c
+++ b/drivers/mci/mci-core.c
@@ -1,26 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2010 Juergen Beisert, Pengutronix
+// SPDX-FileCopyrightText: 2008 Freescale Semiconductor, Inc
+
/*
- * (C) Copyright 2010 Juergen Beisert, Pengutronix
- *
- * This code is havily inspired and in parts from the u-boot project:
- *
- * Copyright 2008, Freescale Semiconductor, Inc
- * Andy Fleming
- *
- * Based vaguely on the Linux code
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
+ * This code is heavily inspired and in parts from the u-boot project which is
+ * based vaguely on the Linux code
*/
/* #define DEBUG */
@@ -30,12 +14,14 @@
#include <mci.h>
#include <malloc.h>
#include <errno.h>
-#include <asm-generic/div64.h>
+#include <linux/math64.h>
#include <asm/byteorder.h>
#include <block.h>
#include <disks.h>
#include <of.h>
#include <linux/err.h>
+#include <linux/sizes.h>
+#include <dma.h>
#define MAX_BUFFER_NUMBER 0xffffffff
@@ -89,6 +75,28 @@ static int mci_send_cmd(struct mci *mci, struct mci_cmd *cmd, struct mci_data *d
}
/**
+ * mci_send_cmd_retry() - send a command to the mmc device, retrying on error
+ *
+ * @dev: device to receive the command
+ * @cmd: command to send
+ * @data: additional data to send/receive
+ * @retries: how many times to retry; mci_send_cmd is always called at least
+ * once
+ * Return: 0 if ok, -ve on error
+ */
+static int mci_send_cmd_retry(struct mci *mci, struct mci_cmd *cmd,
+ struct mci_data *data, unsigned retries)
+{
+ int ret;
+
+ do
+ ret = mci_send_cmd(mci, cmd, data);
+ while (ret && retries--);
+
+ return ret;
+}
+
+/**
* @param p Command definition to setup
* @param cmd Valid SD/MMC command (refer MMC_CMD_* / SD_CMD_*)
* @param arg Argument for the command (optional)
@@ -127,12 +135,105 @@ static int mci_set_blocklen(struct mci *mci, unsigned len)
{
struct mci_cmd cmd;
+ if (mci->host->timing == MMC_TIMING_MMC_DDR52)
+ return 0;
+
mci_setup_cmd(&cmd, MMC_CMD_SET_BLOCKLEN, len, MMC_RSP_R1);
return mci_send_cmd(mci, &cmd, NULL);
}
static void *sector_buf;
+static int mci_send_status(struct mci *mci, unsigned int *status)
+{
+ struct mci_host *host = mci->host;
+ struct mci_cmd cmd;
+ int ret;
+
+ /*
+ * While CMD13 is defined for SPI mode, the reported status bits have
+ * different layout that SD/MMC. We skip supporting this for now.
+ */
+ if (mmc_host_is_spi(host))
+ return -ENOSYS;
+
+ cmd.cmdidx = MMC_CMD_SEND_STATUS;
+ cmd.resp_type = MMC_RSP_R1;
+ cmd.cmdarg = mci->rca << 16;
+
+ ret = mci_send_cmd_retry(mci, &cmd, NULL, 4);
+ if (!ret)
+ *status = cmd.response[0];
+
+ return ret;
+}
+
+static int mmc_switch_status_error(struct mci_host *host, u32 status)
+{
+ if (mmc_host_is_spi(host)) {
+ if (status & R1_SPI_ILLEGAL_COMMAND)
+ return -EBADMSG;
+ } else {
+ if (R1_STATUS(status))
+ pr_warn("unexpected status %#x after switch\n", status);
+ if (status & R1_SWITCH_ERROR)
+ return -EBADMSG;
+ }
+ return 0;
+}
+
+/* Caller must hold re-tuning */
+int mci_switch_status(struct mci *mci, bool crc_err_fatal)
+{
+ u32 status;
+ int err;
+
+ err = mci_send_status(mci, &status);
+ if (!crc_err_fatal && err == -EILSEQ)
+ return 0;
+ if (err)
+ return err;
+
+ return mmc_switch_status_error(mci->host, status);
+}
+
+static int mci_poll_until_ready(struct mci *mci, int timeout_ms)
+{
+ unsigned int status;
+ int err, retries = 0;
+
+ while (1) {
+ err = mci_send_status(mci, &status);
+ if (err)
+ return err;
+
+ /*
+ * Some cards mishandle the status bits, so make sure to
+ * check both the busy indication and the card state.
+ */
+ if ((status & R1_READY_FOR_DATA) &&
+ R1_CURRENT_STATE(status) != R1_STATE_PRG)
+ break;
+
+ if (status & R1_STATUS_MASK) {
+ dev_err(&mci->dev, "Status Error: 0x%08x\n", status);
+ return -EIO;
+ }
+
+ if (retries++ == timeout_ms) {
+ dev_err(&mci->dev, "Timeout awaiting card ready\n");
+ return -ETIMEDOUT;
+ }
+
+ udelay(1000);
+ }
+
+ dev_dbg(&mci->dev, "Ready polling took %ums\n", retries);
+
+ return 0;
+}
+
+
/**
* Write one or several blocks of data to the card
* @param mci_dev MCI instance
@@ -146,28 +247,31 @@ static int mci_block_write(struct mci *mci, const void *src, int blocknum,
{
struct mci_cmd cmd;
struct mci_data data;
- const void *buf;
unsigned mmccmd;
int ret;
+ /*
+ * Quoting eMMC Spec v5.1 (JEDEC Standard No. 84-B51):
+ * Due to legacy reasons, a Device may still treat CMD24/25 during
+ * prg-state (while busy is active) as a legal or illegal command.
+ * A host should not send CMD24/25 while the Device is in the prg
+ * state and busy is active.
+ */
+ ret = mci_poll_until_ready(mci, 1000 /* ms */);
+ if (ret && ret != -ENOSYS)
+ return ret;
+
if (blocks > 1)
mmccmd = MMC_CMD_WRITE_MULTIPLE_BLOCK;
else
mmccmd = MMC_CMD_WRITE_SINGLE_BLOCK;
- if ((unsigned long)src & 0x3) {
- memcpy(sector_buf, src, 512);
- buf = sector_buf;
- } else {
- buf = src;
- }
-
mci_setup_cmd(&cmd,
mmccmd,
mci->high_capacity != 0 ? blocknum : blocknum * mci->write_bl_len,
MMC_RSP_R1);
- data.src = buf;
+ data.src = src;
data.blocks = blocks;
data.blocksize = mci->write_bl_len;
data.flags = MMC_DATA_WRITE;
@@ -215,7 +319,8 @@ static int mci_read_block(struct mci *mci, void *dst, int blocknum,
ret = mci_send_cmd(mci, &cmd, &data);
if (ret || blocks > 1) {
- mci_setup_cmd(&cmd, MMC_CMD_STOP_TRANSMISSION, 0, MMC_RSP_R1b);
+ mci_setup_cmd(&cmd, MMC_CMD_STOP_TRANSMISSION, 0,
+ IS_SD(mci) ? MMC_RSP_R1b : MMC_RSP_R1);
mci_send_cmd(mci, &cmd, NULL);
}
return ret;
@@ -246,6 +351,15 @@ static int mci_go_idle(struct mci *mci)
return 0;
}
+static int sdio_send_op_cond(struct mci *mci)
+{
+ struct mci_cmd cmd;
+
+ mci_setup_cmd(&cmd, SD_IO_SEND_OP_COND, 0, MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR);
+
+ return mci_send_cmd(mci, &cmd, NULL);
+}
+
/**
* FIXME
* @param mci MCI instance
@@ -358,15 +472,15 @@ static int mmc_send_op_cond(struct mci *mci)
mci->ocr = cmd.response[0];
mci->high_capacity = ((mci->ocr & OCR_HCS) == OCR_HCS);
- mci->rca = 0;
+ mci->rca = 2;
return 0;
}
/**
- * FIXME
- * @param mci MCI instance
- * @param ext_csd Buffer for a 512 byte sized extended CSD
+ * Read-in the card's whole extended CSD configuration area
+ * @param[in] mci MCI instance
+ * @param[out] ext_csd Buffer for an #EXT_CSD_BLOCKSIZE byte sized extended CSD
* @return Transaction status (0 on success)
*
* Note: Only cards newer than Version 1.1 (Physical Layer Spec) support
@@ -389,16 +503,22 @@ int mci_send_ext_csd(struct mci *mci, char *ext_csd)
}
/**
- * FIXME
- * @param mci MCI instance
- * @param set FIXME
- * @param index FIXME
- * @param value FIXME
+ * Write a byte into the card's extended CSD configuration area
+ * @param[in] mci MCI instance
+ * @param[in] index Byte index in the extended CSD configuration area
+ * @param[in] value Byte to write at index into the extended CSD configuration area
* @return Transaction status (0 on success)
+ *
+ * This sends a CMD6 (aka SWITCH) to the card and writes @b value at extended CSD @b index.
+ *
+ * @note It always writes a full byte, the alternatives 'bit set' and
+ * 'bit clear' aren't supported.
*/
int mci_switch(struct mci *mci, unsigned index, unsigned value)
{
+ unsigned int status;
struct mci_cmd cmd;
+ int ret;
mci_setup_cmd(&cmd, MMC_CMD_SWITCH,
(MMC_SWITCH_MODE_WRITE_BYTE << 24) |
@@ -406,19 +526,47 @@ int mci_switch(struct mci *mci, unsigned index, unsigned value)
(value << 8),
MMC_RSP_R1b);
- return mci_send_cmd(mci, &cmd, NULL);
+ ret = mci_send_cmd(mci, &cmd, NULL);
+ if (ret)
+ return ret;
+
+ ret = mci_send_status(mci, &status);
+ if (ret)
+ return ret;
+
+ if (status & R1_SWITCH_ERROR)
+ return -EIO;
+
+ return 0;
+}
+
+u8 *mci_get_ext_csd(struct mci *mci)
+{
+ u8 *ext_csd;
+ int ret;
+
+ ext_csd = dma_alloc(512);
+
+ ret = mci_send_ext_csd(mci, ext_csd);
+ if (ret) {
+ printf("Failure to read EXT_CSD register\n");
+ dma_free(ext_csd);
+ return ERR_PTR(-EIO);
+ }
+
+ return ext_csd;
}
-static int mci_calc_blk_cnt(uint64_t cap, unsigned shift)
+static blkcnt_t mci_calc_blk_cnt(blkcnt_t cap, unsigned shift)
{
- unsigned ret = cap >> shift;
+ blkcnt_t ret = cap >> shift;
if (ret > 0x7fffffff) {
pr_warn("Limiting card size due to 31 bit contraints\n");
return 0x7fffffff;
}
- return (int)ret;
+ return ret;
}
static void mci_part_add(struct mci *mci, uint64_t size,
@@ -437,13 +585,78 @@ static void mci_part_add(struct mci *mci, uint64_t size,
part->part_cfg = part_cfg;
part->idx = idx;
- if (area_type == MMC_BLK_DATA_AREA_MAIN)
- part->blk.cdev.device_node = mci->host->hw_dev->device_node;
+ if (area_type == MMC_BLK_DATA_AREA_MAIN) {
+ cdev_set_of_node(&part->blk.cdev, mci->host->hw_dev->of_node);
+ part->blk.cdev.flags |= DEVFS_IS_MCI_MAIN_PART_DEV;
+ }
mci->nr_parts++;
}
/**
+ * Read a value spread to three consecutive bytes in the ECSD information
+ * @param[in] ecsd_info Information from the eMMC
+ * @param[in] idx The index where to start to read
+ * @return The GPP size in units of 'write protect group' size
+ *
+ * The value in the ECSD information block is meant in little endian
+ */
+static __maybe_unused unsigned mmc_extract_gpp_units(const char *ecsd_info, unsigned idx)
+{
+ unsigned val;
+
+ val = ecsd_info[idx];
+ val |= ecsd_info[idx + 1] << 8;
+ val |= ecsd_info[idx + 2] << 16;
+
+ return val;
+}
+
+/**
+ * Create and enable access to 'general purpose hardware partitions' on demand
+ * @param mci[in,out] MCI instance
+ *
+ * General Purpose hardware Partitions (aka GPPs) aren't enabled by default. Its
+ * up to the application to (one-time) setup the eMMC to provide GPPs. Since
+ * they aren't wildly used, enable access to them on demand only.
+ */
+static __maybe_unused void mmc_extract_gpp_partitions(struct mci *mci)
+{
+ uint64_t wpgs, part_size;
+ size_t idx;
+ char *name, *partname;
+ static const unsigned gpp_offsets[MMC_NUM_GP_PARTITION] = {
+ EXT_CSD_GP_SIZE_MULT0, EXT_CSD_GP_SIZE_MULT1,
+ EXT_CSD_GP_SIZE_MULT2, EXT_CSD_GP_SIZE_MULT3, };
+
+ if (!(mci->ext_csd[EXT_CSD_PARTITIONING_SUPPORT] & 0x01))
+ return; /* no partitioning support */
+ /*
+ * The size of GPPs is defined in units of 'write protect group' size.
+ * The 'write protect group' size is defined to:
+ * CSD_HC_ERASE_GRP_SIZE * CSD_HC_WP_GRP_SIZE * 512 kiB
+ */
+ wpgs = mci->ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE];
+ wpgs *= mci->ext_csd[EXT_CSD_HC_WP_GRP_SIZE];
+ wpgs *= SZ_512K;
+
+ /* up to four GPPs can be enabled. */
+ for (idx = 0; idx < ARRAY_SIZE(gpp_offsets); idx++) {
+ part_size = mmc_extract_gpp_units(mci->ext_csd, gpp_offsets[idx]);
+ if (part_size == 0)
+ continue;
+ /* Convert to bytes */
+ part_size *= wpgs;
+
+ partname = xasprintf("gpp%zu", idx);
+ name = xasprintf("%s.%s", mci->cdevname, partname);
+ /* TODO read-only flag */
+ mci_part_add(mci, part_size, EXT_CSD_PART_CONFIG_ACC_GPP0 + idx,
+ name, partname, idx, false, MMC_BLK_DATA_AREA_GP);
+ }
+}
+
+/**
* Change transfer frequency for an MMC card
* @param mci MCI instance
* @return Transaction status (0 on success)
@@ -453,7 +666,7 @@ static int mmc_change_freq(struct mci *mci)
char cardtype;
int err;
- mci->ext_csd = xmalloc(512);
+ mci->ext_csd = dma_alloc(512);
mci->card_caps = 0;
/* Only version 4 supports high-speed */
@@ -470,7 +683,7 @@ static int mmc_change_freq(struct mci *mci)
cardtype = mci->ext_csd[EXT_CSD_DEVICE_TYPE] & EXT_CSD_CARD_TYPE_MASK;
- err = mci_switch(mci, EXT_CSD_HS_TIMING, 1);
+ err = mci_switch(mci, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS);
if (err) {
dev_dbg(&mci->dev, "MMC frequency changing failed: %d\n", err);
@@ -491,11 +704,15 @@ static int mmc_change_freq(struct mci *mci)
return 0;
}
- /* High Speed is set, there are two types: 52MHz and 26MHz */
- if (cardtype & EXT_CSD_CARD_TYPE_52)
- mci->card_caps |= MMC_CAP_MMC_HIGHSPEED_52MHZ | MMC_CAP_MMC_HIGHSPEED;
- else
- mci->card_caps |= MMC_CAP_MMC_HIGHSPEED;
+ mci->card_caps |= MMC_CAP_MMC_HIGHSPEED;
+
+ /* High Speed is set, there are three types: 26MHz, 52MHz, 52MHz DDR */
+ if (cardtype & EXT_CSD_CARD_TYPE_52) {
+ mci->card_caps |= MMC_CAP_MMC_HIGHSPEED_52MHZ;
+
+ if (cardtype & EXT_CSD_CARD_TYPE_DDR_1_8V)
+ mci->card_caps |= MMC_CAP_MMC_3_3V_DDR | MMC_CAP_MMC_1_8V_DDR;
+ }
if (IS_ENABLED(CONFIG_MCI_MMC_BOOT_PARTITIONS) &&
mci->ext_csd[EXT_CSD_REV] >= 3 && mci->ext_csd[EXT_CSD_BOOT_SIZE_MULT]) {
@@ -516,8 +733,12 @@ static int mmc_change_freq(struct mci *mci)
mci->ext_csd_part_config = mci->ext_csd[EXT_CSD_PARTITION_CONFIG];
mci->bootpart = (mci->ext_csd_part_config >> 3) & 0x7;
+ mci->boot_ack_enable = (mci->ext_csd_part_config >> 6) & 0x1;
}
+ if (IS_ENABLED(CONFIG_MCI_MMC_GPP_PARTITIONS))
+ mmc_extract_gpp_partitions(mci);
+
return 0;
}
@@ -675,6 +896,8 @@ static void mci_set_ios(struct mci *mci)
};
host->set_ios(host, &ios);
+
+ host->actual_clock = host->clock;
}
/**
@@ -702,7 +925,7 @@ static void mci_set_clock(struct mci *mci, unsigned clock)
* @param mci MCI instance
* @param width New interface bit width (1, 4 or 8)
*/
-static void mci_set_bus_width(struct mci *mci, unsigned width)
+static void mci_set_bus_width(struct mci *mci, enum mci_bus_width width)
{
struct mci_host *host = mci->host;
@@ -893,7 +1116,7 @@ static void mci_extract_card_dsr_imp_from_csd(struct mci *mci)
mci->dsr_imp = UNSTUFF_BITS(mci->csd, 76, 1);
}
-static int mmc_compare_ext_csds(struct mci *mci, unsigned bus_width)
+static int mmc_compare_ext_csds(struct mci *mci, enum mci_bus_width bus_width)
{
u8 *bw_ext_csd;
int err;
@@ -901,7 +1124,7 @@ static int mmc_compare_ext_csds(struct mci *mci, unsigned bus_width)
if (bus_width == MMC_BUS_WIDTH_1)
return 0;
- bw_ext_csd = xmalloc(512);
+ bw_ext_csd = dma_alloc(512);
err = mci_send_ext_csd(mci, bw_ext_csd);
if (err) {
dev_info(&mci->dev, "mci_send_ext_csd failed with %d\n", err);
@@ -950,7 +1173,7 @@ static int mmc_compare_ext_csds(struct mci *mci, unsigned bus_width)
0 : -EINVAL;
out:
- free(bw_ext_csd);
+ dma_free(bw_ext_csd);
return err;
}
@@ -1005,34 +1228,71 @@ static int mci_startup_sd(struct mci *mci)
return 0;
}
-static int mci_startup_mmc(struct mci *mci)
+static u32 mci_bus_width_ext_csd_bits(enum mci_bus_width bus_width)
{
- struct mci_host *host = mci->host;
+ switch (bus_width) {
+ case MMC_BUS_WIDTH_8:
+ return EXT_CSD_BUS_WIDTH_8;
+ case MMC_BUS_WIDTH_4:
+ return EXT_CSD_BUS_WIDTH_4;
+ case MMC_BUS_WIDTH_1:
+ default:
+ return EXT_CSD_BUS_WIDTH_1;
+ }
+}
+
+static int mci_mmc_try_bus_width(struct mci *mci, enum mci_bus_width bus_width,
+ enum mci_timing timing)
+{
+ u32 ext_csd_bits;
int err;
- int idx = 0;
- static unsigned ext_csd_bits[] = {
- EXT_CSD_BUS_WIDTH_4,
- EXT_CSD_BUS_WIDTH_8,
- };
- static unsigned bus_widths[] = {
- MMC_BUS_WIDTH_4,
- MMC_BUS_WIDTH_8,
- };
- /* if possible, speed up the transfer */
- if (mci_caps(mci) & MMC_CAP_MMC_HIGHSPEED) {
- if (mci->card_caps & MMC_CAP_MMC_HIGHSPEED_52MHZ)
- mci->tran_speed = 52000000;
- else
- mci->tran_speed = 26000000;
+ ext_csd_bits = mci_bus_width_ext_csd_bits(bus_width);
- host->timing = MMC_TIMING_MMC_HS;
+ if (mci_timing_is_ddr(timing))
+ ext_csd_bits |= EXT_CSD_DDR_FLAG;
+
+ err = mci_switch(mci, EXT_CSD_BUS_WIDTH, ext_csd_bits);
+ if (err < 0)
+ goto out;
+
+ mci->host->timing = timing;
+ mci_set_bus_width(mci, bus_width);
+
+ switch (bus_width) {
+ case MMC_BUS_WIDTH_8:
+ mci->card_caps |= MMC_CAP_8_BIT_DATA;
+ break;
+ case MMC_BUS_WIDTH_4:
+ mci->card_caps |= MMC_CAP_4_BIT_DATA;
+ break;
+ default:
+ break;
}
- mci_set_clock(mci, mci->tran_speed);
+ err = mmc_compare_ext_csds(mci, bus_width);
+ if (err < 0)
+ goto out;
+
+out:
+ dev_dbg(&mci->dev, "Tried buswidth %u%s: %s\n", 1 << bus_width,
+ mci_timing_is_ddr(timing) ? " (DDR)" : "", err ? "failed" : "OK");
+
+ return err ?: bus_width;
+}
+
+static int mci_mmc_select_bus_width(struct mci *mci)
+{
+ struct mci_host *host = mci->host;
+ int ret;
+ int idx = 0;
+ static enum mci_bus_width bus_widths[] = {
+ MMC_BUS_WIDTH_4,
+ MMC_BUS_WIDTH_8,
+ };
if (!(host->host_caps & (MMC_CAP_4_BIT_DATA | MMC_CAP_8_BIT_DATA)))
- return 0;
+ return MMC_BUS_WIDTH_1;
/*
* Unlike SD, MMC cards dont have a configuration register to notify
@@ -1044,7 +1304,6 @@ static int mci_startup_mmc(struct mci *mci)
idx = 1;
for (; idx >= 0; idx--) {
-
/*
* Host is capable of 8bit transfer, then switch
* the device to work in 8bit transfer mode. If the
@@ -1052,23 +1311,264 @@ static int mci_startup_mmc(struct mci *mci)
* 4bit transfer mode. On success set the corresponding
* bus width on the host.
*/
- err = mci_switch(mci, EXT_CSD_BUS_WIDTH, ext_csd_bits[idx]);
- if (err) {
- if (idx == 0)
- dev_warn(&mci->dev, "Changing MMC bus width failed: %d\n", err);
- continue;
- }
+ ret = mci_mmc_try_bus_width(mci, bus_widths[idx], host->timing);
+ if (ret > 0)
+ break;
+ }
- mci_set_bus_width(mci, bus_widths[idx]);
+ return ret;
+}
- err = mmc_compare_ext_csds(mci, bus_widths[idx]);
- if (!err)
- break;
+static int mci_mmc_select_hs_ddr(struct mci *mci)
+{
+ struct mci_host *host = mci->host;
+ int ret;
+
+ /*
+ * barebox MCI core does not change voltage, so we don't know here
+ * if we should check for the 1.8v or 3.3v mode. Until we support
+ * higher speed modes that require voltage switching like HS200/HS400,
+ * let's just check for either bit.
+ */
+ if (!(mci_caps(mci) & (MMC_CAP_MMC_1_8V_DDR | MMC_CAP_MMC_3_3V_DDR)))
+ return 0;
+
+ ret = mci_mmc_try_bus_width(mci, host->bus_width, MMC_TIMING_MMC_DDR52);
+ if (ret < 0)
+ return mci_mmc_try_bus_width(mci, host->bus_width, MMC_TIMING_MMC_HS);
+
+ /* Block length is fixed to 512 bytes while in DDR mode */
+ mci->read_bl_len = SECTOR_SIZE;
+ mci->write_bl_len = SECTOR_SIZE;
+
+ return 0;
+}
+
+int mci_execute_tuning(struct mci *mci)
+{
+ struct mci_host *host = mci->host;
+ u32 opcode;
+
+ if (!host->execute_tuning)
+ return 0;
+
+ /* Tuning is only supported for MMC / HS200 */
+ if (mmc_card_hs200(mci))
+ opcode = MMC_SEND_TUNING_BLOCK_HS200;
+ else
+ return 0;
+
+ return host->execute_tuning(host, opcode);
+}
+
+int mci_send_abort_tuning(struct mci *mci, u32 opcode)
+{
+ struct mci_cmd cmd = {};
+
+ /*
+ * eMMC specification specifies that CMD12 can be used to stop a tuning
+ * command, but SD specification does not, so do nothing unless it is
+ * eMMC.
+ */
+ if (opcode != MMC_SEND_TUNING_BLOCK_HS200)
+ return 0;
+
+ cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION;
+ cmd.resp_type = MMC_RSP_SPI_R1 | MMC_RSP_R1 | MMC_CMD_AC;
+
+ return mci_send_cmd(mci, &cmd, NULL);
+}
+EXPORT_SYMBOL_GPL(mci_send_abort_tuning);
+
+static void mmc_select_max_dtr(struct mci *mci)
+{
+ u8 card_type = mci->ext_csd[EXT_CSD_DEVICE_TYPE];
+ u32 caps2 = mci->host->caps2;
+ u32 caps = mci->card_caps;
+ unsigned int hs_max_dtr = 0;
+ unsigned int hs200_max_dtr = 0;
+
+ if ((caps & MMC_CAP_MMC_HIGHSPEED) &&
+ (card_type & EXT_CSD_CARD_TYPE_26)) {
+ hs_max_dtr = MMC_HIGH_26_MAX_DTR;
+ }
+
+ if ((caps & MMC_CAP_MMC_HIGHSPEED) &&
+ (card_type & EXT_CSD_CARD_TYPE_52)) {
+ hs_max_dtr = MMC_HIGH_52_MAX_DTR;
+ }
+
+ if ((caps2 & MMC_CAP2_HS200_1_8V_SDR) &&
+ (card_type & EXT_CSD_CARD_TYPE_HS200_1_8V)) {
+ hs200_max_dtr = MMC_HS200_MAX_DTR;
+ }
+
+ if ((caps2 & MMC_CAP2_HS200_1_2V_SDR) &&
+ (card_type & EXT_CSD_CARD_TYPE_HS200_1_2V)) {
+ hs200_max_dtr = MMC_HS200_MAX_DTR;
}
+ mci->host->hs200_max_dtr = hs200_max_dtr;
+ mci->host->hs_max_dtr = hs_max_dtr;
+}
+/*
+ * For device supporting HS200 mode, the following sequence
+ * should be done before executing the tuning process.
+ * 1. set the desired bus width(4-bit or 8-bit, 1-bit is not supported)
+ * 2. switch to HS200 mode
+ * 3. set the clock to > 52Mhz and <=200MHz
+ */
+static int mmc_select_hs200(struct mci *mci)
+{
+ unsigned int old_timing, old_clock;
+ int err = -EINVAL;
+ u8 val;
+
+ /*
+ * Set the bus width(4 or 8) with host's support and
+ * switch to HS200 mode if bus width is set successfully.
+ */
+ /* find out maximum bus width and then try DDR if supported */
+ err = mci_mmc_select_bus_width(mci);
+ if (err > 0) {
+ /* TODO actually set drive strength instead of 0. Currently unsupported. */
+ val = EXT_CSD_TIMING_HS200 | 0 << EXT_CSD_DRV_STR_SHIFT;
+ err = mci_switch(mci, EXT_CSD_HS_TIMING, val);
+ if (err == -EIO)
+ return -EBADMSG;
+ if (err)
+ goto err;
+
+ /*
+ * Bump to HS timing and frequency. Some cards don't handle
+ * SEND_STATUS reliably at the initial frequency.
+ * NB: We can't move to full (HS200) speeds until after we've
+ * successfully switched over.
+ */
+ old_timing = mci->host->timing;
+ old_clock = mci->host->clock;
+
+ mci->host->timing = MMC_TIMING_MMC_HS200;
+ mci_set_ios(mci);
+ mci_set_clock(mci, mci->host->hs_max_dtr);
+
+ err = mci_switch_status(mci, true);
+
+ /*
+ * mmc_select_timing() assumes timing has not changed if
+ * it is a switch error.
+ */
+ if (err == -EBADMSG) {
+ mci->host->clock = old_clock;
+ mci->host->timing = old_timing;
+ mci_set_ios(mci);
+ }
+ }
+err:
+ if (err) {
+ dev_err(&mci->dev, "%s failed, error %d\n", __func__, err);
+ }
return err;
}
+/*
+ * Set the bus speed for the selected speed mode.
+ */
+static void mmc_set_bus_speed(struct mci *mci)
+{
+ unsigned int max_dtr = (unsigned int)-1;
+
+ if (mmc_card_hs200(mci) &&
+ max_dtr > mci->host->hs200_max_dtr)
+ max_dtr = mci->host->hs200_max_dtr;
+ else if (mmc_card_hs(mci) && max_dtr > mci->host->hs_max_dtr)
+ max_dtr = mci->host->hs_max_dtr;
+ else if (max_dtr > mci->tran_speed)
+ max_dtr = mci->tran_speed;
+
+ mci_set_clock(mci, max_dtr);
+}
+
+/*
+ * Activate HS200 or HS400ES mode if supported.
+ */
+int mmc_select_timing(struct mci *mci)
+{
+ unsigned int mmc_avail_type;
+ int err = 0;
+
+ mmc_select_max_dtr(mci);
+
+ mmc_avail_type = mci->ext_csd[EXT_CSD_DEVICE_TYPE] & EXT_CSD_CARD_TYPE_MASK;
+ if (mmc_avail_type & EXT_CSD_CARD_TYPE_HS200) {
+ err = mmc_select_hs200(mci);
+ if (err == -EBADMSG)
+ mmc_avail_type &= ~EXT_CSD_CARD_TYPE_HS200;
+ else
+ goto out;
+ }
+
+out:
+ if (err && err != -EBADMSG)
+ return err;
+
+ /*
+ * Set the bus speed to the selected bus timing.
+ * If timing is not selected, backward compatible is the default.
+ */
+ mmc_set_bus_speed(mci);
+
+ return 0;
+}
+
+int mmc_hs200_tuning(struct mci *mci)
+{
+ return mci_execute_tuning(mci);
+}
+
+static int mci_startup_mmc(struct mci *mci)
+{
+ struct mci_host *host = mci->host;
+ int ret = 0;
+
+ /* if possible, speed up the transfer */
+ if (mci_caps(mci) & MMC_CAP_MMC_HIGHSPEED) {
+ if (mci->card_caps & MMC_CAP_MMC_HIGHSPEED_52MHZ)
+ mci->tran_speed = 52000000;
+ else
+ mci->tran_speed = 26000000;
+
+ host->timing = MMC_TIMING_MMC_HS;
+ }
+
+ if (IS_ENABLED(CONFIG_MCI_TUNING)) {
+ /*
+ * Select timing interface
+ */
+ ret = mmc_select_timing(mci);
+ if (ret)
+ return ret;
+
+ if (mmc_card_hs200(mci))
+ ret = mmc_hs200_tuning(mci);
+ }
+
+ if (ret || !IS_ENABLED(CONFIG_MCI_TUNING)) {
+ mci_set_clock(mci, mci->tran_speed);
+
+ /* find out maximum bus width and then try DDR if supported */
+ ret = mci_mmc_select_bus_width(mci);
+ if (ret > MMC_BUS_WIDTH_1 && mci->tran_speed == 52000000)
+ ret = mci_mmc_select_hs_ddr(mci);
+
+ if (ret < 0) {
+ dev_warn(&mci->dev, "Changing MMC bus width failed: %d\n", ret);
+ }
+ }
+
+ return ret;
+}
+
/**
* Scan the given host interfaces and detect connected MMC/SD cards
* @param mci MCI instance
@@ -1242,13 +1742,16 @@ static int sd_send_if_cond(struct mci *mci)
return 0;
}
+/**
+ * Switch between hardware MMC partitions on demand
+ */
static int mci_blk_part_switch(struct mci_part *part)
{
struct mci *mci = part->mci;
int ret;
- if (!IS_ENABLED(CONFIG_MCI_MMC_BOOT_PARTITIONS))
- return 0;
+ if (!IS_ENABLED(CONFIG_MCI_MMC_BOOT_PARTITIONS) && !IS_ENABLED(CONFIG_MCI_MMC_GPP_PARTITIONS))
+ return 0; /* no need */
if (mci->part_curr == part)
return 0;
@@ -1284,26 +1787,27 @@ static int mci_blk_part_switch(struct mci_part *part)
* This routine expects the buffer has the correct size to read all data!
*/
static int __maybe_unused mci_sd_write(struct block_device *blk,
- const void *buffer, int block, int num_blocks)
+ const void *buffer, sector_t block, blkcnt_t num_blocks)
{
struct mci_part *part = container_of(blk, struct mci_part, blk);
struct mci *mci = part->mci;
struct mci_host *host = mci->host;
int rc;
- unsigned max_req_block = num_blocks;
- int write_block;
+ blkcnt_t max_req_block = num_blocks;
+ blkcnt_t write_block;
if (mci->host->max_req_size)
max_req_block = mci->host->max_req_size / mci->write_bl_len;
mci_blk_part_switch(part);
- if (host->card_write_protected && host->card_write_protected(host)) {
+ if (!host->disable_wp &&
+ host->card_write_protected && host->card_write_protected(host)) {
dev_err(&mci->dev, "card write protected\n");
return -EPERM;
}
- dev_dbg(&mci->dev, "%s: Write %d block(s), starting at %d\n",
+ dev_dbg(&mci->dev, "%s: Write %llu block(s), starting at %llu\n",
__func__, num_blocks, block);
if (mci->write_bl_len != SECTOR_SIZE) {
@@ -1314,15 +1818,15 @@ static int __maybe_unused mci_sd_write(struct block_device *blk,
/* size of the block number field in the MMC/SD command is 32 bit only */
if (block > MAX_BUFFER_NUMBER) {
- dev_dbg(&mci->dev, "Cannot handle block number %d. Too large!\n", block);
+ dev_dbg(&mci->dev, "Cannot handle block number %llu. Too large!\n", block);
return -EINVAL;
}
while (num_blocks) {
- write_block = min_t(int, num_blocks, max_req_block);
+ write_block = min(num_blocks, max_req_block);
rc = mci_block_write(mci, buffer, block, write_block);
if (rc != 0) {
- dev_dbg(&mci->dev, "Writing block %d failed with %d\n", block, rc);
+ dev_dbg(&mci->dev, "Writing block %llu failed with %d\n", block, rc);
return rc;
}
num_blocks -= write_block;
@@ -1343,13 +1847,13 @@ static int __maybe_unused mci_sd_write(struct block_device *blk,
*
* This routine expects the buffer has the correct size to store all data!
*/
-static int mci_sd_read(struct block_device *blk, void *buffer, int block,
- int num_blocks)
+static int mci_sd_read(struct block_device *blk, void *buffer, sector_t block,
+ blkcnt_t num_blocks)
{
struct mci_part *part = container_of(blk, struct mci_part, blk);
struct mci *mci = part->mci;
- unsigned max_req_block = num_blocks;
- int read_block;
+ blkcnt_t max_req_block = num_blocks;
+ blkcnt_t read_block;
int rc;
if (mci->host->max_req_size)
@@ -1357,25 +1861,25 @@ static int mci_sd_read(struct block_device *blk, void *buffer, int block,
mci_blk_part_switch(part);
- dev_dbg(&mci->dev, "%s: Read %d block(s), starting at %d\n",
+ dev_dbg(&mci->dev, "%s: Read %llu block(s), starting at %llu\n",
__func__, num_blocks, block);
- if (mci->read_bl_len != 512) {
- dev_dbg(&mci->dev, "MMC/SD block size is not 512 bytes (its %u bytes instead)\n",
- mci->read_bl_len);
+ if (mci->read_bl_len != SECTOR_SIZE) {
+ dev_dbg(&mci->dev, "MMC/SD block size is not %d bytes (its %u bytes instead)\n",
+ SECTOR_SIZE, mci->read_bl_len);
return -EINVAL;
}
if (block > MAX_BUFFER_NUMBER) {
- dev_err(&mci->dev, "Cannot handle block number %d. Too large!\n", block);
+ dev_err(&mci->dev, "Cannot handle block number %llu. Too large!\n", block);
return -EINVAL;
}
while (num_blocks) {
- read_block = min_t(int, num_blocks, max_req_block);
+ read_block = min(num_blocks, max_req_block);
rc = mci_read_block(mci, buffer, block, read_block);
if (rc != 0) {
- dev_dbg(&mci->dev, "Reading block %d failed with %d\n", block, rc);
+ dev_dbg(&mci->dev, "Reading block %llu failed with %d\n", block, rc);
return rc;
}
num_blocks -= read_block;
@@ -1488,6 +1992,10 @@ static const char *mci_timing_tostr(unsigned timing)
return "MMC HS";
case MMC_TIMING_SD_HS:
return "SD HS";
+ case MMC_TIMING_MMC_DDR52:
+ return "MMC DDR52";
+ case MMC_TIMING_MMC_HS200:
+ return "HS200";
default:
return "unknown"; /* shouldn't happen */
}
@@ -1495,19 +2003,22 @@ static const char *mci_timing_tostr(unsigned timing)
static void mci_print_caps(unsigned caps)
{
- printf(" capabilities: %s%s%s%s%s\n",
+ printf(" capabilities: %s%s%s%s%s%s%s%s\n",
caps & MMC_CAP_4_BIT_DATA ? "4bit " : "",
caps & MMC_CAP_8_BIT_DATA ? "8bit " : "",
caps & MMC_CAP_SD_HIGHSPEED ? "sd-hs " : "",
caps & MMC_CAP_MMC_HIGHSPEED ? "mmc-hs " : "",
- caps & MMC_CAP_MMC_HIGHSPEED_52MHZ ? "mmc-52MHz " : "");
+ caps & MMC_CAP_MMC_HIGHSPEED_52MHZ ? "mmc-52MHz " : "",
+ caps & MMC_CAP_MMC_3_3V_DDR ? "ddr-3.3v " : "",
+ caps & MMC_CAP_MMC_1_8V_DDR ? "ddr-1.8v " : "",
+ caps & MMC_CAP_MMC_1_2V_DDR ? "ddr-1.2v " : "");
}
/**
* Output some valuable information when the user runs 'devinfo' on an MCI device
* @param mci MCI device instance
*/
-static void mci_info(struct device_d *dev)
+static void mci_info(struct device *dev)
{
struct mci *mci = container_of(dev, struct mci, dev);
struct mci_host *host = mci->host;
@@ -1533,7 +2044,9 @@ static void mci_info(struct device_d *dev)
mci_print_caps(host->host_caps);
printf("Card information:\n");
- printf(" Attached is a %s card\n", IS_SD(mci) ? "SD" : "MMC");
+ printf(" Card type: %s\n", mci->sdio ? "SDIO" : IS_SD(mci) ? "SD" : "MMC");
+ if (mci->sdio)
+ return;
printf(" Version: %s\n", mci_version_string(mci));
printf(" Capacity: %u MiB\n", (unsigned)(mci->capacity >> 20));
@@ -1592,6 +2105,17 @@ static int mci_set_boot(struct param_d *param, void *priv)
EXT_CSD_PARTITION_CONFIG, mci->ext_csd_part_config);
}
+static int mci_set_boot_ack(struct param_d *param, void *priv)
+{
+ struct mci *mci = priv;
+
+ mci->ext_csd_part_config &= ~(0x1 << 6);
+ mci->ext_csd_part_config |= mci->boot_ack_enable << 6;
+
+ return mci_switch(mci,
+ EXT_CSD_PARTITION_CONFIG, mci->ext_csd_part_config);
+}
+
static const char *mci_boot_names[] = {
"disabled",
"boot0",
@@ -1617,6 +2141,7 @@ static int mci_register_partition(struct mci_part *part)
*/
part->blk.dev = &mci->dev;
part->blk.ops = &mci_ops;
+ part->blk.type = IS_SD(mci) ? BLK_TYPE_SD : BLK_TYPE_MMC;
rc = blockdevice_register(&part->blk);
if (rc != 0) {
@@ -1625,7 +2150,7 @@ static int mci_register_partition(struct mci_part *part)
}
dev_info(&mci->dev, "registered %s\n", part->blk.cdev.name);
- np = host->hw_dev->device_node;
+ np = host->hw_dev->of_node;
/* create partitions on demand */
switch (part->area_type) {
@@ -1635,21 +2160,17 @@ static int mci_register_partition(struct mci_part *part)
else
partnodename = "boot1-partitions";
- np = of_get_child_by_name(host->hw_dev->device_node,
+ np = of_get_child_by_name(host->hw_dev->of_node,
partnodename);
break;
case MMC_BLK_DATA_AREA_MAIN:
break;
+ case MMC_BLK_DATA_AREA_GP:
+ break;
default:
return 0;
}
- rc = parse_partition_table(&part->blk);
- if (rc != 0) {
- dev_warn(&mci->dev, "No partition table found\n");
- rc = 0; /* it's not a failure */
- }
-
if (np) {
of_parse_partitions(&part->blk.cdev, np);
@@ -1663,6 +2184,47 @@ static int mci_register_partition(struct mci_part *part)
return 0;
}
+static int of_broken_cd_fixup(struct device_node *root, void *ctx)
+{
+ struct mci_host *host = ctx;
+ struct device *hw_dev = host->hw_dev;
+ struct device_node *np;
+ char *name;
+
+ if (!host->broken_cd)
+ return 0;
+
+ name = of_get_reproducible_name(hw_dev->of_node);
+ np = of_find_node_by_reproducible_name(root, name);
+ free(name);
+ if (!np) {
+ dev_warn(hw_dev, "Cannot find nodepath %pOF, cannot fixup\n",
+ hw_dev->of_node);
+ return -EINVAL;
+ }
+
+ of_property_write_bool(np, "cd-gpios", false);
+ of_property_write_bool(np, "broken-cd", true);
+
+ return 0;
+}
+
+static int mci_get_partition_setting_completed(struct mci *mci)
+{
+ u8 *ext_csd;
+ int ret;
+
+ ext_csd = mci_get_ext_csd(mci);
+ if (IS_ERR(ext_csd))
+ return PTR_ERR(ext_csd);
+
+ ret = ext_csd[EXT_CSD_PARTITION_SETTING_COMPLETED];
+
+ dma_free(ext_csd);
+
+ return ret;
+}
+
/**
* Probe an MCI card at the given host interface
* @param mci MCI device instance
@@ -1672,11 +2234,15 @@ static int mci_card_probe(struct mci *mci)
{
struct mci_host *host = mci->host;
int i, rc, disknum, ret;
+ bool has_bootpart = false;
- if (host->card_present && !host->card_present(host) &&
- !host->non_removable) {
- dev_err(&mci->dev, "no card inserted\n");
- return -ENODEV;
+ if (host->card_present && !host->card_present(host) && !host->non_removable) {
+ if (!host->broken_cd) {
+ dev_err(&mci->dev, "no card inserted\n");
+ return -ENODEV;
+ }
+
+ dev_info(&mci->dev, "no card inserted (ignoring)\n");
}
ret = regulator_enable(host->supply);
@@ -1704,12 +2270,22 @@ static int mci_card_probe(struct mci *mci)
goto on_error;
}
+ if (!(host->caps2 & MMC_CAP2_NO_SDIO)) {
+ rc = sdio_send_op_cond(mci);
+ if (!rc) {
+ mci->ready_for_use = true;
+ mci->sdio = true;
+ dev_info(&mci->dev, "SDIO card detected, ignoring\n");
+ return 0;
+ }
+ }
+
/* Check if this card can handle the "SD Card Physical Layer Specification 2.0" */
- if (!host->no_sd) {
+ if (!(host->caps2 & MMC_CAP2_NO_SD)) {
rc = sd_send_if_cond(mci);
rc = sd_send_op_cond(mci);
}
- if (host->no_sd || rc == -ETIMEDOUT) {
+ if ((host->caps2 & MMC_CAP2_NO_SD) || rc == -ETIMEDOUT) {
/* If SD card initialization was skipped or if it timed out,
* we check for an MMC card */
dev_dbg(&mci->dev, "Card seems to be a MultiMediaCard\n");
@@ -1726,6 +2302,11 @@ static int mci_card_probe(struct mci *mci)
mci->cdevname = basprintf("disk%d", disknum);
}
+ if (!sector_buf)
+ sector_buf = dma_alloc(SECTOR_SIZE);
+
+ /* FIXME we don't check sector_buf against the device dma mask here */
+
rc = mci_startup(mci);
if (rc) {
dev_warn(&mci->dev, "Card's startup fails with %d\n", rc);
@@ -1741,12 +2322,27 @@ static int mci_card_probe(struct mci *mci)
rc = mci_register_partition(part);
if (IS_ENABLED(CONFIG_MCI_MMC_BOOT_PARTITIONS) &&
- part->area_type == MMC_BLK_DATA_AREA_BOOT &&
- !mci->param_boot) {
- mci->param_boot = dev_add_param_enum(&mci->dev, "boot",
- mci_set_boot, NULL, &mci->bootpart,
- mci_boot_names, ARRAY_SIZE(mci_boot_names), mci);
- }
+ part->area_type == MMC_BLK_DATA_AREA_BOOT)
+ has_bootpart = true;
+ }
+
+ if (has_bootpart) {
+ mci->param_boot =
+ dev_add_param_enum(&mci->dev, "boot", mci_set_boot,
+ NULL, &mci->bootpart, mci_boot_names,
+ ARRAY_SIZE(mci_boot_names), mci);
+
+ mci->param_boot_ack =
+ dev_add_param_bool(&mci->dev, "boot_ack",
+ mci_set_boot_ack, NULL,
+ &mci->boot_ack_enable, mci);
+
+ ret = mci_get_partition_setting_completed(mci);
+ if (ret < 0)
+ dev_dbg(&mci->dev,
+ "Failed to determine EXT_CSD_PARTITION_SETTING_COMPLETED\n");
+ else
+ dev_add_param_bool_fixed(&mci->dev, "partitioning_completed", ret);
}
dev_dbg(&mci->dev, "SD Card successfully added\n");
@@ -1787,15 +2383,6 @@ static int mci_set_probe(struct param_d *param, void *priv)
return 0;
}
-static int mci_init(void)
-{
- sector_buf = xmemalign(32, 512);
-
- return 0;
-}
-
-device_initcall(mci_init);
-
int mci_detect_card(struct mci_host *host)
{
int rc;
@@ -1807,13 +2394,25 @@ int mci_detect_card(struct mci_host *host)
return mci_card_probe(host->mci);
}
-static int mci_detect(struct device_d *dev)
+static int mci_detect(struct device *dev)
{
struct mci *mci = container_of(dev, struct mci, dev);
return mci_detect_card(mci->host);
}
+static int mci_hw_detect(struct device *dev)
+{
+ struct mci *mci;
+
+ list_for_each_entry(mci, &mci_list, list) {
+ if (dev == mci->host->hw_dev)
+ return mci_detect_card(mci->host);
+ }
+
+ return -ENODEV;
+}
+
/**
* Create a new mci device (for convenience)
* @param host mci_host for this MCI device
@@ -1822,7 +2421,8 @@ static int mci_detect(struct device_d *dev)
int mci_register(struct mci_host *host)
{
struct mci *mci;
- struct param_d *param_probe;
+ struct device *hw_dev;
+ struct param_d *param;
int ret;
mci = xzalloc(sizeof(*mci));
@@ -1836,15 +2436,28 @@ int mci_register(struct mci_host *host)
mci->dev.id = DEVICE_ID_DYNAMIC;
}
+ hw_dev = host->hw_dev;
mci->dev.platform_data = host;
- mci->dev.parent = host->hw_dev;
+ mci->dev.parent = hw_dev;
mci->host = host;
host->mci = mci;
mci->dev.detect = mci_detect;
+ if (!hw_dev->detect)
+ hw_dev->detect = mci_hw_detect;
- host->supply = regulator_get(host->hw_dev, "vmmc");
+ host->supply = regulator_get(hw_dev, "vmmc");
if (IS_ERR(host->supply)) {
- dev_err(&mci->dev, "Failed to get 'vmmc' regulator.\n");
+ /*
+ * If you know your regulator to be always online on boot, but
+ * can't easily add a barebox driver for it, you may use
+ * barebox,allow-dummy-supply in your board's regulator device
+ * tree node to side step this warning.
+ *
+ * If you run into this warning, because your regulator driver
+ * hasn't probed the device yet, consider enabling deep probe
+ * for your board, to probe dependencies on demand.
+ */
+ dev_warn(hw_dev, "Failed to get 'vmmc' regulator (ignored).\n");
host->supply = NULL;
}
@@ -1852,17 +2465,26 @@ int mci_register(struct mci_host *host)
if (ret)
goto err_free;
- dev_info(mci->host->hw_dev, "registered as %s\n", dev_name(&mci->dev));
+ dev_info(hw_dev, "registered as %s\n", dev_name(&mci->dev));
- param_probe = dev_add_param_bool(&mci->dev, "probe",
- mci_set_probe, NULL, &mci->probe, mci);
+ param = dev_add_param_bool(&mci->dev, "probe", mci_set_probe, NULL,
+ &mci->probe, mci);
- if (IS_ERR(param_probe) && PTR_ERR(param_probe) != -ENOSYS) {
- ret = PTR_ERR(param_probe);
+ if (IS_ERR(param) && PTR_ERR(param) != -ENOSYS) {
+ ret = PTR_ERR(param);
dev_dbg(&mci->dev, "Failed to add 'probe' parameter to the MCI device\n");
goto err_unregister;
}
+ param = dev_add_param_bool(&mci->dev, "broken_cd", NULL, NULL,
+ &host->broken_cd, mci);
+
+ if (IS_ERR(param) && PTR_ERR(param) != -ENOSYS) {
+ ret = PTR_ERR(param);
+ dev_dbg(&mci->dev, "Failed to add 'broken_cd' parameter to the MCI device\n");
+ goto err_unregister;
+ }
+
if (IS_ENABLED(CONFIG_MCI_INFO))
mci->dev.info = mci_info;
@@ -1870,6 +2492,9 @@ int mci_register(struct mci_host *host)
if (IS_ENABLED(CONFIG_MCI_STARTUP))
mci_card_probe(mci);
+ if (!(host->caps2 & MMC_CAP2_NO_SD) && dev_of_node(host->hw_dev))
+ of_register_fixup(of_broken_cd_fixup, host);
+
list_add_tail(&mci->list, &mci_list);
return 0;
@@ -1936,13 +2561,40 @@ void mci_of_parse_node(struct mci_host *host,
}
}
+ host->broken_cd = of_property_read_bool(np, "broken-cd");
host->non_removable = of_property_read_bool(np, "non-removable");
- host->no_sd = of_property_read_bool(np, "no-sd");
+ host->disable_wp = of_property_read_bool(np, "disable-wp");
+
+ if (of_property_read_bool(np, "full-pwr-cycle"))
+ host->caps2 |= MMC_CAP2_FULL_PWR_CYCLE;
+ if (of_property_read_bool(np, "full-pwr-cycle-in-suspend"))
+ host->caps2 |= MMC_CAP2_FULL_PWR_CYCLE_IN_SUSPEND;
+ if (of_property_read_bool(np, "no-sdio"))
+ host->caps2 |= MMC_CAP2_NO_SDIO;
+ if (of_property_read_bool(np, "no-sd"))
+ host->caps2 |= MMC_CAP2_NO_SD;
+ if (of_property_read_bool(np, "no-mmc"))
+ host->caps2 |= MMC_CAP2_NO_MMC;
+ if (IS_ENABLED(CONFIG_MCI_TUNING)) {
+ if (of_property_read_bool(np, "mmc-hs200-1_8v"))
+ host->caps2 |= MMC_CAP2_HS200_1_8V_SDR;
+ if (of_property_read_bool(np, "mmc-hs200-1_2v"))
+ host->caps2 |= MMC_CAP2_HS200_1_2V_SDR;
+ if (of_property_read_bool(np, "mmc-hs400-1_8v"))
+ host->caps2 |= MMC_CAP2_HS400_1_8V | MMC_CAP2_HS200_1_8V_SDR;
+ if (of_property_read_bool(np, "mmc-hs400-1_2v"))
+ host->caps2 |= MMC_CAP2_HS400_1_2V | MMC_CAP2_HS200_1_2V_SDR;
+ if (of_property_read_bool(np, "mmc-hs400-enhanced-strobe"))
+ host->caps2 |= MMC_CAP2_HS400_ES;
+ if (of_property_read_bool(np, "no-mmc-hs400"))
+ host->caps2 &= ~(MMC_CAP2_HS400_1_8V | MMC_CAP2_HS400_1_2V |
+ MMC_CAP2_HS400_ES);
+ }
}
void mci_of_parse(struct mci_host *host)
{
- return mci_of_parse_node(host, host->hw_dev->device_node);
+ return mci_of_parse_node(host, host->hw_dev->of_node);
}
struct mci *mci_get_device_by_name(const char *name)
diff --git a/drivers/mci/mci_spi.c b/drivers/mci/mci_spi.c
index 42072da58a..ad743d19d9 100644
--- a/drivers/mci/mci_spi.c
+++ b/drivers/mci/mci_spi.c
@@ -1,30 +1,12 @@
-/*
- * (C) Copyright 2011 - Franck JULLIEN <elec4fun@gmail.com>
- *
- * This code was inspired from u-boot mmc_spi.c:
- * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
- *
- * and linux mmc_spi.c:
- * (C) Copyright 2005, Intec Automation,
- * Mike Lavender (mike@steroidmicros)
- * (C) Copyright 2006-2007, David Brownell
- * (C) Copyright 2007, Axis Communications,
- * Hans-Peter Nilsson (hp@axis.com)
- * (C) Copyright 2007, ATRON electronic GmbH,
- * Jan Nikitenko <jan.nikitenko@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- *
- */
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2011 Franck JULLIEN <elec4fun@gmail.com>
+// SPDX-FileCopyrightText: 2010 Thomas Chou <thomas@wytron.com.tw>
+// SPDX-FileCopyrightText: 2005 Intec Automation (Mike Lavender <mike@steroidmicros>)
+// SPDX-FileCopyrightText: 2006-2007 David Brownell
+// SPDX-FileCopyrightText: 2007 Axis Communications (Hans-Peter Nilsson <hp@axis.com>)
+// SPDX-FileCopyrightText: 2007 ATRON electronic GmbH (Jan Nikitenko <jan.nikitenko@gmail.com>)
+
+/* This code was inspired from u-boot mmc_spi.c and linux mmc_spi.c. */
#include <common.h>
#include <init.h>
@@ -36,6 +18,8 @@
#include <mci.h>
#include <crc.h>
#include <crc7.h>
+#include <of.h>
+#include <linux/gpio/consumer.h>
#define to_spi_host(mci) container_of(mci, struct mmc_spi_host, mci)
#define spi_setup(spi) spi->master->setup(spi)
@@ -64,7 +48,8 @@
struct mmc_spi_host {
struct mci_host mci;
struct spi_device *spi;
- struct device_d *dev;
+ struct device *dev;
+ struct gpio_desc *detect_pin;
/* for bulk data transfers */
struct spi_transfer t_tx;
@@ -329,7 +314,7 @@ static void mmc_spi_set_ios(struct mci_host *mci, struct mci_ios *ios)
}
}
-static int mmc_spi_init(struct mci_host *mci, struct device_d *mci_dev)
+static int mmc_spi_init(struct mci_host *mci, struct device *mci_dev)
{
struct mmc_spi_host *host = to_spi_host(mci);
mmc_spi_readbytes(host, 10, NULL);
@@ -369,8 +354,23 @@ static int mmc_spi_init(struct mci_host *mci, struct device_d *mci_dev)
return 0;
}
-static int spi_mci_probe(struct device_d *dev)
+static int spi_mci_card_present(struct mci_host *mci)
{
+ struct mmc_spi_host *host = to_spi_host(mci);
+ int ret;
+
+ /* No gpio, assume card is present */
+ if (IS_ERR_OR_NULL(host->detect_pin))
+ return 1;
+
+ ret = gpiod_get_value(host->detect_pin);
+
+ return ret == 0 ? 1 : 0;
+}
+
+static int spi_mci_probe(struct device *dev)
+{
+ struct device_node *np = dev_of_node(dev);
struct spi_device *spi = (struct spi_device *)dev->type_data;
struct mmc_spi_host *host;
void *ones;
@@ -380,6 +380,7 @@ static int spi_mci_probe(struct device_d *dev)
host->mci.send_cmd = mmc_spi_request;
host->mci.set_ios = mmc_spi_set_ios;
host->mci.init = mmc_spi_init;
+ host->mci.card_present = spi_mci_card_present;
host->mci.hw_dev = dev;
/* MMC and SD specs only seem to care that sampling is on the
@@ -434,13 +435,27 @@ static int spi_mci_probe(struct device_d *dev)
host->mci.voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
host->mci.host_caps = MMC_CAP_SPI;
+ if (np) {
+ host->mci.devname = xstrdup(of_alias_get(np));
+ host->detect_pin = gpiod_get_optional(dev, NULL, GPIOD_IN);
+ if (IS_ERR(host->detect_pin))
+ dev_warn(dev, "Failed to get 'reset' GPIO (ignored)\n");
+ }
+
mci_register(&host->mci);
return 0;
}
-static struct driver_d spi_mci_driver = {
+static __maybe_unused struct of_device_id spi_mci_compatible[] = {
+ { .compatible = "mmc-spi-slot" },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, spi_mci_compatible);
+
+static struct driver spi_mci_driver = {
.name = "spi_mci",
.probe = spi_mci_probe,
+ .of_compatible = DRV_OF_COMPAT(spi_mci_compatible),
};
device_spi_driver(spi_mci_driver);
diff --git a/drivers/mci/mmci.c b/drivers/mci/mmci.c
index f45557d4f7..a16deba854 100644
--- a/drivers/mci/mmci.c
+++ b/drivers/mci/mmci.c
@@ -1,22 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2010 ST-Ericsson SA
+
/*
* ARM PrimeCell MultiMedia Card Interface - PL180
*
- * Copyright (C) ST-Ericsson SA 2010
- *
* Author: Ulf Hansson <ulf.hansson@stericsson.com>
* Author: Martin Lundholm <martin.xa.lundholm@stericsson.com>
* Ported to drivers/mmc/ by: Matt Waddel <matt.waddel@linaro.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
*/
#include <common.h>
@@ -106,7 +96,7 @@ static struct variant_data variant_ux500v2 = {
struct mmci_host {
struct mci_host mci;
void __iomem *base;
- struct device_d *hw_dev;
+ struct device *hw_dev;
struct mmci_platform_data *plat;
struct clk *clk;
unsigned long mclk;
@@ -444,7 +434,7 @@ static int mci_request(struct mci_host *mci, struct mci_cmd *cmd, struct mci_dat
}
/* MMC uses open drain drivers in the enumeration phase */
-static int mci_reset(struct mci_host *mci, struct device_d *mci_dev)
+static int mci_reset(struct mci_host *mci, struct device *mci_dev)
{
struct mmci_host *host = to_mci_host(mci);
struct variant_data *variant = host->variant;
@@ -561,8 +551,8 @@ static int mmci_of_parse(struct device_node *np,
static int mmci_probe(struct amba_device *dev, const struct amba_id *id)
{
- struct device_d *hw_dev = &dev->dev;
- struct device_node *np = hw_dev->device_node;
+ struct device *hw_dev = &dev->dev;
+ struct device_node *np = hw_dev->of_node;
struct mmci_platform_data *plat = hw_dev->platform_data;
struct variant_data *variant = id->data;
u32 sdi_u32;
@@ -709,9 +699,4 @@ static struct amba_driver mmci_driver = {
.id_table = mmci_ids,
};
-static int mmci_init(void)
-{
- amba_driver_register(&mmci_driver);
- return 0;
-}
-device_initcall(mmci_init);
+device_amba_driver(mmci_driver);
diff --git a/drivers/mci/mmci.h b/drivers/mci/mmci.h
index 20a31a1cfe..328f21d2d6 100644
--- a/drivers/mci/mmci.h
+++ b/drivers/mci/mmci.h
@@ -1,12 +1,7 @@
-/*
- * linux/drivers/mmc/host/mmci.h - ARM PrimeCell MMCI PL180/1 driver
- *
- * Copyright (C) 2003 Deep Blue Solutions, Ltd, All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- */
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* SPDX-FileCopyrightText: 2003 Deep Blue Solutions, Ltd */
+
+/* linux/drivers/mmc/host/mmci.h - ARM PrimeCell MMCI PL180/1 driver */
#define COMMAND_REG_DELAY 300
#define DATA_REG_DELAY 1000
diff --git a/drivers/mci/mxs.c b/drivers/mci/mxs.c
index afd6a56397..6883b78d5c 100644
--- a/drivers/mci/mxs.c
+++ b/drivers/mci/mxs.c
@@ -1,26 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// SPDX-FileCopyrightText: 2010 Juergen Beisert <jbe@pengutronix.de>, Pengutronix
+// SPDX-FileCopyrightText: 2007 SigmaTel, Inc. (Ioannis Kappas <ikappas@sigmatel.com>)
+// SPDX-FileCopyrightText: 2003 Russell King
+// SPDX-FileCopyrightText: 2004-2005 Pierre Ossman
+// SPDX-FileCopyrightText: 2008-2009 Freescale Semiconductor, Inc
+// SPDX-FileCopyrightText: 2008 Embedded Alley Solutions, Inc
+
/*
- * Copyright (C) 2010 Juergen Beisert, Pengutronix <jbe@pengutronix.de>
- *
- * This code is based on:
- *
- * Copyright (C) 2007 SigmaTel, Inc., Ioannis Kappas <ikappas@sigmatel.com>
- *
- * Portions copyright (C) 2003 Russell King, PXA MMCI Driver
- * Portions copyright (C) 2004-2005 Pierre Ossman, W83L51xD SD/MMC driver
- *
- * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
- * Copyright 2008 Embedded Alley Solutions, Inc All Rights Reserved.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
+ * This code is reused code from the PXA MMCI Driver and W83L51xD SD/MMC driver.
*/
/**
@@ -40,9 +27,8 @@
#include <linux/clk.h>
#include <linux/err.h>
#include <asm/bitops.h>
-#include <mach/mci.h>
-#include <mach/clock.h>
-#include <mach/ssp.h>
+#include <mach/mxs/mci.h>
+#include <mach/mxs/ssp.h>
#define CLOCKRATE_MIN (1 * 1000 * 1000)
#define CLOCKRATE_MAX (480 * 1000 * 1000)
@@ -446,7 +432,7 @@ static unsigned mxs_mci_setup_clock_speed(struct mxs_mci_host *mxs_mci, unsigned
* @param mci_dev MCI device instance
* @return 0 on success, negative value else
*/
-static int mxs_mci_initialize(struct mci_host *host, struct device_d *mci_dev)
+static int mxs_mci_initialize(struct mci_host *host, struct device *mci_dev)
{
struct mxs_mci_host *mxs_mci = to_mxs_mci(host);
@@ -530,7 +516,7 @@ static void mxs_mci_set_ios(struct mci_host *host, struct mci_ios *ios)
const unsigned char bus_width[3] = { 1, 4, 8 };
-static void mxs_mci_info(struct device_d *hw_dev)
+static void mxs_mci_info(struct device *hw_dev)
{
struct mxs_mci_host *mxs_mci = hw_dev->priv;
@@ -542,7 +528,7 @@ static void mxs_mci_info(struct device_d *hw_dev)
printf("\n");
}
-static int mxs_mci_probe(struct device_d *hw_dev)
+static int mxs_mci_probe(struct device *hw_dev)
{
struct resource *iores;
struct mxs_mci_platform_data *pd = hw_dev->platform_data;
@@ -611,8 +597,9 @@ static __maybe_unused struct of_device_id mxs_mmc_compatible[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, mxs_mmc_compatible);
-static struct driver_d mxs_mci_driver = {
+static struct driver mxs_mci_driver = {
.name = "mxs_mci",
.probe = mxs_mci_probe,
.of_compatible = DRV_OF_COMPAT(mxs_mmc_compatible),
diff --git a/drivers/mci/omap_hsmmc.c b/drivers/mci/omap_hsmmc.c
index 4e809bae4f..41d5a62f32 100644
--- a/drivers/mci/omap_hsmmc.c
+++ b/drivers/mci/omap_hsmmc.c
@@ -1,22 +1,6 @@
-/*
- * (C) Copyright 2008
- * Texas Instruments, <www.ti.com>
- * Sukumar Ghorai <s-ghorai@ti.com>
- *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation's version 2 of
- * the License.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2008 Texas Instruments (http://www.ti.com/, Sukumar Ghorai <s-ghorai@ti.com>)
+
/* #define DEBUG */
#include <config.h>
#include <common.h>
@@ -28,12 +12,12 @@
#include <io.h>
#include <linux/err.h>
-#include <mach/omap_hsmmc.h>
+#include <mach/omap/omap_hsmmc.h>
#if defined(CONFIG_MFD_TWL6030) && \
defined(CONFIG_MCI_OMAP_HSMMC) && \
defined(CONFIG_ARCH_OMAP4)
-#include <mach/omap4_twl6030_mmc.h>
+#include <mach/omap/omap4_twl6030_mmc.h>
#endif
struct hsmmc {
@@ -186,7 +170,7 @@ static struct omap_mmc_driver_data omap4_data = {
struct omap_hsmmc {
struct mci_host mci;
- struct device_d *dev;
+ struct device *dev;
struct hsmmc *base;
void __iomem *iobase;
};
@@ -223,7 +207,7 @@ static int mmc_init_stream(struct omap_hsmmc *hsmmc)
return 0;
}
-static int mmc_init_setup(struct mci_host *mci, struct device_d *dev)
+static int mmc_init_setup(struct mci_host *mci, struct device *dev)
{
struct omap_hsmmc *hsmmc = to_hsmmc(mci);
struct hsmmc *mmc_base = hsmmc->base;
@@ -584,14 +568,7 @@ static void mmc_set_ios(struct mci_host *mci, struct mci_ios *ios)
writel(readl(&mmc_base->sysctl) | CEN_ENABLE, &mmc_base->sysctl);
}
-static int omap_mmc_detect(struct device_d *dev)
-{
- struct omap_hsmmc *hsmmc = dev->priv;
-
- return mci_detect_card(&hsmmc->mci);
-}
-
-static int omap_mmc_probe(struct device_d *dev)
+static int omap_mmc_probe(struct device *dev)
{
struct resource *iores;
struct omap_hsmmc *hsmmc;
@@ -636,9 +613,6 @@ static int omap_mmc_probe(struct device_d *dev)
mci_of_parse(&hsmmc->mci);
- dev->priv = hsmmc;
- dev->detect = omap_mmc_detect,
-
mci_register(&hsmmc->mci);
return 0;
@@ -664,11 +638,18 @@ static __maybe_unused struct of_device_id omap_mmc_dt_ids[] = {
.compatible = "ti,omap4-hsmmc",
.data = &omap4_data,
}, {
+ .compatible = "ti,am335-sdhci",
+ .data = &omap4_data,
+ }, {
+ .compatible = "ti,am437-sdhci",
+ .data = &omap4_data,
+ }, {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, omap_mmc_dt_ids);
-static struct driver_d omap_mmc_driver = {
+static struct driver omap_mmc_driver = {
.name = "omap-hsmmc",
.probe = omap_mmc_probe,
.id_table = omap_mmc_ids,
diff --git a/drivers/mci/pxamci.c b/drivers/mci/pxamci.c
index 1a33661d0f..5df1ef5cb6 100644
--- a/drivers/mci/pxamci.c
+++ b/drivers/mci/pxamci.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2011 Robert Jarzmik
+
/*
- * PXA MCI driver
- *
- * Copyright (C) 2011 Robert Jarzmik
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
+ * PXA MCI driver
* Insprired by linux kernel driver
*/
@@ -19,9 +15,9 @@
#include <mci.h>
#include <linux/err.h>
-#include <mach/clock.h>
-#include <mach/mci_pxa2xx.h>
-#include <mach/pxa-regs.h>
+#include <mach/pxa/clock.h>
+#include <mach/pxa/mci_pxa2xx.h>
+#include <mach/pxa/pxa-regs.h>
#include "pxamci.h"
#define DRIVER_NAME "pxa-mmc"
@@ -30,7 +26,7 @@
#define TX_TIMEOUT (250 * MSECOND)
#define CMD_TIMEOUT (100 * MSECOND)
-static void clk_enable(void)
+static void mmc_clk_enable(void)
{
CKEN |= CKEN_MMC;
}
@@ -323,7 +319,7 @@ static void pxamci_set_ios(struct mci_host *mci, struct mci_ios *ios)
mmc_writel(host->clkrt, MMC_CLKRT);
}
-static int pxamci_init(struct mci_host *mci, struct device_d *dev)
+static int pxamci_init(struct mci_host *mci, struct device *dev)
{
struct pxamci_host *host = to_pxamci(mci);
@@ -332,13 +328,13 @@ static int pxamci_init(struct mci_host *mci, struct device_d *dev)
return 0;
}
-static int pxamci_probe(struct device_d *dev)
+static int pxamci_probe(struct device *dev)
{
struct resource *iores;
struct pxamci_host *host;
int gpio_power = -1;
- clk_enable();
+ mmc_clk_enable();
host = xzalloc(sizeof(*host));
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores))
@@ -379,7 +375,7 @@ static int pxamci_probe(struct device_d *dev)
return 0;
}
-static struct driver_d pxamci_driver = {
+static struct driver pxamci_driver = {
.name = DRIVER_NAME,
.probe = pxamci_probe,
};
diff --git a/drivers/mci/pxamci.h b/drivers/mci/pxamci.h
index 07dea451c1..3dd93cb267 100644
--- a/drivers/mci/pxamci.h
+++ b/drivers/mci/pxamci.h
@@ -1,12 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/* SPDX-FileCopyrightText: 2011 Robert Jarzmik */
+
/*
- * PXA MCI driver
- *
- * Copyright (C) 2011 Robert Jarzmik
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
+ * PXA MCI driver
* Insprired by linux kernel driver
*/
diff --git a/drivers/mci/rockchip-dwcmshc-sdhci.c b/drivers/mci/rockchip-dwcmshc-sdhci.c
new file mode 100644
index 0000000000..f503dbae65
--- /dev/null
+++ b/drivers/mci/rockchip-dwcmshc-sdhci.c
@@ -0,0 +1,361 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <clock.h>
+#include <common.h>
+#include <driver.h>
+#include <init.h>
+#include <linux/clk.h>
+#include <mci.h>
+#include <dma.h>
+#include <linux/iopoll.h>
+
+#include "sdhci.h"
+
+/* DWCMSHC specific Mode Select value */
+#define DWCMSHC_CTRL_HS400 0x7
+
+#define DWCMSHC_VER_ID 0x500
+#define DWCMSHC_VER_TYPE 0x504
+#define DWCMSHC_HOST_CTRL3 0x508
+#define DWCMSHC_EMMC_CONTROL 0x52c
+#define DWCMSHC_EMMC_ATCTRL 0x540
+
+/* Rockchip specific Registers */
+#define DWCMSHC_EMMC_DLL_CTRL 0x800
+#define DWCMSHC_EMMC_DLL_RXCLK 0x804
+#define DWCMSHC_EMMC_DLL_TXCLK 0x808
+#define DWCMSHC_EMMC_DLL_STRBIN 0x80c
+#define DECMSHC_EMMC_DLL_CMDOUT 0x810
+#define DWCMSHC_EMMC_DLL_STATUS0 0x840
+#define DWCMSHC_EMMC_DLL_START BIT(0)
+#define DWCMSHC_EMMC_DLL_LOCKED BIT(8)
+#define DWCMSHC_EMMC_DLL_TIMEOUT BIT(9)
+#define DWCMSHC_EMMC_DLL_RXCLK_SRCSEL 29
+#define DWCMSHC_EMMC_DLL_START_POINT 16
+#define DWCMSHC_EMMC_DLL_INC 8
+#define DWCMSHC_EMMC_DLL_BYPASS BIT(24)
+#define DWCMSHC_EMMC_DLL_DLYENA BIT(27)
+#define DLL_TXCLK_TAPNUM_DEFAULT 0x10
+#define DLL_TXCLK_TAPNUM_90_DEGREES 0xA
+#define DLL_TXCLK_TAPNUM_FROM_SW BIT(24)
+#define DLL_STRBIN_TAPNUM_DEFAULT 0x8
+#define DLL_STRBIN_TAPNUM_FROM_SW BIT(24)
+#define DLL_STRBIN_DELAY_NUM_SEL BIT(26)
+#define DLL_STRBIN_DELAY_NUM_OFFSET 16
+#define DLL_STRBIN_DELAY_NUM_DEFAULT 0x16
+#define DLL_RXCLK_NO_INVERTER 1
+#define DLL_RXCLK_INVERTER 0
+#define DLL_CMDOUT_TAPNUM_90_DEGREES 0x8
+#define DLL_RXCLK_ORI_GATE BIT(31)
+#define DLL_CMDOUT_TAPNUM_FROM_SW BIT(24)
+#define DLL_CMDOUT_SRC_CLK_NEG BIT(28)
+#define DLL_CMDOUT_EN_SRC_CLK_NEG BIT(29)
+
+#define DLL_LOCK_WO_TMOUT(x) \
+ ((((x) & DWCMSHC_EMMC_DLL_LOCKED) == DWCMSHC_EMMC_DLL_LOCKED) && \
+ (((x) & DWCMSHC_EMMC_DLL_TIMEOUT) == 0))
+
+#define SDHCI_DWCMSHC_INT_DATA_MASK SDHCI_INT_XFER_COMPLETE | \
+ SDHCI_INT_DMA | \
+ SDHCI_INT_SPACE_AVAIL | \
+ SDHCI_INT_DATA_AVAIL | \
+ SDHCI_INT_DATA_TIMEOUT | \
+ SDHCI_INT_DATA_CRC | \
+ SDHCI_INT_DATA_END_BIT
+
+#define SDHCI_DWCMSHC_INT_CMD_MASK SDHCI_INT_CMD_COMPLETE | \
+ SDHCI_INT_TIMEOUT | \
+ SDHCI_INT_CRC | \
+ SDHCI_INT_END_BIT | \
+ SDHCI_INT_INDEX
+
+enum {
+ CLK_CORE,
+ CLK_BUS,
+ CLK_AXI,
+ CLK_BLOCK,
+ CLK_TIMER,
+ CLK_MAX,
+};
+
+struct rk_sdhci_host {
+ struct mci_host mci;
+ struct sdhci sdhci;
+ struct clk_bulk_data clks[CLK_MAX];
+};
+
+
+static inline
+struct rk_sdhci_host *to_rk_sdhci_host(struct mci_host *mci)
+{
+ return container_of(mci, struct rk_sdhci_host, mci);
+}
+
+static int rk_sdhci_card_present(struct mci_host *mci)
+{
+ struct rk_sdhci_host *host = to_rk_sdhci_host(mci);
+
+ return !!(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & SDHCI_CARD_DETECT);
+}
+
+static int rk_sdhci_init(struct mci_host *mci, struct device *dev)
+{
+ struct rk_sdhci_host *host = to_rk_sdhci_host(mci);
+ int ret;
+
+ ret = sdhci_reset(&host->sdhci, SDHCI_RESET_ALL);
+ if (ret)
+ return ret;
+
+ sdhci_write8(&host->sdhci, SDHCI_POWER_CONTROL,
+ SDHCI_BUS_VOLTAGE_330 | SDHCI_BUS_POWER_EN);
+ udelay(400);
+
+ sdhci_write32(&host->sdhci, SDHCI_INT_ENABLE,
+ SDHCI_DWCMSHC_INT_DATA_MASK |
+ SDHCI_DWCMSHC_INT_CMD_MASK);
+ sdhci_write32(&host->sdhci, SDHCI_SIGNAL_ENABLE, 0x00);
+
+ /* Disable cmd conflict check */
+ sdhci_write32(&host->sdhci, DWCMSHC_HOST_CTRL3, 0x0);
+ /* Reset previous settings */
+ sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_TXCLK, 0);
+ sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_STRBIN, 0);
+
+ return 0;
+}
+
+static void rk_sdhci_set_clock(struct rk_sdhci_host *host, unsigned int clock)
+{
+ u32 txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT, extra;
+ int err;
+
+ host->mci.clock = 0;
+
+ /* DO NOT TOUCH THIS SETTING */
+ extra = DWCMSHC_EMMC_DLL_DLYENA |
+ DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL;
+ sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_RXCLK, extra);
+
+ if (clock == 0)
+ return;
+
+ /* Rockchip platform only support 375KHz for identify mode */
+ if (clock <= 400000)
+ clock = 375000;
+
+ clk_set_rate(host->clks[CLK_CORE].clk, clock);
+
+ sdhci_set_clock(&host->sdhci, clock, clk_get_rate(host->clks[CLK_CORE].clk));
+
+ /* Disable cmd conflict check */
+ extra = sdhci_read32(&host->sdhci, DWCMSHC_HOST_CTRL3);
+ extra &= ~BIT(0);
+ sdhci_write32(&host->sdhci, DWCMSHC_HOST_CTRL3, extra);
+
+ if (clock <= 52000000) {
+ /*
+ * Disable DLL and reset both of sample and drive clock.
+ * The bypass bit and start bit need to be set if DLL is not locked.
+ */
+ sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_CTRL,
+ DWCMSHC_EMMC_DLL_BYPASS | DWCMSHC_EMMC_DLL_START);
+ sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_RXCLK, DLL_RXCLK_ORI_GATE);
+ sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_TXCLK, 0);
+ sdhci_write32(&host->sdhci, DECMSHC_EMMC_DLL_CMDOUT, 0);
+ /*
+ * Before switching to hs400es mode, the driver will enable
+ * enhanced strobe first. PHY needs to configure the parameters
+ * of enhanced strobe first.
+ */
+ extra = DWCMSHC_EMMC_DLL_DLYENA |
+ DLL_STRBIN_DELAY_NUM_SEL |
+ DLL_STRBIN_DELAY_NUM_DEFAULT << DLL_STRBIN_DELAY_NUM_OFFSET;
+ sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_STRBIN, extra);
+ return;
+ }
+
+ /* Reset DLL */
+ sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_CTRL, BIT(1));
+ udelay(1);
+ sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_CTRL, 0x0);
+
+ /* Init DLL settings */
+ extra = 0x5 << DWCMSHC_EMMC_DLL_START_POINT |
+ 0x2 << DWCMSHC_EMMC_DLL_INC |
+ DWCMSHC_EMMC_DLL_START;
+ sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_CTRL, extra);
+ err = readl_poll_timeout(host->sdhci.base + DWCMSHC_EMMC_DLL_STATUS0,
+ extra, DLL_LOCK_WO_TMOUT(extra),
+ 500 * USEC_PER_MSEC);
+ if (err) {
+ dev_err(host->mci.hw_dev, "DLL lock timeout!\n");
+ return;
+ }
+
+ extra = 0x1 << 16 | /* tune clock stop en */
+ 0x2 << 17 | /* pre-change delay */
+ 0x3 << 19; /* post-change delay */
+ sdhci_write32(&host->sdhci, DWCMSHC_EMMC_ATCTRL, extra);
+
+ extra = DWCMSHC_EMMC_DLL_DLYENA |
+ DLL_TXCLK_TAPNUM_FROM_SW |
+ txclk_tapnum;
+ sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_TXCLK, extra);
+
+ extra = DWCMSHC_EMMC_DLL_DLYENA |
+ DLL_STRBIN_TAPNUM_DEFAULT |
+ DLL_STRBIN_TAPNUM_FROM_SW;
+ sdhci_write32(&host->sdhci, DWCMSHC_EMMC_DLL_STRBIN, extra);
+}
+
+static void rk_sdhci_set_ios(struct mci_host *mci, struct mci_ios *ios)
+{
+ struct rk_sdhci_host *host = to_rk_sdhci_host(mci);
+ u16 val;
+
+ /* stop clock */
+ sdhci_write16(&host->sdhci, SDHCI_CLOCK_CONTROL, 0);
+
+ if (ios->clock)
+ rk_sdhci_set_clock(host, ios->clock);
+
+ sdhci_set_bus_width(&host->sdhci, ios->bus_width);
+
+ val = sdhci_read8(&host->sdhci, SDHCI_HOST_CONTROL);
+
+ if (ios->clock > 26000000)
+ val |= SDHCI_CTRL_HISPD;
+ else
+ val &= ~SDHCI_CTRL_HISPD;
+
+ sdhci_write8(&host->sdhci, SDHCI_HOST_CONTROL, val);
+}
+
+static void print_error(struct rk_sdhci_host *host, int cmdidx)
+{
+ dev_dbg(host->mci.hw_dev,
+ "error while transfering data for command %d\n", cmdidx);
+ dev_dbg(host->mci.hw_dev, "state = 0x%08x , interrupt = 0x%08x\n",
+ sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE),
+ sdhci_read32(&host->sdhci, SDHCI_INT_NORMAL_STATUS));
+}
+
+static int rk_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
+ struct mci_data *data)
+{
+ struct rk_sdhci_host *host = to_rk_sdhci_host(mci);
+ u32 command, xfer;
+ int ret;
+ dma_addr_t dma;
+
+ ret = sdhci_wait_idle_data(&host->sdhci, cmd);
+ if (ret)
+ return ret;
+
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
+
+ sdhci_write8(&host->sdhci, SDHCI_TIMEOUT_CONTROL, 0xe);
+
+ sdhci_setup_data_dma(&host->sdhci, data, &dma);
+
+ sdhci_set_cmd_xfer_mode(&host->sdhci, cmd, data,
+ dma == SDHCI_NO_DMA ? false : true,
+ &command, &xfer);
+
+ sdhci_write16(&host->sdhci, SDHCI_TRANSFER_MODE, xfer);
+
+ sdhci_write32(&host->sdhci, SDHCI_ARGUMENT, cmd->cmdarg);
+ sdhci_write16(&host->sdhci, SDHCI_COMMAND, command);
+
+ ret = sdhci_wait_for_done(&host->sdhci, SDHCI_INT_CMD_COMPLETE);
+ if (ret)
+ goto error;
+
+ sdhci_read_response(&host->sdhci, cmd);
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, SDHCI_INT_CMD_COMPLETE);
+
+ ret = sdhci_transfer_data_dma(&host->sdhci, data, dma);
+
+error:
+ if (ret) {
+ print_error(host, cmd->cmdidx);
+ sdhci_reset(&host->sdhci, SDHCI_RESET_CMD);
+ sdhci_reset(&host->sdhci, SDHCI_RESET_DATA);
+ }
+
+ sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, ~0);
+ return ret;
+}
+
+static int rk_sdhci_probe(struct device *dev)
+{
+ struct rk_sdhci_host *host;
+ struct resource *iores;
+ struct mci_host *mci;
+ int ret;
+
+ host = xzalloc(sizeof(*host));
+
+ mci = &host->mci;
+
+ iores = dev_request_mem_resource(dev, 0);
+ if (IS_ERR(iores))
+ return PTR_ERR(iores);
+
+ host->sdhci.base = IOMEM(iores->start);
+ host->sdhci.mci = mci;
+ mci->send_cmd = rk_sdhci_send_cmd;
+ mci->set_ios = rk_sdhci_set_ios;
+ mci->init = rk_sdhci_init;
+ mci->card_present = rk_sdhci_card_present;
+ mci->hw_dev = dev;
+
+ host->clks[CLK_CORE].id = "core";
+ host->clks[CLK_BUS].id = "bus";
+ host->clks[CLK_AXI].id = "axi";
+ host->clks[CLK_BLOCK].id = "block";
+ host->clks[CLK_TIMER].id = "timer";
+
+ ret = clk_bulk_get(host->mci.hw_dev, CLK_MAX, host->clks);
+ if (ret) {
+ dev_err(host->mci.hw_dev, "failed to get clocks: %s\n",
+ strerror(-ret));
+ return ret;
+ }
+
+ ret = clk_bulk_enable(CLK_MAX, host->clks);
+ if (ret) {
+ dev_err(host->mci.hw_dev, "failed to enable clocks: %s\n",
+ strerror(-ret));
+ return ret;
+ }
+
+ host->sdhci.max_clk = clk_get_rate(host->clks[CLK_CORE].clk);
+
+ mci_of_parse(&host->mci);
+
+ sdhci_setup_host(&host->sdhci);
+
+ dev->priv = host;
+
+ return mci_register(&host->mci);
+}
+
+static __maybe_unused struct of_device_id rk_sdhci_compatible[] = {
+ {
+ .compatible = "rockchip,rk3568-dwcmshc"
+ }, {
+ .compatible = "rockchip,rk3588-dwcmshc"
+ }, {
+ /* sentinel */
+ }
+};
+MODULE_DEVICE_TABLE(of, rk_sdhci_compatible);
+
+static struct driver rk_sdhci_driver = {
+ .name = "rk3568-dwcmshc-sdhci",
+ .probe = rk_sdhci_probe,
+ .of_compatible = DRV_OF_COMPAT(rk_sdhci_compatible),
+};
+device_platform_driver(rk_sdhci_driver);
diff --git a/drivers/mci/s3c.c b/drivers/mci/s3c.c
deleted file mode 100644
index 489609712c..0000000000
--- a/drivers/mci/s3c.c
+++ /dev/null
@@ -1,774 +0,0 @@
-/*
- * Copyright (C) 2010 Juergen Beisert <juergen@kreuzholzen.de>
- *
- * This code is partially based on u-boot code:
- *
- * This code is based on various Linux and u-boot sources:
- * Copyright (C) 2004-2006 maintech GmbH, Thomas Kleffel <tk@maintech.de>
- * Copyright (C) 2008 Simtec Electronics <ben-linux@fluff.org>
- * (C) Copyright 2006 by OpenMoko, Inc.
- * Author: Harald Welte <laforge@openmoko.org>
- * based on u-boot pxa MMC driver and linux/drivers/mmc/s3c2410mci.c
- * (C) 2005-2005 Thomas Kleffel
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- */
-
-/**
- * @file
- * @brief MCI card host interface for S3C2440 CPU
- */
-
-/* #define DEBUG */
-
-#include <common.h>
-#include <init.h>
-#include <mci.h>
-#include <errno.h>
-#include <clock.h>
-#include <io.h>
-#include <linux/err.h>
-#include <mach/s3c-mci.h>
-#include <mach/s3c-generic.h>
-#include <mach/s3c-iomap.h>
-
-#define GET_HOST_DATA(x) (x->priv)
-#define GET_MCI_PDATA(x) (x->platform_data)
-
-#define SDICON 0x0
-# define SDICON_SDRESET (1 << 8)
-# define SDICON_MMCCLOCK (1 << 5) /* this is a clock type SD or MMC style WTF? */
-# define SDICON_BYTEORDER (1 << 4)
-# define SDICON_SDIOIRQ (1 << 3)
-# define SDICON_RWAITEN (1 << 2)
-# define SDICON_FIFORESET (1 << 1) /* reserved bit on 2440 ????? */
-# define SDICON_CLKEN (1 << 0) /* enable/disable external clock */
-
-#define SDIPRE 0x4
-
-#define SDICMDARG 0x8
-
-#define SDICMDCON 0xc
-# define SDICMDCON_ABORT (1 << 12)
-# define SDICMDCON_WITHDATA (1 << 11)
-# define SDICMDCON_LONGRSP (1 << 10)
-# define SDICMDCON_WAITRSP (1 << 9)
-# define SDICMDCON_CMDSTART (1 << 8)
-# define SDICMDCON_SENDERHOST (1 << 6)
-# define SDICMDCON_INDEX (0x3f)
-
-#define SDICMDSTAT 0x10
-# define SDICMDSTAT_CRCFAIL (1 << 12)
-# define SDICMDSTAT_CMDSENT (1 << 11)
-# define SDICMDSTAT_CMDTIMEOUT (1 << 10)
-# define SDICMDSTAT_RSPFIN (1 << 9)
-# define SDICMDSTAT_XFERING (1 << 8)
-# define SDICMDSTAT_INDEX (0xff)
-
-#define SDIRSP0 0x14
-#define SDIRSP1 0x18
-#define SDIRSP2 0x1C
-#define SDIRSP3 0x20
-
-#define SDITIMER 0x24
-#define SDIBSIZE 0x28
-
-#define SDIDCON 0x2c
-# define SDIDCON_DS_BYTE (0 << 22)
-# define SDIDCON_DS_HALFWORD (1 << 22)
-# define SDIDCON_DS_WORD (2 << 22)
-# define SDIDCON_IRQPERIOD (1 << 21)
-# define SDIDCON_TXAFTERRESP (1 << 20)
-# define SDIDCON_RXAFTERCMD (1 << 19)
-# define SDIDCON_BUSYAFTERCMD (1 << 18)
-# define SDIDCON_BLOCKMODE (1 << 17)
-# define SDIDCON_WIDEBUS (1 << 16)
-# define SDIDCON_DMAEN (1 << 15)
-# define SDIDCON_STOP (0 << 14)
-# define SDIDCON_DATSTART (1 << 14)
-# define SDIDCON_DATMODE (3 << 12)
-# define SDIDCON_BLKNUM (0xfff)
-# define SDIDCON_XFER_READY (0 << 12)
-# define SDIDCON_XFER_CHKSTART (1 << 12)
-# define SDIDCON_XFER_RXSTART (2 << 12)
-# define SDIDCON_XFER_TXSTART (3 << 12)
-
-#define SDIDCNT 0x30
-# define SDIDCNT_BLKNUM_SHIFT 12
-
-#define SDIDSTA 0x34
-# define SDIDSTA_RDYWAITREQ (1 << 10)
-# define SDIDSTA_SDIOIRQDETECT (1 << 9)
-# define SDIDSTA_FIFOFAIL (1 << 8) /* reserved on 2440 */
-# define SDIDSTA_CRCFAIL (1 << 7)
-# define SDIDSTA_RXCRCFAIL (1 << 6)
-# define SDIDSTA_DATATIMEOUT (1 << 5)
-# define SDIDSTA_XFERFINISH (1 << 4)
-# define SDIDSTA_BUSYFINISH (1 << 3)
-# define SDIDSTA_SBITERR (1 << 2) /* reserved on 2410a/2440 */
-# define SDIDSTA_TXDATAON (1 << 1)
-# define SDIDSTA_RXDATAON (1 << 0)
-
-#define SDIFSTA 0x38
-# define SDIFSTA_FIFORESET (1<<16)
-# define SDIFSTA_FIFOFAIL (3<<14) /* 3 is correct (2 bits) */
-# define SDIFSTA_TFDET (1<<13)
-# define SDIFSTA_RFDET (1<<12)
-# define SDIFSTA_TFHALF (1<<11)
-# define SDIFSTA_TFEMPTY (1<<10)
-# define SDIFSTA_RFLAST (1<<9)
-# define SDIFSTA_RFFULL (1<<8)
-# define SDIFSTA_RFHALF (1<<7)
-# define SDIFSTA_COUNTMASK (0x7f)
-
-#define SDIIMSK 0x3C
-# define SDIIMSK_RESPONSECRC (1<<17)
-# define SDIIMSK_CMDSENT (1<<16)
-# define SDIIMSK_CMDTIMEOUT (1<<15)
-# define SDIIMSK_RESPONSEND (1<<14)
-# define SDIIMSK_READWAIT (1<<13)
-# define SDIIMSK_SDIOIRQ (1<<12)
-# define SDIIMSK_FIFOFAIL (1<<11)
-# define SDIIMSK_CRCSTATUS (1<<10)
-# define SDIIMSK_DATACRC (1<<9)
-# define SDIIMSK_DATATIMEOUT (1<<8)
-# define SDIIMSK_DATAFINISH (1<<7)
-# define SDIIMSK_BUSYFINISH (1<<6)
-# define SDIIMSK_SBITERR (1<<5) /* reserved 2440/2410a */
-# define SDIIMSK_TXFIFOHALF (1<<4)
-# define SDIIMSK_TXFIFOEMPTY (1<<3)
-# define SDIIMSK_RXFIFOLAST (1<<2)
-# define SDIIMSK_RXFIFOFULL (1<<1)
-# define SDIIMSK_RXFIFOHALF (1<<0)
-
-#define SDIDATA 0x40
-
-struct s3c_mci_host {
- struct mci_host host;
- void __iomem *base;
- int bus_width:2; /* 0 = 1 bit, 1 = 4 bit, 2 = 8 bit */
- unsigned clock; /* current clock in Hz */
- unsigned data_size; /* data transfer in bytes */
-};
-
-#define to_s3c_host(h) container_of(h, struct s3c_mci_host, host)
-
-/**
- * Finish a request
- * @param hw_dev Host interface instance
- *
- * Just a little bit paranoia.
- */
-static void s3c_finish_request(struct s3c_mci_host *host_data)
-{
- /* TODO ensure the engines are stopped */
-}
-
-/**
- * Setup a new clock frequency on this MCI bus
- * @param hw_dev Host interface instance
- * @param nc New clock value in Hz (can be 0)
- * @return New clock value (may differ from 'nc')
- */
-static unsigned s3c_setup_clock_speed(struct s3c_mci_host *host_data, unsigned nc)
-{
- unsigned clock;
- uint32_t mci_psc;
-
- if (nc == 0)
- return 0;
-
- clock = s3c_get_pclk();
- /* Calculate the required prescaler value to get the requested frequency */
- mci_psc = (clock + (nc >> 2)) / nc;
-
- if (mci_psc > 256) {
- mci_psc = 256;
- pr_warning("SD/MMC clock might be too high!\n");
- }
-
- writel(mci_psc - 1, host_data->base + SDIPRE);
-
- return clock / mci_psc;
-}
-
-/**
- * Reset the MCI engine (the hard way)
- * @param hw_dev Host interface instance
- *
- * This will reset everything in all registers of this unit!
- */
-static void s3c_mci_reset(struct s3c_mci_host *host_data)
-{
- /* reset the hardware */
- writel(SDICON_SDRESET, host_data->base + SDICON);
- /* wait until reset it finished */
- while (readl(host_data->base + SDICON) & SDICON_SDRESET)
- ;
-}
-
-/**
- * Initialize hard and software
- * @param hw_dev Host interface instance
- * @param mci_dev MCI device instance (might be NULL)
- */
-static int s3c_mci_initialize(struct s3c_mci_host *host_data, struct device_d *mci_dev)
-{
- s3c_mci_reset(host_data);
-
- /* restore last settings */
- host_data->clock = s3c_setup_clock_speed(host_data, host_data->clock);
- writel(0x007FFFFF, host_data->base + SDITIMER);
- writel(SDICON_MMCCLOCK, host_data->base + SDICON);
- writel(512, host_data->base + SDIBSIZE);
-
- return 0;
-}
-
-/**
- * Prepare engine's bits for the next command transfer
- * @param cmd_flags MCI's command flags
- * @param data_flags MCI's data flags
- * @return Register bits for this transfer
- */
-static uint32_t s3c_prepare_command_setup(unsigned cmd_flags, unsigned data_flags)
-{
- uint32_t reg;
-
- /* source (=host) */
- reg = SDICMDCON_SENDERHOST;
-
- if (cmd_flags & MMC_RSP_PRESENT) {
- reg |= SDICMDCON_WAITRSP;
- pr_debug("Command with response\n");
- }
- if (cmd_flags & MMC_RSP_136) {
- reg |= SDICMDCON_LONGRSP;
- pr_debug("Command with long response\n");
- }
- if (cmd_flags & MMC_RSP_CRC)
- ; /* FIXME */
- if (cmd_flags & MMC_RSP_BUSY)
- ; /* FIXME */
- if (cmd_flags & MMC_RSP_OPCODE)
- ; /* FIXME */
- if (data_flags != 0)
- reg |= SDICMDCON_WITHDATA;
-
- return reg;
-}
-
-/**
- * Prepare engine's bits for the next data transfer
- * @param hw_dev Host interface device instance
- * @param data_flags MCI's data flags
- * @return Register bits for this transfer
- */
-static uint32_t s3c_prepare_data_setup(struct s3c_mci_host *host_data, unsigned data_flags)
-{
- uint32_t reg = SDIDCON_BLOCKMODE; /* block mode only is supported */
-
- if (host_data->bus_width == 1)
- reg |= SDIDCON_WIDEBUS;
-
- /* enable any kind of data transfers on demand only */
- if (data_flags & MMC_DATA_WRITE)
- reg |= SDIDCON_TXAFTERRESP | SDIDCON_XFER_TXSTART;
-
- if (data_flags & MMC_DATA_READ)
- reg |= SDIDCON_RXAFTERCMD | SDIDCON_XFER_RXSTART;
-
- /* TODO: Support more than the 2440 CPU */
- reg |= SDIDCON_DS_WORD | SDIDCON_DATSTART;
-
- return reg;
-}
-
-/**
- * Terminate a current running transfer
- * @param hw_dev Host interface device instance
- * @return 0 on success
- *
- * Note: Try to stop a running transfer. This should not happen, as all
- * transfers must complete in this driver. But who knows... ;-)
- */
-static int s3c_terminate_transfer(struct s3c_mci_host *host_data)
-{
- unsigned stoptries = 3;
-
- while (readl(host_data->base + SDIDSTA) & (SDIDSTA_TXDATAON | SDIDSTA_RXDATAON)) {
- pr_debug("Transfer still in progress.\n");
-
- writel(SDIDCON_STOP, host_data->base + SDIDCON);
- s3c_mci_initialize(host_data, NULL);
-
- if ((stoptries--) == 0) {
- pr_warning("Cannot stop the engine!\n");
- return -EINVAL;
- }
- }
-
- return 0;
-}
-
-/**
- * Setup registers for data transfer
- * @param hw_dev Host interface device instance
- * @param data The data information (buffer, direction aso.)
- * @return 0 on success
- */
-static int s3c_prepare_data_transfer(struct s3c_mci_host *host_data, struct mci_data *data)
-{
- uint32_t reg;
-
- writel(data->blocksize, host_data->base + SDIBSIZE);
- reg = s3c_prepare_data_setup(host_data, data->flags);
- reg |= data->blocks & SDIDCON_BLKNUM;
- writel(reg, host_data->base + SDIDCON);
- writel(0x007FFFFF, host_data->base + SDITIMER);
-
- return 0;
-}
-
-/**
- * Send a command and receive the response
- * @param hw_dev Host interface device instance
- * @param cmd The command to handle
- * @param data The data information (buffer, direction aso.)
- * @return 0 on success
- */
-static int s3c_send_command(struct s3c_mci_host *host_data, struct mci_cmd *cmd,
- struct mci_data *data)
-{
- uint32_t reg, t1;
- int rc;
-
- writel(0x007FFFFF, host_data->base + SDITIMER);
-
- /* setup argument */
- writel(cmd->cmdarg, host_data->base + SDICMDARG);
-
- /* setup command and transfer characteristic */
- reg = s3c_prepare_command_setup(cmd->resp_type, data != NULL ? data->flags : 0);
- reg |= cmd->cmdidx & SDICMDCON_INDEX;
-
- /* run the command right now */
- writel(reg | SDICMDCON_CMDSTART, host_data->base + SDICMDCON);
- t1 = readl(host_data->base + SDICMDSTAT);
- /* wait until command is done */
- while (1) {
- reg = readl(host_data->base + SDICMDSTAT);
- /* done? */
- if (cmd->resp_type & MMC_RSP_PRESENT) {
- if (reg & SDICMDSTAT_RSPFIN) {
- writel(SDICMDSTAT_RSPFIN,
- host_data->base + SDICMDSTAT);
- rc = 0;
- break;
- }
- } else {
- if (reg & SDICMDSTAT_CMDSENT) {
- writel(SDICMDSTAT_CMDSENT,
- host_data->base + SDICMDSTAT);
- rc = 0;
- break;
- }
- }
- /* timeout? */
- if (reg & SDICMDSTAT_CMDTIMEOUT) {
- writel(SDICMDSTAT_CMDTIMEOUT,
- host_data->base + SDICMDSTAT);
- rc = -ETIMEDOUT;
- break;
- }
- }
-
- if ((rc == 0) && (cmd->resp_type & MMC_RSP_PRESENT)) {
- cmd->response[0] = readl(host_data->base + SDIRSP0);
- cmd->response[1] = readl(host_data->base + SDIRSP1);
- cmd->response[2] = readl(host_data->base + SDIRSP2);
- cmd->response[3] = readl(host_data->base + SDIRSP3);
- }
- /* do not disable the clock! */
- return rc;
-}
-
-/**
- * Clear major registers prior a new transaction
- * @param hw_dev Host interface device instance
- * @return 0 on success
- *
- * FIFO clear is only necessary on 2440, but doesn't hurt on 2410
- */
-static int s3c_prepare_engine(struct s3c_mci_host *host_data)
-{
- int rc;
-
- rc = s3c_terminate_transfer(host_data);
- if (rc != 0)
- return rc;
-
- writel(-1, host_data->base + SDICMDSTAT);
- writel(-1, host_data->base + SDIDSTA);
- writel(-1, host_data->base + SDIFSTA);
-
- return 0;
-}
-
-/**
- * Handle MCI commands without data
- * @param hw_dev Host interface device instance
- * @param cmd The command to handle
- * @return 0 on success
- *
- * This functions handles the following MCI commands:
- * - "broadcast command (BC)" without a response
- * - "broadcast commands with response (BCR)"
- * - "addressed command (AC)" with response, but without data
- */
-static int s3c_mci_std_cmds(struct s3c_mci_host *host_data, struct mci_cmd *cmd)
-{
- int rc;
-
- rc = s3c_prepare_engine(host_data);
- if (rc != 0)
- return 0;
-
- return s3c_send_command(host_data, cmd, NULL);
-}
-
-/**
- * Read one block of data from the FIFO
- * @param hw_dev Host interface device instance
- * @param data The data information (buffer, direction aso.)
- * @return 0 on success
- */
-static int s3c_mci_read_block(struct s3c_mci_host *host_data, struct mci_data *data)
-{
- uint32_t *p;
- unsigned cnt, data_size;
-
-#define READ_REASON_TO_FAIL (SDIDSTA_CRCFAIL | SDIDSTA_RXCRCFAIL | SDIDSTA_DATATIMEOUT)
-
- p = (uint32_t*)data->dest;
- data_size = data->blocksize * data->blocks;
-
- while (data_size > 0) {
-
- /* serious error? */
- if (readl(host_data->base + SDIDSTA) & READ_REASON_TO_FAIL) {
- pr_err("Failed while reading data\n");
- return -EIO;
- }
-
- /* now check the FIFO status */
- if (readl(host_data->base + SDIFSTA) & SDIFSTA_FIFOFAIL) {
- pr_err("Data loss due to FIFO overflow when reading\n");
- return -EIO;
- }
-
- /* we only want to read full words */
- cnt = (readl(host_data->base + SDIFSTA) & SDIFSTA_COUNTMASK) >> 2;
-
- /* read one chunk of data from the FIFO */
- while (cnt--) {
- *p = readl(host_data->base + SDIDATA);
- p++;
- if (data_size >= 4)
- data_size -= 4;
- else {
- data_size = 0;
- break;
- }
- }
- }
-
- return 0;
-}
-
-/**
- * Write one block of data into the FIFO
- * @param hw_dev Host interface device instance
- * @param cmd The command to handle
- * @param data The data information (buffer, direction aso.)
- * @return 0 on success
- *
- * We must ensure data in the FIFO when the command phase changes into the
- * data phase. To ensure this, the FIFO gets filled first, then the command.
- */
-static int s3c_mci_write_block(struct s3c_mci_host *host_data, struct mci_cmd *cmd,
- struct mci_data *data)
-{
- const uint32_t *p = (const uint32_t*)data->src;
- unsigned cnt, data_size;
- uint32_t reg;
-
-#define WRITE_REASON_TO_FAIL (SDIDSTA_CRCFAIL | SDIDSTA_DATATIMEOUT)
-
- data_size = data->blocksize * data->blocks;
- /*
- * With high clock rates we must fill the FIFO as early as possible
- * Its size is 16 words. We assume its empty, when this function is
- * entered.
- */
- cnt = 16;
- while (cnt--) {
- writel(*p, host_data->base + SDIDATA);
- p++;
- if (data_size >= 4)
- data_size -= 4;
- else {
- data_size = 0;
- break;
- }
- }
-
- /* data is now in place and waits for transmitt. Start the command right now */
- s3c_send_command(host_data, cmd, data);
-
- if ((reg = readl(host_data->base + SDIFSTA)) & SDIFSTA_FIFOFAIL) {
- pr_err("Command fails immediatly due to FIFO underrun when writing %08X\n",
- reg);
- return -EIO;
- }
-
- while (data_size > 0) {
-
- if (readl(host_data->base + SDIDSTA) & WRITE_REASON_TO_FAIL) {
- pr_err("Failed writing data\n");
- return -EIO;
- }
-
- /* now check the FIFO status */
- if ((reg = readl(host_data->base + SDIFSTA)) & SDIFSTA_FIFOFAIL) {
- pr_err("Data loss due to FIFO underrun when writing %08X\n",
- reg);
- return -EIO;
- }
-
- /* we only want to write full words */
- cnt = 16 - (((readl(host_data->base + SDIFSTA) & SDIFSTA_COUNTMASK) + 3) >> 2);
-
- /* fill the FIFO if it has free entries */
- while (cnt--) {
- writel(*p, host_data->base + SDIDATA);
- p++;
- if (data_size >= 4)
- data_size -= 4;
- else {
- data_size = 0;
- break;
- }
- }
- }
-
- return 0;
-}
-
-/**
- * Handle MCI commands with or without data
- * @param hw_dev Host interface device instance
- * @param cmd The command to handle
- * @param data The data information (buffer, direction aso.)
- * @return 0 on success
-*/
-static int s3c_mci_adtc(struct s3c_mci_host *host_data, struct mci_cmd *cmd,
- struct mci_data *data)
-{
- int rc;
-
- rc = s3c_prepare_engine(host_data);
- if (rc != 0)
- return rc;
-
- rc = s3c_prepare_data_transfer(host_data, data);
- if (rc != 0)
- return rc;
-
- if (data->flags & MMC_DATA_READ) {
- s3c_send_command(host_data, cmd, data);
- rc = s3c_mci_read_block(host_data, data);
- if (rc == 0) {
- while (!(readl(host_data->base + SDIDSTA) & SDIDSTA_XFERFINISH))
- ;
- } else
- s3c_terminate_transfer(host_data);
- }
-
- if (data->flags & MMC_DATA_WRITE) {
- rc = s3c_mci_write_block(host_data, cmd, data);
- if (rc == 0) {
- while (!(readl(host_data->base + SDIDSTA) & SDIDSTA_XFERFINISH))
- ;
- } else
- s3c_terminate_transfer(host_data);
- }
- writel(0, host_data->base + SDIDCON);
-
- return rc;
-}
-
-/* ------------------------- MCI API -------------------------------------- */
-
-/**
- * Keep the attached MMC/SD unit in a well know state
- * @param host MCI host
- * @param mci_dev MCI device instance
- * @return 0 on success, negative value else
- */
-static int mci_reset(struct mci_host *host, struct device_d *mci_dev)
-{
- struct s3c_mci_host *host_data = to_s3c_host(host);
-
- return s3c_mci_initialize(host_data, mci_dev);
-}
-
-/**
- * Process one command to the MCI card
- * @param host MCI host
- * @param cmd The command to process
- * @param data The data to handle in the command (can be NULL)
- * @return 0 on success, negative value else
- */
-static int mci_request(struct mci_host *host, struct mci_cmd *cmd,
- struct mci_data *data)
-{
- struct s3c_mci_host *host_data = to_s3c_host(host);
- int rc;
-
- /* enable clock */
- writel(readl(host_data->base + SDICON) | SDICON_CLKEN,
- host_data->base + SDICON);
-
- if ((cmd->resp_type == 0) || (data == NULL))
- rc = s3c_mci_std_cmds(host_data, cmd);
- else
- rc = s3c_mci_adtc(host_data, cmd, data); /* with response and data */
-
- s3c_finish_request(host_data);
-
- /* disable clock */
- writel(readl(host_data->base + SDICON) & ~SDICON_CLKEN,
- host_data->base + SDICON);
- return rc;
-}
-
-/**
- * Setup the bus width and IO speed
- * @param host MCI host
- * @param bus_width New bus width value (1, 4 or 8)
- * @param clock New clock in Hz (can be '0' to disable the clock)
- */
-static void mci_set_ios(struct mci_host *host, struct mci_ios *ios)
-{
- struct s3c_mci_host *host_data = to_s3c_host(host);
- uint32_t reg;
-
- switch (ios->bus_width) {
- case MMC_BUS_WIDTH_4:
- host_data->bus_width = 1;
- break;
- case MMC_BUS_WIDTH_1:
- host_data->bus_width = 0;
- break;
- default:
- return;
- }
-
- reg = readl(host_data->base + SDICON);
- if (ios->clock) {
- /* setup the IO clock frequency and enable it */
- host_data->clock = s3c_setup_clock_speed(host_data, ios->clock);
- reg |= SDICON_CLKEN; /* enable the clock */
- } else {
- reg &= ~SDICON_CLKEN; /* disable the clock */
- host_data->clock = 0;
- }
- writel(reg, host_data->base + SDICON);
-
- pr_debug("IO settings: bus width=%d, frequency=%u Hz\n",
- host_data->bus_width, host_data->clock);
-}
-
-/* ----------------------------------------------------------------------- */
-
-static void s3c_info(struct device_d *hw_dev)
-{
- struct s3c_mci_host *host = hw_dev->priv;
- struct s3c_mci_platform_data *pd = hw_dev->platform_data;
-
- printf(" Bus data width: %d bit\n", host->bus_width == 1 ? 4 : 1);
- printf(" Bus frequency: %u Hz\n", host->clock);
- printf(" Frequency limits: ");
- if (pd->f_min == 0)
- printf("no lower limit ");
- else
- printf("%u Hz lower limit ", pd->f_min);
- if (pd->f_max == 0)
- printf("- no upper limit");
- else
- printf("- %u Hz upper limit", pd->f_max);
- printf("\n Card detection support: %s\n",
- pd->gpio_detect != 0 ? "yes" : "no");
-}
-
-static int s3c_mci_probe(struct device_d *hw_dev)
-{
- struct resource *iores;
- struct s3c_mci_host *s3c_host;
- struct s3c_mci_platform_data *pd = hw_dev->platform_data;
-
- s3c_host = xzalloc(sizeof(*s3c_host));
- s3c_host->host.send_cmd = mci_request;
- s3c_host->host.set_ios = mci_set_ios;
- s3c_host->host.init = mci_reset;
-
- /* TODO replace by the global func: enable the SDI unit clock */
- writel(readl(S3C_CLOCK_POWER_BASE + 0x0c) | 0x200,
- S3C_CLOCK_POWER_BASE + 0x0c);
-
- if (pd == NULL) {
- pr_err("Missing platform data\n");
- return -EINVAL;
- }
-
- hw_dev->priv = s3c_host;
- iores = dev_request_mem_resource(hw_dev, 0);
- if (IS_ERR(iores))
- return PTR_ERR(iores);
- s3c_host->base = IOMEM(iores->start);
-
- s3c_host->host.hw_dev = hw_dev;
-
- /* feed forward the platform specific values */
- s3c_host->host.voltages = pd->voltages;
- s3c_host->host.host_caps = pd->caps;
- s3c_host->host.f_min = pd->f_min == 0 ? s3c_get_pclk() / 256 : pd->f_min;
- s3c_host->host.f_max = pd->f_max == 0 ? s3c_get_pclk() / 2 : pd->f_max;
-
- if (IS_ENABLED(CONFIG_MCI_INFO))
- hw_dev->info = s3c_info;
-
- /*
- * Start the clock to let the engine and the card finishes its startup
- */
- s3c_host->clock = s3c_setup_clock_speed(s3c_host, pd->f_min);
- writel(SDICON_FIFORESET | SDICON_MMCCLOCK, s3c_host->base + SDICON);
-
- return mci_register(&s3c_host->host);
-}
-
-static struct driver_d s3c_mci_driver = {
- .name = "s3c_mci",
- .probe = s3c_mci_probe,
-};
-device_platform_driver(s3c_mci_driver);
diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c
index 172c8343a1..8bba1e3bf9 100644
--- a/drivers/mci/sdhci.c
+++ b/drivers/mci/sdhci.c
@@ -1,12 +1,235 @@
-// SPDX-License-Identifier: GPL-2.0
+// SPDX-License-Identifier: GPL-2.0-only
#include <common.h>
#include <driver.h>
#include <mci.h>
#include <io.h>
+#include <dma.h>
+#include <linux/bitfield.h>
#include "sdhci.h"
+#define MAX_TUNING_LOOP 40
+#define SDHCI_MAKE_BLKSZ(dma, blksz) (((dma & 0x7) << 12) | (blksz & 0xFFF))
+
+enum sdhci_reset_reason {
+ SDHCI_RESET_FOR_INIT,
+ SDHCI_RESET_FOR_REQUEST_ERROR,
+ SDHCI_RESET_FOR_REQUEST_ERROR_DATA_ONLY,
+ SDHCI_RESET_FOR_TUNING_ABORT,
+ SDHCI_RESET_FOR_CARD_REMOVED,
+ SDHCI_RESET_FOR_CQE_RECOVERY,
+};
+
+static void sdhci_reset_for_reason(struct sdhci *host, enum sdhci_reset_reason reason)
+{
+ if (host->quirks2 & SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER) {
+ sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+ return;
+ }
+
+ switch (reason) {
+ case SDHCI_RESET_FOR_INIT:
+ sdhci_reset(host, SDHCI_RESET_CMD | SDHCI_RESET_DATA);
+ break;
+ case SDHCI_RESET_FOR_REQUEST_ERROR:
+ case SDHCI_RESET_FOR_TUNING_ABORT:
+ case SDHCI_RESET_FOR_CARD_REMOVED:
+ case SDHCI_RESET_FOR_CQE_RECOVERY:
+ sdhci_reset(host, SDHCI_RESET_CMD);
+ sdhci_reset(host, SDHCI_RESET_DATA);
+ break;
+ case SDHCI_RESET_FOR_REQUEST_ERROR_DATA_ONLY:
+ sdhci_reset(host, SDHCI_RESET_DATA);
+ break;
+ }
+}
+
+#define sdhci_reset_for(h, r) sdhci_reset_for_reason((h), SDHCI_RESET_FOR_##r)
+
+static int sdhci_send_command_retry(struct sdhci *host, struct mci_cmd *cmd)
+{
+ int timeout = 10;
+
+ while ((sdhci_read32(host, SDHCI_PRESENT_STATE) & SDHCI_CMD_INHIBIT_CMD)) {
+ if (!timeout--)
+ return -ETIMEDOUT;
+
+ mdelay(1);
+ }
+
+ return host->mci->send_cmd(host->mci, cmd, NULL);
+}
+
+/*
+ * We use sdhci_send_tuning() because mmc_send_tuning() is not a good fit. SDHCI
+ * tuning command does not have a data payload (or rather the hardware does it
+ * automatically) so mmc_send_tuning() will return -EIO. Also the tuning command
+ * interrupt setup is different to other commands and there is no timeout
+ * interrupt so special handling is needed.
+ */
+static int sdhci_send_tuning(struct sdhci *host, u32 opcode)
+{
+ struct mci_cmd cmd = {};
+ int ret;
+
+ cmd.cmdidx = opcode;
+ cmd.resp_type = MMC_RSP_R1 | MMC_CMD_ADTC;
+
+ /*
+ * In response to CMD19, the card sends 64 bytes of tuning
+ * block to the Host Controller. So we set the block size
+ * to 64 here.
+ */
+ if (cmd.cmdidx == MMC_SEND_TUNING_BLOCK_HS200 &&
+ host->mci->bus_width == MMC_BUS_WIDTH_8) {
+ sdhci_write16(host, SDHCI_BLOCK_SIZE, SDHCI_MAKE_BLKSZ(7, 128));
+ } else {
+ sdhci_write16(host, SDHCI_BLOCK_SIZE, SDHCI_MAKE_BLKSZ(7, 64));
+ }
+
+ ret = sdhci_send_command_retry(host, &cmd);
+
+ return ret;
+}
+
+static void sdhci_end_tuning(struct sdhci *host)
+{
+ sdhci_write32(host, SDHCI_INT_ENABLE, host->tuning_old_ier);
+ sdhci_write32(host, SDHCI_SIGNAL_ENABLE, host->tuning_old_sig);
+}
+
+static void sdhci_start_tuning(struct sdhci *host)
+{
+ u16 ctrl;
+
+ ctrl = sdhci_read16(host, SDHCI_HOST_CONTROL2);
+ ctrl |= SDHCI_CTRL_EXEC_TUNING;
+ sdhci_write16(host, SDHCI_HOST_CONTROL2, ctrl);
+
+ mdelay(1);
+
+ host->tuning_old_ier = sdhci_read32(host, SDHCI_INT_ENABLE);
+ host->tuning_old_sig = sdhci_read32(host, SDHCI_SIGNAL_ENABLE);
+
+ sdhci_write32(host, SDHCI_INT_ENABLE, SDHCI_INT_DATA_AVAIL);
+ sdhci_write32(host, SDHCI_SIGNAL_ENABLE, SDHCI_INT_DATA_AVAIL);
+}
+
+static void sdhci_reset_tuning(struct sdhci *host)
+{
+ u16 ctrl;
+
+ ctrl = sdhci_read16(host, SDHCI_HOST_CONTROL2);
+ ctrl &= ~SDHCI_CTRL_TUNED_CLK;
+ ctrl &= ~SDHCI_CTRL_EXEC_TUNING;
+ sdhci_write16(host, SDHCI_HOST_CONTROL2, ctrl);
+}
+
+static void sdhci_abort_tuning(struct sdhci *host, u32 opcode)
+{
+ sdhci_reset_tuning(host);
+
+ sdhci_reset_for(host, TUNING_ABORT);
+
+ sdhci_end_tuning(host);
+
+ mci_send_abort_tuning(host->mci->mci, opcode);
+}
+
+static int __sdhci_execute_tuning(struct sdhci *host, u32 opcode)
+{
+ int i;
+ int ret;
+
+ /*
+ * Issue opcode repeatedly till Execute Tuning is set to 0 or the number
+ * of loops reaches tuning loop count.
+ * Some controllers are known to always require 40 iterations.
+ */
+ for (i = 0; i < host->tuning_loop_count; i++) {
+ u16 ctrl;
+
+ ret = sdhci_send_tuning(host, opcode);
+ if (ret) {
+ sdhci_abort_tuning(host, opcode);
+ return -ETIMEDOUT;
+ }
+
+ /* Spec does not require a delay between tuning cycles */
+ if (host->tuning_delay > 0)
+ mdelay(host->tuning_delay);
+
+ ctrl = sdhci_read16(host, SDHCI_HOST_CONTROL2);
+ if (!(ctrl & SDHCI_CTRL_EXEC_TUNING)) {
+ if (ctrl & SDHCI_CTRL_TUNED_CLK) {
+ return 0; /* Success! */
+ }
+ break;
+ }
+
+ }
+
+ dev_dbg(&host->mci->mci->dev, "Tuning timeout, falling back to fixed sampling clock\n");
+ sdhci_reset_tuning(host);
+ return -EAGAIN;
+}
+
+int sdhci_execute_tuning(struct sdhci *sdhci, u32 opcode)
+{
+ struct mci_host *host = sdhci->mci;
+ int err = 0;
+ unsigned int tuning_count = 0;
+
+ if (sdhci->tuning_mode == SDHCI_TUNING_MODE_1)
+ tuning_count = sdhci->tuning_count;
+
+ /*
+ * The Host Controller needs tuning in case of SDR104 and DDR50
+ * mode, and for SDR50 mode when Use Tuning for SDR50 is set in
+ * the Capabilities register.
+ * If the Host Controller supports the HS200 mode then the
+ * tuning function has to be executed.
+ */
+ switch (host->timing) {
+ /* HS400 tuning is done in HS200 mode */
+ case MMC_TIMING_MMC_HS400:
+ err = -EINVAL;
+ goto out;
+
+ case MMC_TIMING_MMC_HS200:
+ break;
+
+ case MMC_TIMING_UHS_SDR104:
+ case MMC_TIMING_UHS_DDR50:
+ break;
+
+ case MMC_TIMING_UHS_SDR50:
+ fallthrough;
+
+ default:
+ goto out;
+ }
+
+ if (sdhci->platform_execute_tuning) {
+ err = sdhci->platform_execute_tuning(host, opcode);
+ goto out;
+ }
+
+ if (sdhci->tuning_delay < 0)
+ sdhci->tuning_delay = opcode == MMC_SEND_TUNING_BLOCK;
+
+ sdhci_start_tuning(sdhci);
+
+ sdhci->tuning_err = __sdhci_execute_tuning(sdhci, opcode);
+
+ sdhci_end_tuning(sdhci);
+out:
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(sdhci_execute_tuning);
+
void sdhci_read_response(struct sdhci *sdhci, struct mci_cmd *cmd)
{
if (cmd->resp_type & MMC_RSP_136) {
@@ -48,7 +271,12 @@ void sdhci_set_cmd_xfer_mode(struct sdhci *host, struct mci_cmd *cmd,
*command |= SDHCI_CMD_INDEX(cmd->cmdidx);
- if (data) {
+ if (cmd->cmdidx == MMC_SEND_TUNING_BLOCK ||
+ cmd->cmdidx == MMC_SEND_TUNING_BLOCK_HS200) {
+ *command |= SDHCI_DATA_PRESENT;
+ *xfer = SDHCI_DATA_TO_HOST;
+
+ } else if (data) {
*command |= SDHCI_DATA_PRESENT;
*xfer |= SDHCI_BLOCK_COUNT_EN;
@@ -88,6 +316,81 @@ static void sdhci_tx_pio(struct sdhci *sdhci, struct mci_data *data,
sdhci_write32(sdhci, SDHCI_BUFFER, buf[i]);
}
+void sdhci_set_bus_width(struct sdhci *host, int width)
+{
+ u8 ctrl;
+
+ BUG_ON(!host->mci); /* Call sdhci_setup_host() before using this */
+
+ ctrl = sdhci_read8(host, SDHCI_HOST_CONTROL);
+ if (width == MMC_BUS_WIDTH_8) {
+ ctrl &= ~SDHCI_CTRL_4BITBUS;
+ ctrl |= SDHCI_CTRL_8BITBUS;
+ } else {
+ if (host->mci->host_caps & MMC_CAP_8_BIT_DATA)
+ ctrl &= ~SDHCI_CTRL_8BITBUS;
+ if (width == MMC_BUS_WIDTH_4)
+ ctrl |= SDHCI_CTRL_4BITBUS;
+ else
+ ctrl &= ~SDHCI_CTRL_4BITBUS;
+ }
+ sdhci_write8(host, SDHCI_HOST_CONTROL, ctrl);
+}
+
+static void sdhci_set_uhs_signaling(struct sdhci *host, unsigned timing)
+{
+ u16 ctrl_2;
+
+ ctrl_2 = sdhci_read16(host, SDHCI_HOST_CONTROL2);
+ /* Select Bus Speed Mode for host */
+ ctrl_2 &= ~SDHCI_CTRL_UHS_MASK;
+ if ((timing == MMC_TIMING_MMC_HS200) ||
+ (timing == MMC_TIMING_UHS_SDR104))
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR104;
+ else if (timing == MMC_TIMING_UHS_SDR12)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR12;
+ else if (timing == MMC_TIMING_UHS_SDR25)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR25;
+ else if (timing == MMC_TIMING_UHS_SDR50)
+ ctrl_2 |= SDHCI_CTRL_UHS_SDR50;
+ else if ((timing == MMC_TIMING_UHS_DDR50) ||
+ (timing == MMC_TIMING_MMC_DDR52))
+ ctrl_2 |= SDHCI_CTRL_UHS_DDR50;
+ else if (timing == MMC_TIMING_MMC_HS400)
+ ctrl_2 |= SDHCI_CTRL_HS400; /* Non-standard */
+ sdhci_write16(host, SDHCI_HOST_CONTROL2, ctrl_2);
+}
+EXPORT_SYMBOL_GPL(sdhci_set_uhs_signaling);
+
+static inline bool sdhci_can_64bit_dma(struct sdhci *host)
+{
+ /*
+ * According to SD Host Controller spec v4.10, bit[27] added from
+ * version 4.10 in Capabilities Register is used as 64-bit System
+ * Address support for V4 mode.
+ */
+ if (host->version >= SDHCI_SPEC_410 && host->v4_mode)
+ return host->caps & SDHCI_CAN_64BIT_V4;
+
+ return host->caps & SDHCI_CAN_64BIT;
+}
+
+
+static void sdhci_set_adma_addr(struct sdhci *host, dma_addr_t addr)
+{
+ sdhci_write32(host, SDHCI_ADMA_ADDRESS, lower_32_bits(addr));
+ if (host->flags & SDHCI_USE_64_BIT_DMA)
+ sdhci_write32(host, SDHCI_ADMA_ADDRESS_HI, upper_32_bits(addr));
+}
+
+static void sdhci_set_sdma_addr(struct sdhci *host, dma_addr_t addr)
+{
+ if (host->v4_mode)
+ sdhci_set_adma_addr(host, addr);
+ else
+ sdhci_write32(host, SDHCI_DMA_ADDRESS, addr);
+}
+
#ifdef __PBL__
/*
* Stubs to make timeout logic below work in PBL
@@ -101,12 +404,183 @@ static void sdhci_tx_pio(struct sdhci *sdhci, struct mci_data *data,
#endif
-int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data)
+int sdhci_wait_for_done(struct sdhci *sdhci, u32 mask)
+{
+ u64 start = get_time_ns();
+ u32 stat;
+
+ do {
+ stat = sdhci_read32(sdhci, SDHCI_INT_STATUS);
+
+ if (stat & SDHCI_INT_TIMEOUT)
+ return -ETIMEDOUT;
+
+ if (stat & SDHCI_INT_ERROR) {
+ dev_err(sdhci->mci->hw_dev, "SDHCI_INT_ERROR: 0x%08x\n",
+ stat);
+ return -EPERM;
+ }
+
+ if (is_timeout(start, 1000 * MSECOND)) {
+ dev_err(sdhci->mci->hw_dev,
+ "SDHCI timeout while waiting for done\n");
+ return -ETIMEDOUT;
+ }
+ } while ((stat & mask) != mask);
+
+ return 0;
+}
+
+void sdhci_setup_data_pio(struct sdhci *sdhci, struct mci_data *data)
+{
+ if (!data)
+ return;
+
+ sdhci_write32(sdhci, SDHCI_BLOCK_SIZE, sdhci->sdma_boundary |
+ SDHCI_TRANSFER_BLOCK_SIZE(data->blocksize) | data->blocks << 16);
+}
+
+static void sdhci_config_dma(struct sdhci *host)
+{
+ u8 ctrl;
+ u16 ctrl2;
+
+ if (host->version < SDHCI_SPEC_200)
+ return;
+
+ ctrl = sdhci_read8(host, SDHCI_HOST_CONTROL);
+ /* Note if DMA Select is zero then SDMA is selected */
+ ctrl &= ~SDHCI_CTRL_DMA_MASK;
+ sdhci_write8(host, SDHCI_HOST_CONTROL, ctrl);
+
+ if (host->flags & SDHCI_USE_64_BIT_DMA) {
+ /*
+ * If v4 mode, all supported DMA can be 64-bit addressing if
+ * controller supports 64-bit system address, otherwise only
+ * ADMA can support 64-bit addressing.
+ */
+ if (host->v4_mode) {
+ ctrl2 = sdhci_read16(host, SDHCI_HOST_CONTROL2);
+ ctrl2 |= SDHCI_CTRL_64BIT_ADDR;
+ sdhci_write16(host, SDHCI_HOST_CONTROL2, ctrl2);
+ }
+ }
+}
+
+void sdhci_setup_data_dma(struct sdhci *sdhci, struct mci_data *data,
+ dma_addr_t *dma)
+{
+ struct device *dev = sdhci->mci->hw_dev;
+ int nbytes;
+
+ if (!data)
+ return;
+
+ sdhci_setup_data_pio(sdhci, data);
+
+ if (!dma)
+ return;
+
+ nbytes = data->blocks * data->blocksize;
+
+ if (data->flags & MMC_DATA_READ)
+ *dma = dma_map_single(dev, (void *)data->src, nbytes,
+ DMA_FROM_DEVICE);
+ else
+ *dma = dma_map_single(dev, data->dest, nbytes,
+ DMA_TO_DEVICE);
+
+ if (dma_mapping_error(dev, *dma)) {
+ *dma = SDHCI_NO_DMA;
+ return;
+ }
+
+ sdhci_config_dma(sdhci);
+ sdhci_set_sdma_addr(sdhci, *dma);
+}
+
+int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_data *data,
+ dma_addr_t dma)
+{
+ struct device *dev = sdhci->mci->hw_dev;
+ u64 start;
+ int nbytes;
+ u32 irqstat;
+ int ret;
+
+ if (!data)
+ return 0;
+
+ nbytes = data->blocks * data->blocksize;
+
+ start = get_time_ns();
+
+ do {
+ irqstat = sdhci_read32(sdhci, SDHCI_INT_STATUS);
+
+ if (irqstat & SDHCI_INT_DATA_END_BIT) {
+ ret = -EIO;
+ goto out;
+ }
+
+ if (irqstat & SDHCI_INT_DATA_CRC) {
+ ret = -EBADMSG;
+ goto out;
+ }
+
+ if (irqstat & SDHCI_INT_DATA_TIMEOUT) {
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+
+ /*
+ * We currently don't do anything fancy with DMA
+ * boundaries, but as we can't disable the feature
+ * we need to at least restart the transfer.
+ *
+ * According to the spec sdhci_readl(host, SDHCI_DMA_ADDRESS)
+ * should return a valid address to continue from, but as
+ * some controllers are faulty, don't trust them.
+ */
+ if (irqstat & SDHCI_INT_DMA) {
+ /*
+ * DMA engine has stopped on buffer boundary. Acknowledge
+ * the interrupt and kick the DMA engine again.
+ */
+ sdhci_write32(sdhci, SDHCI_INT_STATUS, SDHCI_INT_DMA);
+ sdhci_set_sdma_addr(sdhci, ALIGN(dma, SDHCI_DEFAULT_BOUNDARY_SIZE));
+ }
+
+ if (irqstat & SDHCI_INT_XFER_COMPLETE)
+ break;
+
+ if (is_timeout(start, 10 * SECOND)) {
+ dev_alert(dev, "DMA wait timed out. Resetting, but recovery unlikely\n");
+ sdhci_reset(sdhci, SDHCI_RESET_ALL);
+ ret = -ETIMEDOUT;
+ goto out;
+ }
+ } while (1);
+
+ ret = 0;
+out:
+ if (data->flags & MMC_DATA_READ)
+ dma_unmap_single(dev, dma, nbytes, DMA_FROM_DEVICE);
+ else
+ dma_unmap_single(dev, dma, nbytes, DMA_TO_DEVICE);
+
+ return ret;
+}
+
+int sdhci_transfer_data_pio(struct sdhci *sdhci, struct mci_data *data)
{
unsigned int block = 0;
u32 stat, prs;
uint64_t start = get_time_ns();
+ if (!data)
+ return 0;
+
do {
stat = sdhci_read32(sdhci, SDHCI_INT_STATUS);
if (stat & SDHCI_INT_ERROR)
@@ -138,3 +612,386 @@ int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data)
return 0;
}
+
+int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data, dma_addr_t dma)
+{
+ struct device *dev = sdhci->mci->hw_dev;
+
+ if (!data)
+ return 0;
+
+ if (dma_mapping_error(dev, dma))
+ return sdhci_transfer_data_pio(sdhci, data);
+ else
+ return sdhci_transfer_data_dma(sdhci, data, dma);
+}
+
+int sdhci_reset(struct sdhci *sdhci, u8 mask)
+{
+ u8 val;
+
+ sdhci_write8(sdhci, SDHCI_SOFTWARE_RESET, mask);
+
+ return sdhci_read8_poll_timeout(sdhci, SDHCI_SOFTWARE_RESET,
+ val, !(val & mask),
+ 100 * USEC_PER_MSEC);
+}
+
+static u16 sdhci_get_preset_value(struct sdhci *host)
+{
+ u16 preset = 0;
+
+ BUG_ON(!host->mci); /* Call sdhci_setup_host() before using this */
+
+ switch (host->timing) {
+ case MMC_TIMING_UHS_SDR12:
+ preset = sdhci_read16(host, SDHCI_PRESET_FOR_SDR12);
+ break;
+ case MMC_TIMING_UHS_SDR25:
+ preset = sdhci_read16(host, SDHCI_PRESET_FOR_SDR25);
+ break;
+ case MMC_TIMING_UHS_SDR50:
+ preset = sdhci_read16(host, SDHCI_PRESET_FOR_SDR50);
+ break;
+ case MMC_TIMING_UHS_SDR104:
+ case MMC_TIMING_MMC_HS200:
+ preset = sdhci_read16(host, SDHCI_PRESET_FOR_SDR104);
+ break;
+ case MMC_TIMING_UHS_DDR50:
+ case MMC_TIMING_MMC_DDR52:
+ preset = sdhci_read16(host, SDHCI_PRESET_FOR_DDR50);
+ break;
+ case MMC_TIMING_MMC_HS400:
+ preset = sdhci_read16(host, SDHCI_PRESET_FOR_HS400);
+ break;
+ default:
+ dev_warn(host->mci->hw_dev, "Invalid UHS-I mode selected\n");
+ preset = sdhci_read16(host, SDHCI_PRESET_FOR_SDR12);
+ break;
+ }
+ return preset;
+}
+
+u16 sdhci_calc_clk(struct sdhci *host, unsigned int clock,
+ unsigned int *actual_clock, unsigned int input_clock)
+{
+ int div = 0; /* Initialized for compiler warning */
+ int real_div = div, clk_mul = 1;
+ u16 clk = 0;
+ bool switch_base_clk = false;
+
+ BUG_ON(!host->mci); /* Call sdhci_setup_host() before using this */
+
+ if (host->version >= SDHCI_SPEC_300) {
+ if (host->preset_enabled) {
+ u16 pre_val;
+
+ clk = sdhci_read16(host, SDHCI_CLOCK_CONTROL);
+ pre_val = sdhci_get_preset_value(host);
+ div = FIELD_GET(SDHCI_PRESET_SDCLK_FREQ_MASK, pre_val);
+ if (host->clk_mul &&
+ (pre_val & SDHCI_PRESET_CLKGEN_SEL)) {
+ clk = SDHCI_PROG_CLOCK_MODE;
+ real_div = div + 1;
+ clk_mul = host->clk_mul;
+ } else {
+ real_div = max_t(int, 1, div << 1);
+ }
+ goto clock_set;
+ }
+
+ /*
+ * Check if the Host Controller supports Programmable Clock
+ * Mode.
+ */
+ if (host->clk_mul) {
+ for (div = 1; div <= 1024; div++) {
+ if ((input_clock * host->clk_mul / div)
+ <= clock)
+ break;
+ }
+ if ((input_clock * host->clk_mul / div) <= clock) {
+ /*
+ * Set Programmable Clock Mode in the Clock
+ * Control register.
+ */
+ clk = SDHCI_PROG_CLOCK_MODE;
+ real_div = div;
+ clk_mul = host->clk_mul;
+ div--;
+ } else {
+ /*
+ * Divisor can be too small to reach clock
+ * speed requirement. Then use the base clock.
+ */
+ switch_base_clk = true;
+ }
+ }
+
+ if (!host->clk_mul || switch_base_clk) {
+ /* Version 3.00 divisors must be a multiple of 2. */
+ if (input_clock <= clock)
+ div = 1;
+ else {
+ for (div = 2; div < SDHCI_MAX_DIV_SPEC_300;
+ div += 2) {
+ if ((input_clock / div) <= clock)
+ break;
+ }
+ }
+ real_div = div;
+ div >>= 1;
+ if ((host->quirks2 & SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN)
+ && !div && input_clock <= 25000000)
+ div = 1;
+ }
+ } else {
+ /* Version 2.00 divisors must be a power of 2. */
+ for (div = 1; div < SDHCI_MAX_DIV_SPEC_200; div *= 2) {
+ if ((input_clock / div) <= clock)
+ break;
+ }
+ real_div = div;
+ div >>= 1;
+ }
+
+clock_set:
+ if (real_div)
+ *actual_clock = (input_clock * clk_mul) / real_div;
+ clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT;
+ clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN)
+ << SDHCI_DIVIDER_HI_SHIFT;
+
+ return clk;
+}
+
+void sdhci_enable_clk(struct sdhci *host, u16 clk)
+{
+ u64 start;
+
+ BUG_ON(!host->mci); /* Call sdhci_setup_host() before using this */
+
+ clk |= SDHCI_CLOCK_INT_EN;
+ sdhci_write16(host, SDHCI_CLOCK_CONTROL, clk);
+
+ start = get_time_ns();
+ while (!(sdhci_read16(host, SDHCI_CLOCK_CONTROL) &
+ SDHCI_CLOCK_INT_STABLE)) {
+ if (is_timeout(start, 150 * MSECOND)) {
+ dev_err(host->mci->hw_dev,
+ "SDHCI clock stable timeout\n");
+ return;
+ }
+ }
+
+ clk |= SDHCI_CLOCK_CARD_EN;
+ sdhci_write16(host, SDHCI_CLOCK_CONTROL, clk);
+}
+
+int sdhci_wait_idle(struct sdhci *host, struct mci_cmd *cmd, struct mci_data *data)
+{
+ u32 mask;
+ int ret;
+
+ mask = SDHCI_CMD_INHIBIT_CMD;
+
+ if (data || (cmd && (cmd->resp_type & MMC_RSP_BUSY)))
+ mask |= SDHCI_CMD_INHIBIT_DATA;
+
+ if (cmd && cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
+ mask &= ~SDHCI_CMD_INHIBIT_DATA;
+
+ ret = wait_on_timeout(10 * MSECOND,
+ !(sdhci_read32(host, SDHCI_PRESENT_STATE) & mask));
+
+ if (ret) {
+ dev_err(host->mci->hw_dev,
+ "SDHCI timeout while waiting for idle\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+int sdhci_wait_idle_data(struct sdhci *host, struct mci_cmd *cmd)
+{
+ u32 mask;
+ int ret;
+
+ mask = SDHCI_CMD_INHIBIT_CMD | SDHCI_CMD_INHIBIT_DATA;
+
+ if (cmd && cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
+ mask &= ~SDHCI_CMD_INHIBIT_DATA;
+
+ ret = wait_on_timeout(10 * MSECOND,
+ !(sdhci_read32(host, SDHCI_PRESENT_STATE) & mask));
+
+ if (ret) {
+ dev_err(host->mci->hw_dev,
+ "SDHCI timeout while waiting for idle\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+void sdhci_set_clock(struct sdhci *host, unsigned int clock, unsigned int input_clock)
+{
+ u16 clk;
+
+ BUG_ON(!host->mci); /* Call sdhci_setup_host() before using this */
+
+ host->mci->clock = 0;
+
+ sdhci_set_uhs_signaling(host, host->mci->timing);
+
+ sdhci_wait_idle_data(host, NULL);
+
+ sdhci_write16(host, SDHCI_CLOCK_CONTROL, 0);
+
+ if (clock == 0)
+ return;
+
+ clk = sdhci_calc_clk(host, clock, &host->mci->clock, input_clock);
+ sdhci_enable_clk(host, clk);
+}
+
+static void sdhci_do_enable_v4_mode(struct sdhci *host)
+{
+ u16 ctrl2;
+
+ ctrl2 = sdhci_read16(host, SDHCI_HOST_CONTROL2);
+ if (ctrl2 & SDHCI_CTRL_V4_MODE)
+ return;
+
+ ctrl2 |= SDHCI_CTRL_V4_MODE;
+ sdhci_write16(host, SDHCI_HOST_CONTROL2, ctrl2);
+}
+
+void sdhci_enable_v4_mode(struct sdhci *host)
+{
+ host->v4_mode = true;
+ sdhci_do_enable_v4_mode(host);
+}
+
+void __sdhci_read_caps(struct sdhci *host, const u16 *ver,
+ const u32 *caps, const u32 *caps1)
+{
+ u16 v;
+ u64 dt_caps_mask = 0;
+ u64 dt_caps = 0;
+ struct device_node *np = host->mci->hw_dev->of_node;
+
+ BUG_ON(!host->mci); /* Call sdhci_setup_host() before using this */
+
+ if (host->read_caps)
+ return;
+
+ host->read_caps = true;
+
+ sdhci_reset(host, SDHCI_RESET_ALL);
+
+ if (host->v4_mode)
+ sdhci_do_enable_v4_mode(host);
+
+ of_property_read_u64(np, "sdhci-caps-mask", &dt_caps_mask);
+ of_property_read_u64(np, "sdhci-caps", &dt_caps);
+
+ v = ver ? *ver : sdhci_read16(host, SDHCI_HOST_VERSION);
+ host->version = (v & SDHCI_SPEC_VER_MASK) >> SDHCI_SPEC_VER_SHIFT;
+
+ if (host->quirks & SDHCI_QUIRK_MISSING_CAPS)
+ return;
+
+ if (caps) {
+ host->caps = *caps;
+ } else {
+ host->caps = sdhci_read32(host, SDHCI_CAPABILITIES);
+ host->caps &= ~lower_32_bits(dt_caps_mask);
+ host->caps |= lower_32_bits(dt_caps);
+ }
+
+ if (host->version < SDHCI_SPEC_300)
+ return;
+
+ if (caps1) {
+ host->caps1 = *caps1;
+ } else {
+ host->caps1 = sdhci_read32(host, SDHCI_CAPABILITIES_1);
+ host->caps1 &= ~upper_32_bits(dt_caps_mask);
+ host->caps1 |= upper_32_bits(dt_caps);
+ }
+}
+
+int sdhci_setup_host(struct sdhci *host)
+{
+ struct mci_host *mci = host->mci;
+
+ BUG_ON(!mci);
+
+ sdhci_read_caps(host);
+
+ if (!host->max_clk) {
+ if (host->version >= SDHCI_SPEC_300)
+ host->max_clk = FIELD_GET(SDHCI_CLOCK_V3_BASE_MASK, host->caps);
+ else
+ host->max_clk = FIELD_GET(SDHCI_CLOCK_BASE_MASK, host->caps);
+
+ host->max_clk *= 1000000;
+ }
+
+ /*
+ * In case of Host Controller v3.00, find out whether clock
+ * multiplier is supported.
+ */
+ host->clk_mul = FIELD_GET(SDHCI_CLOCK_MUL_MASK, host->caps1);
+
+ /*
+ * In case the value in Clock Multiplier is 0, then programmable
+ * clock mode is not supported, otherwise the actual clock
+ * multiplier is one more than the value of Clock Multiplier
+ * in the Capabilities Register.
+ */
+ if (host->clk_mul)
+ host->clk_mul += 1;
+
+ if (host->caps & SDHCI_CAN_VDD_180)
+ mci->voltages |= MMC_VDD_165_195;
+ if (host->caps & SDHCI_CAN_VDD_300)
+ mci->voltages |= MMC_VDD_29_30 | MMC_VDD_30_31;
+ if (host->caps & SDHCI_CAN_VDD_330)
+ mci->voltages |= MMC_VDD_32_33 | MMC_VDD_33_34;
+
+ if (host->caps & SDHCI_CAN_DO_HISPD)
+ mci->host_caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
+
+ if (host->caps & SDHCI_CAN_DO_8BIT)
+ mci->host_caps |= MMC_CAP_8_BIT_DATA;
+
+ host->sdma_boundary = SDHCI_DEFAULT_BOUNDARY_ARG;
+
+ if (sdhci_can_64bit_dma(host))
+ host->flags |= SDHCI_USE_64_BIT_DMA;
+
+ if ((mci->caps2 & (MMC_CAP2_HS200_1_8V_SDR | MMC_CAP2_HS400_1_8V)))
+ host->flags |= SDHCI_SIGNALING_180;
+
+ host->tuning_delay = -1;
+ host->tuning_loop_count = MAX_TUNING_LOOP;
+
+ /* Initial value for re-tuning timer count */
+ host->tuning_count = FIELD_GET(SDHCI_RETUNING_TIMER_COUNT_MASK,
+ host->caps1);
+
+ /*
+ * In case Re-tuning Timer is not disabled, the actual value of
+ * re-tuning timer will be 2 ^ (n - 1).
+ */
+ if (host->tuning_count)
+ host->tuning_count = 1 << (host->tuning_count - 1);
+
+ /* Re-tuning mode supported by the Host Controller */
+ host->tuning_mode = FIELD_GET(SDHCI_RETUNING_MODE_MASK, host->caps1);
+
+ return 0;
+}
diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h
index a307dc97cd..5de85239b1 100644
--- a/drivers/mci/sdhci.h
+++ b/drivers/mci/sdhci.h
@@ -1,6 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __MCI_SDHCI_H
#define __MCI_SDHCI_H
+#include <pbl.h>
+#include <dma.h>
+#include <linux/iopoll.h>
+#include <linux/sizes.h>
+
#define SDHCI_DMA_ADDRESS 0x00
#define SDHCI_BLOCK_SIZE__BLOCK_COUNT 0x04
#define SDHCI_BLOCK_SIZE 0x04
@@ -13,6 +19,8 @@
#define SDHCI_DMA_BOUNDARY_8K SDHCI_DMA_BOUNDARY(1)
#define SDHCI_DMA_BOUNDARY_4K SDHCI_DMA_BOUNDARY(0)
#define SDHCI_DMA_BOUNDARY(x) (((x) & 0x7) << 12)
+#define SDHCI_DEFAULT_BOUNDARY_SIZE SZ_512K
+#define SDHCI_DEFAULT_BOUNDARY_ARG SDHCI_DMA_BOUNDARY_512K
#define SDHCI_TRANSFER_BLOCK_SIZE(x) ((x) & 0xfff)
#define SDHCI_BLOCK_COUNT 0x06
#define SDHCI_ARGUMENT 0x08
@@ -20,9 +28,11 @@
#define SDHCI_TRANSFER_MODE 0x0c
#define SDHCI_MULTIPLE_BLOCKS BIT(5)
#define SDHCI_DATA_TO_HOST BIT(4)
+#define SDHCI_TRNS_AUTO_CMD12 BIT(3)
#define SDHCI_BLOCK_COUNT_EN BIT(1)
#define SDHCI_DMA_EN BIT(0)
#define SDHCI_COMMAND 0x0e
+#define SDHCI_MAKE_CMD(c, f) (((c & 0xff) << 8) | (f & 0xff))
#define SDHCI_CMD_INDEX(c) (((c) & 0x3f) << 8)
#define SDHCI_COMMAND_CMDTYP_SUSPEND (1 << 6)
#define SDHCI_COMMAND_CMDTYP_RESUME (2 << 6)
@@ -42,6 +52,7 @@
#define SDHCI_PRESENT_STATE 0x24
#define SDHCI_WRITE_PROTECT BIT(19)
#define SDHCI_CARD_DETECT BIT(18)
+#define SDHCI_CARD_PRESENT BIT(16)
#define SDHCI_BUFFER_READ_ENABLE BIT(11)
#define SDHCI_BUFFER_WRITE_ENABLE BIT(10)
#define SDHCI_DATA_LINE_ACTIVE BIT(2)
@@ -50,24 +61,45 @@
#define SDHCI_PRESENT_STATE1 0x26
#define SDHCI_HOST_CONTROL__POWER_CONTROL__BLOCK_GAP_CONTROL 0x28
#define SDHCI_HOST_CONTROL 0x28
-#define SDHCI_CARD_DETECT_SIGNAL_SELECTION BIT(7)
-#define SDHCI_CARD_DETECT_TEST_LEVEL BIT(6)
-#define SDHCI_DATA_WIDTH_8BIT BIT(5)
-#define SDHCI_HIGHSPEED_EN BIT(2)
-#define SDHCI_DATA_WIDTH_4BIT BIT(1)
+#define SDHCI_CTRL_LED BIT(0)
+#define SDHCI_CTRL_4BITBUS BIT(1)
+#define SDHCI_CTRL_HISPD BIT(2)
+#define SDHCI_CTRL_DMA_MASK 0x18
+#define SDHCI_CTRL_SDMA 0x00
+#define SDHCI_CTRL_ADMA1 0x08
+#define SDHCI_CTRL_ADMA32 0x10
+#define SDHCI_CTRL_ADMA64 0x18
+#define SDHCI_CTRL_ADMA3 0x18
+#define SDHCI_CTRL_8BITBUS BIT(5)
+#define SDHCI_CTRL_CDTEST_INS BIT(6)
+#define SDHCI_CTRL_CDTEST_EN BIT(7)
#define SDHCI_POWER_CONTROL 0x29
+#define SDHCI_POWER_ON 0x01
+#define SDHCI_POWER_180 0x0A
+#define SDHCI_POWER_300 0x0C
+#define SDHCI_POWER_330 0x0E
#define SDHCI_BUS_VOLTAGE_330 SDHCI_BUS_VOLTAGE(7)
#define SDHCI_BUS_VOLTAGE(v) ((v) << 1)
#define SDHCI_BUS_POWER_EN BIT(0)
#define SDHCI_CLOCK_CONTROL__TIMEOUT_CONTROL__SOFTWARE_RESET 0x2c
-#define SDHCI_CLOCK_CONTROL 0x2c
-#define SDHCI_FREQ_SEL(x) (((x) & 0xff) << 8)
-#define SDHCI_SDCLOCK_EN BIT(2)
-#define SDHCI_INTCLOCK_STABLE BIT(1)
-#define SDHCI_INTCLOCK_EN BIT(0)
+#define SDHCI_CLOCK_CONTROL 0x2C
+#define SDHCI_DIVIDER_SHIFT 8
+#define SDHCI_DIVIDER_HI_SHIFT 6
+#define SDHCI_DIV_MASK 0xFF
+#define SDHCI_DIV_HI_MASK 0x300
+#define SDHCI_DIV_MASK_LEN 8
+#define SDHCI_FREQ_SEL(x) (((x) & 0xff) << 8)
+#define SDHCI_DIV_HI_MASK 0x300
+#define SDHCI_PROG_CLOCK_MODE BIT(5)
+#define SDHCI_CLOCK_CARD_EN BIT(2)
+#define SDHCI_CLOCK_PLL_EN BIT(3)
+#define SDHCI_CLOCK_INT_STABLE BIT(1)
+#define SDHCI_CLOCK_INT_EN BIT(0)
#define SDHCI_TIMEOUT_CONTROL 0x2e
#define SDHCI_SOFTWARE_RESET 0x2f
#define SDHCI_RESET_ALL BIT(0)
+#define SDHCI_RESET_CMD BIT(1)
+#define SDHCI_RESET_DATA BIT(2)
#define SDHCI_INT_STATUS 0x30
#define SDHCI_INT_NORMAL_STATUS 0x30
#define SDHCI_INT_DATA_END_BIT BIT(22)
@@ -79,6 +111,7 @@
#define SDHCI_INT_TIMEOUT BIT(16)
#define SDHCI_INT_ERROR BIT(15)
#define SDHCI_INT_CARD_INT BIT(8)
+#define SDHCI_INT_CARD_INSERT BIT(6)
#define SDHCI_INT_DATA_AVAIL BIT(5)
#define SDHCI_INT_SPACE_AVAIL BIT(4)
#define SDHCI_INT_DMA BIT(3)
@@ -86,19 +119,87 @@
#define SDHCI_INT_CMD_COMPLETE BIT(0)
#define SDHCI_INT_ERROR_STATUS 0x32
#define SDHCI_INT_ENABLE 0x34
+#define SDHCI_INT_ERROR_ENABLE 0x36
#define SDHCI_SIGNAL_ENABLE 0x38
#define SDHCI_ACMD12_ERR__HOST_CONTROL2 0x3C
+#define SDHCI_HOST_CONTROL2 0x3E
+#define SDHCI_CTRL_UHS_MASK GENMASK(3, 0)
+#define SDHCI_CTRL_UHS_SDR12 0x0
+#define SDHCI_CTRL_UHS_SDR25 0x1
+#define SDHCI_CTRL_UHS_SDR50 0x2
+#define SDHCI_CTRL_UHS_SDR104 0x3
+#define SDHCI_CTRL_UHS_DDR50 0x4
+#define SDHCI_CTRL_HS400 0x5 /* Non-standard */
+#define SDHCI_CTRL_EXEC_TUNING BIT(6)
+#define SDHCI_CTRL_TUNED_CLK BIT(7)
+#define SDHCI_CTRL_64BIT_ADDR BIT(13)
+#define SDHCI_CTRL_V4_MODE BIT(12)
#define SDHCI_CAPABILITIES 0x40
-#define SDHCI_CAPABILITIES_1 0x42
-#define SDHCI_HOSTCAP_VOLTAGE_180 BIT(10)
-#define SDHCI_HOSTCAP_VOLTAGE_300 BIT(9)
-#define SDHCI_HOSTCAP_VOLTAGE_330 BIT(8)
-#define SDHCI_HOSTCAP_HIGHSPEED BIT(5)
-#define SDHCI_HOSTCAP_8BIT BIT(2)
-
-#define SDHCI_SPEC_200_MAX_CLK_DIVIDER 256
+#define SDHCI_TIMEOUT_CLK_MASK GENMASK(5, 0)
+#define SDHCI_TIMEOUT_CLK_UNIT 0x00000080
+#define SDHCI_CLOCK_BASE_MASK GENMASK(13, 8)
+#define SDHCI_CLOCK_V3_BASE_MASK GENMASK(15, 8)
+#define SDHCI_MAX_BLOCK_MASK 0x00030000
+#define SDHCI_MAX_BLOCK_SHIFT 16
+#define SDHCI_CAN_DO_8BIT 0x00040000
+#define SDHCI_CAN_DO_ADMA2 0x00080000
+#define SDHCI_CAN_DO_ADMA1 0x00100000
+#define SDHCI_CAN_DO_HISPD 0x00200000
+#define SDHCI_CAN_DO_SDMA 0x00400000
+#define SDHCI_CAN_DO_SUSPEND 0x00800000
+#define SDHCI_CAN_VDD_330 0x01000000
+#define SDHCI_CAN_VDD_300 0x02000000
+#define SDHCI_CAN_VDD_180 0x04000000
+#define SDHCI_CAN_64BIT_V4 0x08000000
+#define SDHCI_CAN_64BIT 0x10000000
+
+#define SDHCI_CAPABILITIES_1 0x44
+#define SDHCI_SUPPORT_SDR50 0x00000001
+#define SDHCI_SUPPORT_SDR104 0x00000002
+#define SDHCI_SUPPORT_DDR50 0x00000004
+#define SDHCI_DRIVER_TYPE_A 0x00000010
+#define SDHCI_DRIVER_TYPE_C 0x00000020
+#define SDHCI_DRIVER_TYPE_D 0x00000040
+#define SDHCI_RETUNING_TIMER_COUNT_MASK GENMASK(11, 8)
+#define SDHCI_USE_SDR50_TUNING 0x00002000
+#define SDHCI_RETUNING_MODE_MASK GENMASK(15, 14)
+#define SDHCI_CLOCK_MUL_MASK GENMASK(23, 16)
+#define SDHCI_CAN_DO_ADMA3 0x08000000
+#define SDHCI_SUPPORT_HS400 0x80000000 /* Non-standard */
+
+#define SDHCI_PRESET_FOR_SDR12 0x66
+#define SDHCI_PRESET_FOR_SDR25 0x68
+#define SDHCI_PRESET_FOR_SDR50 0x6A
+#define SDHCI_PRESET_FOR_SDR104 0x6C
+#define SDHCI_PRESET_FOR_DDR50 0x6E
+#define SDHCI_PRESET_FOR_HS400 0x74 /* Non-standard */
+#define SDHCI_PRESET_CLKGEN_SEL BIT(10)
+#define SDHCI_PRESET_SDCLK_FREQ_MASK GENMASK(9, 0)
+
+#define SDHCI_P_VENDOR_SPEC_AREA 0xE8
+#define SDHCI_P_VENDOR_SPEC_AREA_MASK GENMASK(11, 0)
+#define SDHCI_HOST_VERSION 0xFE
+#define SDHCI_VENDOR_VER_MASK 0xFF00
+#define SDHCI_VENDOR_VER_SHIFT 8
+#define SDHCI_SPEC_VER_MASK 0x00FF
+#define SDHCI_SPEC_VER_SHIFT 0
+#define SDHCI_SPEC_100 0
+#define SDHCI_SPEC_200 1
+#define SDHCI_SPEC_300 2
+#define SDHCI_SPEC_400 3
+#define SDHCI_SPEC_410 4
+#define SDHCI_SPEC_420 5
+
+#define SDHCI_CLOCK_MUL_SHIFT 16
+
+#define SDHCI_ADMA_ADDRESS 0x58
+#define SDHCI_ADMA_ADDRESS_HI 0x5c
+
#define SDHCI_MMC_BOOT 0xC4
+#define SDHCI_MAX_DIV_SPEC_200 256
+#define SDHCI_MAX_DIV_SPEC_300 2046
+
struct sdhci {
u32 (*read32)(struct sdhci *host, int reg);
u16 (*read16)(struct sdhci *host, int reg);
@@ -106,42 +207,145 @@ struct sdhci {
void (*write32)(struct sdhci *host, int reg, u32 val);
void (*write16)(struct sdhci *host, int reg, u16 val);
void (*write8)(struct sdhci *host, int reg, u8 val);
+
+ void __iomem *base;
+
+ int max_clk; /* Max possible freq (Hz) */
+ int clk_mul; /* Clock Muliplier value */
+
+ int flags; /* Host attributes */
+#define SDHCI_USE_SDMA (1<<0) /* Host is SDMA capable */
+#define SDHCI_USE_ADMA (1<<1) /* Host is ADMA capable */
+#define SDHCI_REQ_USE_DMA (1<<2) /* Use DMA for this req. */
+#define SDHCI_DEVICE_DEAD (1<<3) /* Device unresponsive */
+#define SDHCI_SDR50_NEEDS_TUNING (1<<4) /* SDR50 needs tuning */
+#define SDHCI_AUTO_CMD12 (1<<6) /* Auto CMD12 support */
+#define SDHCI_AUTO_CMD23 (1<<7) /* Auto CMD23 support */
+#define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */
+#define SDHCI_USE_64_BIT_DMA (1<<12) /* Use 64-bit DMA */
+#define SDHCI_HS400_TUNING (1<<13) /* Tuning for HS400 */
+#define SDHCI_SIGNALING_330 (1<<14) /* Host is capable of 3.3V signaling */
+#define SDHCI_SIGNALING_180 (1<<15) /* Host is capable of 1.8V signaling */
+#define SDHCI_SIGNALING_120 (1<<16) /* Host is capable of 1.2V signaling */
+
+ unsigned int version; /* SDHCI spec. version */
+
+ enum mci_timing timing;
+ bool preset_enabled; /* Preset is enabled */
+ bool v4_mode; /* Host Version 4 Enable */
+
+ unsigned int quirks;
+#define SDHCI_QUIRK_MISSING_CAPS BIT(27)
+ unsigned int quirks2;
+#define SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN BIT(15)
+#define SDHCI_QUIRK2_ISSUE_CMD_DAT_RESET_TOGETHER BIT(19)
+ u32 caps; /* CAPABILITY_0 */
+ u32 caps1; /* CAPABILITY_1 */
+ bool read_caps; /* Capability flags have been read */
+ u32 sdma_boundary;
+
+ unsigned int tuning_count; /* Timer count for re-tuning */
+ unsigned int tuning_mode; /* Re-tuning mode supported by host */
+ unsigned int tuning_err; /* Error code for re-tuning */
+#define SDHCI_TUNING_MODE_1 0
+#define SDHCI_TUNING_MODE_2 1
+#define SDHCI_TUNING_MODE_3 2
+ /* Delay (ms) between tuning commands */
+ int tuning_delay;
+ int tuning_loop_count;
+ int tuning_old_ier;
+ int tuning_old_sig;
+
+ struct mci_host *mci;
+
+ int (*platform_execute_tuning)(struct mci_host *host, u32 opcode);
};
static inline u32 sdhci_read32(struct sdhci *host, int reg)
{
- return host->read32(host, reg);
+ if (host->read32)
+ return host->read32(host, reg);
+ else
+ return readl(host->base + reg);
}
static inline u32 sdhci_read16(struct sdhci *host, int reg)
{
- return host->read16(host, reg);
+ if (host->read16)
+ return host->read16(host, reg);
+ else
+ return readw(host->base + reg);
}
static inline u32 sdhci_read8(struct sdhci *host, int reg)
{
- return host->read8(host, reg);
+ if (host->read8)
+ return host->read8(host, reg);
+ else
+ return readb(host->base + reg);
}
static inline void sdhci_write32(struct sdhci *host, int reg, u32 val)
{
- host->write32(host, reg, val);
+ if (host->write32)
+ host->write32(host, reg, val);
+ else
+ writel(val, host->base + reg);
}
static inline void sdhci_write16(struct sdhci *host, int reg, u32 val)
{
- host->write16(host, reg, val);
+ if (host->write16)
+ host->write16(host, reg, val);
+ else
+ writew(val, host->base + reg);
}
static inline void sdhci_write8(struct sdhci *host, int reg, u32 val)
{
- host->write8(host, reg, val);
+ if (host->write8)
+ host->write8(host, reg, val);
+ else
+ writeb(val, host->base + reg);
}
+#define SDHCI_NO_DMA DMA_ERROR_CODE
+int sdhci_execute_tuning(struct sdhci *sdhci, u32 opcode);
+int sdhci_wait_idle_data(struct sdhci *host, struct mci_cmd *cmd);
+int sdhci_wait_idle(struct sdhci *host, struct mci_cmd *cmd, struct mci_data *data);
+int sdhci_wait_for_done(struct sdhci *host, u32 mask);
void sdhci_read_response(struct sdhci *host, struct mci_cmd *cmd);
void sdhci_set_cmd_xfer_mode(struct sdhci *host, struct mci_cmd *cmd,
struct mci_data *data, bool dma, u32 *command,
u32 *xfer);
-int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data);
+void sdhci_setup_data_pio(struct sdhci *sdhci, struct mci_data *data);
+void sdhci_setup_data_dma(struct sdhci *sdhci, struct mci_data *data, dma_addr_t *dma);
+int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data, dma_addr_t dma);
+int sdhci_transfer_data_pio(struct sdhci *sdhci, struct mci_data *data);
+int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_data *data,
+ dma_addr_t dma);
+int sdhci_reset(struct sdhci *sdhci, u8 mask);
+u16 sdhci_calc_clk(struct sdhci *host, unsigned int clock,
+ unsigned int *actual_clock, unsigned int input_clock);
+void sdhci_set_clock(struct sdhci *host, unsigned int clock, unsigned int input_clock);
+void sdhci_enable_clk(struct sdhci *host, u16 clk);
+void sdhci_enable_v4_mode(struct sdhci *host);
+int sdhci_setup_host(struct sdhci *host);
+void __sdhci_read_caps(struct sdhci *host, const u16 *ver,
+ const u32 *caps, const u32 *caps1);
+static inline void sdhci_read_caps(struct sdhci *host)
+{
+ __sdhci_read_caps(host, NULL, NULL, NULL);
+}
+void sdhci_set_bus_width(struct sdhci *host, int width);
+
+#define sdhci_read8_poll_timeout(sdhci, reg, val, cond, timeout_us) \
+ read_poll_timeout(sdhci_read8, val, cond, timeout_us, sdhci, reg)
+
+#define sdhci_read16_poll_timeout(sdhci, reg, val, cond, timeout_us) \
+ read_poll_timeout(sdhci_read16, val, cond, timeout_us, sdhci, reg)
+
+#define sdhci_read32_poll_timeout(sdhci, reg, val, cond, timeout_us) \
+ read_poll_timeout(sdhci_read32, val, cond, timeout_us, sdhci, reg)
#endif /* __MCI_SDHCI_H */
diff --git a/drivers/mci/stm32_sdmmc2.c b/drivers/mci/stm32_sdmmc2.c
index 0c26869b03..418213a1b3 100644
--- a/drivers/mci/stm32_sdmmc2.c
+++ b/drivers/mci/stm32_sdmmc2.c
@@ -176,7 +176,7 @@
struct stm32_sdmmc2_priv {
void __iomem *base;
struct mci_host mci;
- struct device_d *dev;
+ struct device *dev;
struct clk *clk;
struct reset_control *reset_ctl;
u32 clk_reg_msk;
@@ -185,7 +185,7 @@ struct stm32_sdmmc2_priv {
#define to_mci_host(mci) container_of(mci, struct stm32_sdmmc2_priv, mci)
-static int stm32_sdmmc2_reset(struct mci_host *mci, struct device_d *mci_dev)
+static int stm32_sdmmc2_reset(struct mci_host *mci, struct device *mci_dev)
{
struct stm32_sdmmc2_priv *priv = to_mci_host(mci);
@@ -257,11 +257,12 @@ static void stm32_sdmmc2_pwron(struct stm32_sdmmc2_priv *priv)
udelay(DIV_ROUND_UP(74 * USEC_PER_SEC, priv->mci.clock));
}
-static void stm32_sdmmc2_start_data(struct stm32_sdmmc2_priv *priv,
- struct mci_data *data, u32 data_length)
+static dma_addr_t stm32_sdmmc2_start_data(struct stm32_sdmmc2_priv *priv,
+ struct mci_data *data, u32 data_length)
{
unsigned int num_bytes = data->blocks * data->blocksize;
- u32 data_ctrl, idmabase0;
+ dma_addr_t idmabase0;
+ u32 data_ctrl;
/* Configure the SDMMC DPSM (Data Path State Machine) */
data_ctrl = (__ilog2_u32(data->blocksize) <<
@@ -270,27 +271,27 @@ static void stm32_sdmmc2_start_data(struct stm32_sdmmc2_priv *priv,
if (data->flags & MMC_DATA_READ) {
data_ctrl |= SDMMC_DCTRL_DTDIR;
- idmabase0 = (u32)data->dest;
+ idmabase0 = dma_map_single(priv->dev, (void *)data->src, num_bytes,
+ DMA_FROM_DEVICE);
} else {
- idmabase0 = (u32)data->src;
+ idmabase0 = dma_map_single(priv->dev, (void *)data->src, num_bytes,
+ DMA_TO_DEVICE);
}
+ if (dma_mapping_error(priv->dev, idmabase0))
+ return DMA_ERROR_CODE;
+
/* Set the SDMMC DataLength value */
writel(data_length, priv->base + SDMMC_DLEN);
/* Write to SDMMC DCTRL */
writel(data_ctrl, priv->base + SDMMC_DCTRL);
- if (data->flags & MMC_DATA_WRITE)
- dma_sync_single_for_device((unsigned long)idmabase0,
- num_bytes, DMA_TO_DEVICE);
- else
- dma_sync_single_for_device((unsigned long)idmabase0,
- num_bytes, DMA_FROM_DEVICE);
-
/* Enable internal DMA */
writel(idmabase0, priv->base + SDMMC_IDMABASE0);
writel(SDMMC_IDMACTRL_IDMAEN, priv->base + SDMMC_IDMACTRL);
+
+ return idmabase0;
}
static void stm32_sdmmc2_start_cmd(struct stm32_sdmmc2_priv *priv,
@@ -367,7 +368,7 @@ static int stm32_sdmmc2_end_cmd(struct stm32_sdmmc2_priv *priv,
/* Check status */
if (status & SDMMC_STA_CTIMEOUT) {
- dev_err(priv->dev, "%s: error SDMMC_STA_CTIMEOUT (0x%x) for cmd %d\n",
+ dev_dbg(priv->dev, "%s: error SDMMC_STA_CTIMEOUT (0x%x) for cmd %d\n",
__func__, status, cmd->cmdidx);
return -ETIMEDOUT;
}
@@ -415,7 +416,8 @@ static int stm32_sdmmc2_end_cmd(struct stm32_sdmmc2_priv *priv,
static int stm32_sdmmc2_end_data(struct stm32_sdmmc2_priv *priv,
struct mci_cmd *cmd,
- struct mci_data *data)
+ struct mci_data *data,
+ dma_addr_t dma_addr)
{
u32 mask = SDMMC_STA_DCRCFAIL | SDMMC_STA_DTIMEOUT |
SDMMC_STA_IDMATE | SDMMC_STA_DATAEND;
@@ -436,12 +438,10 @@ static int stm32_sdmmc2_end_data(struct stm32_sdmmc2_priv *priv,
return ret;
}
- if (data->flags & MMC_DATA_WRITE)
- dma_sync_single_for_cpu((unsigned long)data->src,
- num_bytes, DMA_TO_DEVICE);
+ if (data->flags & MMC_DATA_READ)
+ dma_unmap_single(priv->dev, dma_addr, num_bytes, DMA_FROM_DEVICE);
else
- dma_sync_single_for_cpu((unsigned long)data->dest,
- num_bytes, DMA_FROM_DEVICE);
+ dma_unmap_single(priv->dev, dma_addr, num_bytes, DMA_TO_DEVICE);
if (status & SDMMC_STA_DCRCFAIL) {
dev_err(priv->dev, "error SDMMC_STA_DCRCFAIL (0x%x) for cmd %d\n",
@@ -481,26 +481,26 @@ static int stm32_sdmmc2_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
{
struct stm32_sdmmc2_priv *priv = to_mci_host(mci);
u32 cmdat = data ? SDMMC_CMD_CMDTRANS : 0;
- u32 data_length;
- int ret, retry = 3;
-
-retry_cmd:
- data_length = 0;
+ dma_addr_t dma_addr = DMA_ERROR_CODE;
+ u32 data_length = 0;
+ int ret;
if (data) {
data_length = data->blocks * data->blocksize;
- stm32_sdmmc2_start_data(priv, data, data_length);
+ dma_addr = stm32_sdmmc2_start_data(priv, data, data_length);
+ if (dma_addr == DMA_ERROR_CODE)
+ return -EFAULT;
}
stm32_sdmmc2_start_cmd(priv, cmd, cmdat, data_length);
- dev_dbg(priv->dev, "%s: send cmd %d data: 0x%x @ 0x%x\n", __func__,
- cmd->cmdidx, data ? data_length : 0, (unsigned int)data);
+ dev_dbg(priv->dev, "%s: send cmd %d data: 0x%x @ %p\n", __func__,
+ cmd->cmdidx, data ? data_length : 0, data);
ret = stm32_sdmmc2_end_cmd(priv, cmd);
if (data && !ret)
- ret = stm32_sdmmc2_end_data(priv, cmd, data);
+ ret = stm32_sdmmc2_end_data(priv, cmd, data, dma_addr);
/* Clear flags */
writel(SDMMC_ICR_STATIC_FLAGS, priv->base + SDMMC_ICR);
@@ -530,15 +530,6 @@ retry_cmd:
writel(SDMMC_ICR_STATIC_FLAGS, priv->base + SDMMC_ICR);
}
- if (ret && retry) {
- dev_warn(priv->dev, "%s: cmd %d failed, retrying ...\n",
- __func__, cmd->cmdidx);
-
- retry--;
-
- goto retry_cmd;
- }
-
dev_dbg(priv->dev, "%s: end for CMD %d, ret = %d\n", __func__,
cmd->cmdidx, ret);
@@ -550,11 +541,14 @@ static void stm32_sdmmc2_set_ios(struct mci_host *mci, struct mci_ios *ios)
struct stm32_sdmmc2_priv *priv = to_mci_host(mci);
u32 desired = mci->clock;
u32 sys_clock = clk_get_rate(priv->clk);
- u32 clk = 0;
+ u32 clk = 0, ddr = 0;
dev_dbg(priv->dev, "%s: bus_width = %d, clock = %d\n", __func__,
mci->bus_width, mci->clock);
+ if (mci_timing_is_ddr(ios->timing))
+ ddr = SDMMC_CLKCR_DDR;
+
if (mci->clock)
stm32_sdmmc2_pwron(priv);
else
@@ -567,13 +561,15 @@ static void stm32_sdmmc2_set_ios(struct mci_host *mci, struct mci_ios *ios)
* clk_div > 0 and NEGEDGE = 1 => command and data generated on
* SDMMCCLK falling edge
*/
- if (desired && (sys_clock > desired ||
+ if (desired && (sys_clock > desired || ddr ||
IS_RISING_EDGE(priv->clk_reg_msk))) {
clk = DIV_ROUND_UP(sys_clock, 2 * desired);
if (clk > SDMMC_CLKCR_CLKDIV_MAX)
clk = SDMMC_CLKCR_CLKDIV_MAX;
}
+ clk |= ddr;
+
if (mci->bus_width == MMC_BUS_WIDTH_4)
clk |= SDMMC_CLKCR_WIDBUS_4;
if (mci->bus_width == MMC_BUS_WIDTH_8)
@@ -586,8 +582,8 @@ static void stm32_sdmmc2_set_ios(struct mci_host *mci, struct mci_ios *ios)
static int stm32_sdmmc2_probe(struct amba_device *adev,
const struct amba_id *id)
{
- struct device_d *dev = &adev->dev;
- struct device_node *np = dev->device_node;
+ struct device *dev = &adev->dev;
+ struct device_node *np = dev->of_node;
struct stm32_sdmmc2_priv *priv;
struct mci_host *mci;
int ret;
@@ -598,8 +594,8 @@ static int stm32_sdmmc2_probe(struct amba_device *adev,
priv->dev = dev;
mci = &priv->mci;
- mci->send_cmd = stm32_sdmmc2_send_cmd,
- mci->set_ios = stm32_sdmmc2_set_ios,
+ mci->send_cmd = stm32_sdmmc2_send_cmd;
+ mci->set_ios = stm32_sdmmc2_set_ios;
mci->init = stm32_sdmmc2_reset;
mci->hw_dev = dev;
@@ -636,6 +632,11 @@ static int stm32_sdmmc2_probe(struct amba_device *adev,
if (mci->f_max >= 52000000)
mci->host_caps |= MMC_CAP_MMC_HIGHSPEED_52MHZ;
+ if (of_property_read_bool(np, "mmc-ddr-3_3v"))
+ mci->host_caps |= MMC_CAP_MMC_3_3V_DDR;
+ if (of_property_read_bool(np, "mmc-ddr-1_8v"))
+ mci->host_caps |= MMC_CAP_MMC_1_8V_DDR;
+
return mci_register(&priv->mci);
priv_free:
@@ -645,11 +646,21 @@ priv_free:
}
static struct amba_id stm32_sdmmc2_ids[] = {
- /* ST Micro STM32MP157C */
+ /* ST Micro STM32MP15 v1.1 */
{
.id = 0x10153180,
.mask = 0xf0ffffff,
},
+ /* ST Micro STM32MP15 v2.0 */
+ {
+ .id = 0x00253180,
+ .mask = 0xf0ffffff,
+ },
+ /* ST Micro STM32MP13 */
+ {
+ .id = 0x20253180,
+ .mask = 0xf0ffffff,
+ },
{ 0, 0 },
};
diff --git a/drivers/mci/tegra-sdmmc.c b/drivers/mci/tegra-sdmmc.c
index 1cc75dc524..e940edf322 100644
--- a/drivers/mci/tegra-sdmmc.c
+++ b/drivers/mci/tegra-sdmmc.c
@@ -1,21 +1,6 @@
-/*
- * Copyright (C) 2013 Lucas Stach <l.stach@pengutronix.de>
- *
- * Partly based on code (C) Copyright 2010-2013
- * NVIDIA Corporation <www.nvidia.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
+// SPDX-License-Identifier: GPL-2.0-only
+// SPDX-FileCopyrightText: 2013 Lucas Stach <l.stach@pengutronix.de>
+// SPDX-FileCopyrightText: 2010-2013 NVIDIA Corporation (http://www.nvidia.com/)
#include <common.h>
#include <clock.h>
@@ -128,13 +113,15 @@ static int tegra_sdmmc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
num_bytes = data->blocks * data->blocksize;
if (data->flags & MMC_DATA_WRITE) {
- dma_sync_single_for_device((unsigned long)data->src,
+ dma_sync_single_for_device(mci->hw_dev, (unsigned long)data->src,
num_bytes, DMA_TO_DEVICE);
- sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS, (u32)data->src);
+ sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS,
+ lower_32_bits(virt_to_phys(data->src)));
} else {
- dma_sync_single_for_device((unsigned long)data->dest,
+ dma_sync_single_for_device(mci->hw_dev, (unsigned long)data->dest,
num_bytes, DMA_FROM_DEVICE);
- sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS, (u32)data->dest);
+ sdhci_write32(&host->sdhci, SDHCI_DMA_ADDRESS,
+ lower_32_bits(virt_to_phys(data->dest)));
}
sdhci_write32(&host->sdhci, SDHCI_BLOCK_SIZE__BLOCK_COUNT,
@@ -235,10 +222,10 @@ static int tegra_sdmmc_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, val);
if (data->flags & MMC_DATA_WRITE)
- dma_sync_single_for_cpu((unsigned long)data->src,
+ dma_sync_single_for_cpu(mci->hw_dev, (unsigned long)data->src,
num_bytes, DMA_TO_DEVICE);
else
- dma_sync_single_for_cpu((unsigned long)data->dest,
+ dma_sync_single_for_cpu(mci->hw_dev, (unsigned long)data->dest,
num_bytes, DMA_FROM_DEVICE);
}
@@ -297,7 +284,7 @@ static void tegra_sdmmc_set_ios(struct mci_host *mci, struct mci_ios *ios)
sdhci_write32(&host->sdhci, TEGRA_SDMMC_PWR_CNTL, val);
}
-static int tegra_sdmmc_init(struct mci_host *mci, struct device_d *dev)
+static int tegra_sdmmc_init(struct mci_host *mci, struct device *dev)
{
struct tegra_sdmmc_host *host = to_tegra_sdmmc_host(mci);
void __iomem *regs = host->regs;
@@ -323,9 +310,8 @@ static int tegra_sdmmc_init(struct mci_host *mci, struct device_d *dev)
sdhci_write32(&host->sdhci, TEGRA_SDMMC_PWR_CNTL, val);
/* sdmmc1 and sdmmc3 on T30 need a bit of padctrl init */
- if (of_device_is_compatible(mci->hw_dev->device_node,
- "nvidia,tegra30-sdhci") &&
- ((u32)regs == 0x78000000 || (u32)regs == 78000400)) {
+ if (of_device_is_compatible(mci->hw_dev->of_node, "nvidia,tegra30-sdhci") &&
+ (regs == IOMEM(0x78000000) || regs == IOMEM(0x78000400))) {
val = readl(regs + TEGRA_SDMMC_SDMEMCOMPPADCTRL);
val &= 0xfffffff0;
val |= 0x7 << TEGRA_SDMMC_SDMEMCOMPPADCTRL_VREF_SEL_SHIFT;
@@ -379,23 +365,16 @@ static int tegra_sdmmc_card_present(struct mci_host *mci)
return !(sdhci_read32(&host->sdhci, SDHCI_PRESENT_STATE) & SDHCI_WRITE_PROTECT);
}
-static int tegra_sdmmc_detect(struct device_d *dev)
-{
- struct tegra_sdmmc_host *host = dev->priv;
-
- return mci_detect_card(&host->mci);
-}
-
static void tegra_sdmmc_parse_dt(struct tegra_sdmmc_host *host)
{
- struct device_node *np = host->mci.hw_dev->device_node;
+ struct device_node *np = host->mci.hw_dev->of_node;
host->gpio_cd = of_get_named_gpio(np, "cd-gpios", 0);
host->gpio_pwr = of_get_named_gpio(np, "power-gpios", 0);
mci_of_parse(&host->mci);
}
-static int tegra_sdmmc_probe(struct device_d *dev)
+static int tegra_sdmmc_probe(struct device *dev)
{
struct resource *iores;
struct tegra_sdmmc_host *host;
@@ -459,9 +438,6 @@ static int tegra_sdmmc_probe(struct device_d *dev)
mci->host_caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED_52MHZ |
MMC_CAP_SD_HIGHSPEED;
- dev->priv = host;
- dev->detect = tegra_sdmmc_detect;
-
return mci_register(&host->mci);
}
@@ -476,8 +452,9 @@ static __maybe_unused struct of_device_id tegra_sdmmc_compatible[] = {
/* sentinel */
}
};
+MODULE_DEVICE_TABLE(of, tegra_sdmmc_compatible);
-static struct driver_d tegra_sdmmc_driver = {
+static struct driver tegra_sdmmc_driver = {
.name = "tegra-sdmmc",
.probe = tegra_sdmmc_probe,
.of_compatible = DRV_OF_COMPAT(tegra_sdmmc_compatible),