diff options
Diffstat (limited to 'drivers/mci')
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), ®map_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, ¶ms->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), |