1// SPDX-License-Identifier: GPL-2.0+ 2/* 3 * Socionext F_SDH30 eMMC driver 4 * Copyright 2021 Linaro Ltd. 5 * Copyright 2021 Socionext, Inc. 6 */ 7 8#include <common.h> 9#include <clk.h> 10#include <dm.h> 11#include <malloc.h> 12#include <sdhci.h> 13 14#define F_SDH30_ESD_CONTROL 0x124 15#define F_SDH30_CMD_DAT_DELAY BIT(9) 16 17#define F_SDH30_TEST 0x158 18#define F_SDH30_FORCE_CARD_INSERT BIT(6) 19 20struct f_sdh30_data { 21 void (*init)(struct udevice *dev); 22 u32 quirks; 23}; 24 25struct f_sdh30_plat { 26 struct mmc_config cfg; 27 struct mmc mmc; 28 29 bool enable_cmd_dat_delay; 30 const struct f_sdh30_data *data; 31}; 32 33DECLARE_GLOBAL_DATA_PTR; 34 35static void f_sdh30_e51_init(struct udevice *dev) 36{ 37 struct f_sdh30_plat *plat = dev_get_plat(dev); 38 struct sdhci_host *host = dev_get_priv(dev); 39 u32 val; 40 41 val = sdhci_readl(host, F_SDH30_ESD_CONTROL); 42 if (plat->enable_cmd_dat_delay) 43 val |= F_SDH30_CMD_DAT_DELAY; 44 else 45 val &= ~F_SDH30_CMD_DAT_DELAY; 46 sdhci_writel(host, val, F_SDH30_ESD_CONTROL); 47 48 val = sdhci_readl(host, F_SDH30_TEST); 49 if (plat->cfg.host_caps & MMC_CAP_NONREMOVABLE) 50 val |= F_SDH30_FORCE_CARD_INSERT; 51 else 52 val &= ~F_SDH30_FORCE_CARD_INSERT; 53 sdhci_writel(host, val, F_SDH30_TEST); 54} 55 56static int f_sdh30_sdhci_probe(struct udevice *dev) 57{ 58 struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); 59 struct f_sdh30_plat *plat = dev_get_plat(dev); 60 struct sdhci_host *host = dev_get_priv(dev); 61 int ret; 62 63 plat->data = (const struct f_sdh30_data *)dev_get_driver_data(dev); 64 65 ret = mmc_of_parse(dev, &plat->cfg); 66 if (ret) 67 return ret; 68 69 host->mmc = &plat->mmc; 70 host->mmc->dev = dev; 71 host->mmc->priv = host; 72 73 if (plat->data && plat->data->quirks) 74 host->quirks = plat->data->quirks; 75 76 ret = sdhci_setup_cfg(&plat->cfg, host, 200000000, 400000); 77 if (ret) 78 return ret; 79 80 upriv->mmc = host->mmc; 81 82 mmc_set_clock(host->mmc, host->mmc->cfg->f_min, MMC_CLK_ENABLE); 83 84 ret = sdhci_probe(dev); 85 if (ret) 86 return ret; 87 88 if (plat->data && plat->data->init) 89 plat->data->init(dev); 90 91 return 0; 92} 93 94static int f_sdh30_of_to_plat(struct udevice *dev) 95{ 96 struct sdhci_host *host = dev_get_priv(dev); 97 struct f_sdh30_plat *plat = dev_get_plat(dev); 98 99 host->name = strdup(dev->name); 100 host->ioaddr = dev_read_addr_ptr(dev); 101 host->bus_width = dev_read_u32_default(dev, "bus-width", 4); 102 host->index = dev_read_u32_default(dev, "index", 0); 103 104 plat->enable_cmd_dat_delay = 105 dev_read_bool(dev, "socionext,enable-cmd-dat-delay"); 106 107 return 0; 108} 109 110static int f_sdh30_bind(struct udevice *dev) 111{ 112 struct f_sdh30_plat *plat = dev_get_plat(dev); 113 114 return sdhci_bind(dev, &plat->mmc, &plat->cfg); 115} 116 117static const struct f_sdh30_data f_sdh30_e51_data = { 118 .init = f_sdh30_e51_init, 119 .quirks = SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_SUPPORT_SINGLE, 120}; 121 122static const struct udevice_id f_sdh30_mmc_ids[] = { 123 { 124 .compatible = "fujitsu,mb86s70-sdhci-3.0", 125 }, 126 { 127 .compatible = "socionext,f-sdh30-e51-mmc", 128 .data = (ulong)&f_sdh30_e51_data, 129 }, 130 { } 131}; 132 133U_BOOT_DRIVER(f_sdh30_drv) = { 134 .name = "f_sdh30_sdhci", 135 .id = UCLASS_MMC, 136 .of_match = f_sdh30_mmc_ids, 137 .of_to_plat = f_sdh30_of_to_plat, 138 .ops = &sdhci_ops, 139 .bind = f_sdh30_bind, 140 .probe = f_sdh30_sdhci_probe, 141 .priv_auto = sizeof(struct sdhci_host), 142 .plat_auto = sizeof(struct f_sdh30_plat), 143}; 144