123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * Copyright (C) 2021 Alibaba Group Holding Limited.
- */
- #include <common.h>
- #include <clk.h>
- #include <dm.h>
- #include <malloc.h>
- #include <sdhci.h>
- #include "snps_sdhci.h"
- #define HS400_DELAY_LANE 24
- volatile int DELAY_LANE = 50;
- static void sdhci_phy_1_8v_init_no_pull(struct sdhci_host *host)
- {
- uint32_t val;
- sdhci_writel(host, 1, DWC_MSHC_PTR_PHY_R);
- sdhci_writeb(host, 1 << 4, PHY_SDCLKDL_CNFG_R);
- sdhci_writeb(host, 0x40, PHY_SDCLKDL_DC_R);
- sdhci_writeb(host, 0xa, PHY_DLL_CNFG2_R);
- val = sdhci_readb(host, PHY_SDCLKDL_CNFG_R);
- val &= ~(1 << 4);
- sdhci_writeb(host, val, PHY_SDCLKDL_CNFG_R);
- val = sdhci_readw(host, PHY_CMDPAD_CNFG_R);
- sdhci_writew(host, val | 1, PHY_CMDPAD_CNFG_R);
- val = sdhci_readw(host, PHY_DATAPAD_CNFG_R);
- sdhci_writew(host, val | 1, PHY_DATAPAD_CNFG_R);
- val = sdhci_readw(host, PHY_RSTNPAD_CNFG_R);
- sdhci_writew(host, val | 1, PHY_RSTNPAD_CNFG_R);
- val = sdhci_readw(host, PHY_STBPAD_CNFG_R);
- sdhci_writew(host, val | 1, PHY_STBPAD_CNFG_R);
- val = sdhci_readb(host, PHY_DLL_CTRL_R);
- sdhci_writeb(host, val | 1, PHY_DLL_CTRL_R);
- }
- static void sdhci_phy_3_3v_init_no_pull(struct sdhci_host *host)
- {
- uint32_t val;
- sdhci_writel(host, 1, DWC_MSHC_PTR_PHY_R);
- sdhci_writeb(host, 1 << 4, PHY_SDCLKDL_CNFG_R);
- sdhci_writeb(host, 0x40, PHY_SDCLKDL_DC_R);
- sdhci_writeb(host, 0xa, PHY_DLL_CNFG2_R);
- val = sdhci_readb(host, PHY_SDCLKDL_CNFG_R);
- val &= ~(1 << 4);
- sdhci_writeb(host, val, PHY_SDCLKDL_CNFG_R);
- val = sdhci_readw(host, PHY_CMDPAD_CNFG_R);
- sdhci_writew(host, val | 2, PHY_CMDPAD_CNFG_R);
- val = sdhci_readw(host, PHY_DATAPAD_CNFG_R);
- sdhci_writew(host, val | 2, PHY_DATAPAD_CNFG_R);
- val = sdhci_readw(host, PHY_RSTNPAD_CNFG_R);
- sdhci_writew(host, val | 2, PHY_RSTNPAD_CNFG_R);
- val = sdhci_readw(host, PHY_STBPAD_CNFG_R);
- sdhci_writew(host, val | 2, PHY_STBPAD_CNFG_R);
- val = sdhci_readb(host, PHY_DLL_CTRL_R);
- sdhci_writeb(host, val | 1, PHY_DLL_CTRL_R);
- }
- static void sdhci_phy_1_8v_init(struct sdhci_host *host)
- {
- uint32_t val;
- struct snps_sdhci_plat *plat = dev_get_platdata(host->mmc->dev);
- if(plat->pull_up_en == false) {
- sdhci_phy_1_8v_init_no_pull(host);
- return;
- }
- //set driving force
- sdhci_writel(host, (1 << PHY_RSTN) | (0xc << PAD_SP) | (0xc << PAD_SN), PHY_CNFG_R);
- //disable delay lane
- sdhci_writeb(host, 1 << UPDATE_DC, PHY_SDCLKDL_CNFG_R);
- //set delay lane
- sdhci_writeb(host, DELAY_LANE, PHY_SDCLKDL_DC_R);
- sdhci_writeb(host, 0xa, PHY_DLL_CNFG2_R);
- //enable delay lane
- val = sdhci_readb(host, PHY_SDCLKDL_CNFG_R);
- val &= ~(1 << UPDATE_DC);
- sdhci_writeb(host, val, PHY_SDCLKDL_CNFG_R);
- val = (1 << RXSEL) | (1 << WEAKPULL_EN) | (3 << TXSLEW_CTRL_P) | (3 << TXSLEW_CTRL_N);
- sdhci_writew(host, val, PHY_CMDPAD_CNFG_R);
- sdhci_writew(host, val, PHY_DATAPAD_CNFG_R);
- sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R);
- val = (3 << TXSLEW_CTRL_P) | (3 << TXSLEW_CTRL_N);
- sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
- val = (1 << RXSEL) | (2 << WEAKPULL_EN) | (3 << TXSLEW_CTRL_P) | (3 << TXSLEW_CTRL_N);
- sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
- /* enable data strobe mode */
- sdhci_writeb(host, 0, PHY_DLL_CTRL_R);
- sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL);
- sdhci_writew(host, 0x8000, PHY_DLLBT_CNFG_R);
- sdhci_writeb(host, 3 << SLV_INPSEL, PHY_DLLDL_CNFG_R);
- sdhci_writeb(host, 0x25, PHY_DLL_CNFG1_R);
- sdhci_writew(host, 0x7, SDHCI_CLOCK_CONTROL);
- sdhci_writeb(host, (1 << DLL_EN), PHY_DLL_CTRL_R);
- }
- static void sdhci_phy_3_3v_init(struct sdhci_host *host)
- {
- uint32_t val;
- struct snps_sdhci_plat *plat = dev_get_platdata(host->mmc->dev);
- if(plat->pull_up_en == false) {
- sdhci_phy_3_3v_init_no_pull(host);
- return;
- }
- //set driving force
- sdhci_writel(host, (1 << PHY_RSTN) | (0xc << PAD_SP) | (0xc << PAD_SN), PHY_CNFG_R);
- //disable delay lane
- sdhci_writeb(host, 1 << UPDATE_DC, PHY_SDCLKDL_CNFG_R);
- //set delay lane
- sdhci_writeb(host, DELAY_LANE, PHY_SDCLKDL_DC_R);
- sdhci_writeb(host, 0xa, PHY_DLL_CNFG2_R);
- //enable delay lane
- val = sdhci_readb(host, PHY_SDCLKDL_CNFG_R);
- val &= ~(1 << UPDATE_DC);
- sdhci_writeb(host, val, PHY_SDCLKDL_CNFG_R);
- val = (2 << RXSEL) | (1 << WEAKPULL_EN) | (3 << TXSLEW_CTRL_P) | (3 << TXSLEW_CTRL_N);
- sdhci_writew(host, val, PHY_CMDPAD_CNFG_R);
- sdhci_writew(host, val, PHY_DATAPAD_CNFG_R);
- sdhci_writew(host, val, PHY_RSTNPAD_CNFG_R);
- val = (3 << TXSLEW_CTRL_P) | (3 << TXSLEW_CTRL_N);
- sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
- val = (2 << RXSEL) | (2 << WEAKPULL_EN) | (3 << TXSLEW_CTRL_P) | (3 << TXSLEW_CTRL_N);
- sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
- sdhci_writeb(host, (1 << DLL_EN), PHY_DLL_CTRL_R);
- /*set i wait*/
- sdhci_writeb(host, 0x5, PHY_DLL_CNFG1_R);
- }
- void snps_set_uhs_timing(struct sdhci_host *host)
- {
- struct mmc *mmc = (struct mmc *)host->mmc;
- u32 reg;
- reg = sdhci_readw(host, SDHCI_HOST_CONTROL2);
- reg &= ~SDHCI_CTRL_UHS_MASK;
- switch (mmc->selected_mode) {
- case UHS_SDR50:
- case MMC_HS_52:
- sdhci_phy_1_8v_init(host);
- reg |= SDHCI_CTRL_UHS_SDR50;
- break;
- case UHS_DDR50:
- case MMC_DDR_52:
- sdhci_phy_1_8v_init(host);
- reg |= SDHCI_CTRL_UHS_DDR50;
- break;
- case UHS_SDR104:
- case MMC_HS_200:
- sdhci_phy_1_8v_init(host);
- reg |= SDHCI_CTRL_UHS_SDR104;
- break;
- case MMC_HS_400:
- DELAY_LANE = HS400_DELAY_LANE;
- sdhci_phy_1_8v_init(host);
- reg |= SNPS_SDHCI_CTRL_HS400;
- break;
- default:
- sdhci_phy_3_3v_init(host);
- reg |= SDHCI_CTRL_UHS_SDR12;
- }
- sdhci_writew(host, reg, SDHCI_HOST_CONTROL2);
- if (mmc->selected_mode == MMC_HS_400) {
- // //disable delay lane
- // sdhci_writeb(host, 1 << UPDATE_DC, PHY_SDCLKDL_CNFG_R);
- // //set delay lane
- // sdhci_writeb(host, DELAY_LANE, PHY_SDCLKDL_DC_R);
- // //enable delay lane
- // reg = sdhci_readb(host, PHY_SDCLKDL_CNFG_R);
- // reg &= ~(1 << UPDATE_DC);
- // sdhci_writeb(host, reg, PHY_SDCLKDL_CNFG_R);
- //disable auto tuning
- reg = sdhci_readl(host, AT_CTRL_R);
- reg &= ~1;
- sdhci_writel(host, reg, AT_CTRL_R);
- } else {
- sdhci_writeb(host, 0, PHY_DLLDL_CNFG_R);
- }
- }
- static void snps_sdhci_set_control_reg(struct sdhci_host *host)
- {
- struct mmc *mmc = (struct mmc *)host->mmc;
- struct snps_sdhci_plat *plat = dev_get_platdata(host->mmc->dev);
- u32 reg;
- reg = sdhci_readw(host, EMMC_CTRL_R);
- if (IS_SD(host->mmc)) {
- reg &=~EMMC_CARD;
- } else {
- reg |=EMMC_CARD;
- }
- sdhci_writeb(host, reg, EMMC_CTRL_R);
- if (mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_180) {
- reg = sdhci_readw(host, SDHCI_HOST_CONTROL2);
- reg |= SDHCI_CTRL_VDD_180;
- sdhci_writew(host, reg, SDHCI_HOST_CONTROL2);
- sdhci_phy_1_8v_init(host);
- } else {
- reg = sdhci_readw(host, SDHCI_HOST_CONTROL2);
- reg &= ~SDHCI_CTRL_VDD_180;
- if (plat->io_fixed_1v8)
- reg |= SDHCI_CTRL_VDD_180;
- sdhci_writew(host, reg, SDHCI_HOST_CONTROL2);
- sdhci_phy_3_3v_init(host);
- }
- snps_set_uhs_timing(host);
- }
- extern int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data);
- static int snps_execute_tuning(struct mmc *mmc, u8 opcode)
- {
- #define SDHCI_TUNING_LOOP_COUNT 128
- struct sdhci_host *host = dev_get_priv(mmc->dev);
- struct mmc_cmd cmd;
- struct mmc_data data;
- char tuning_loop_counter = SDHCI_TUNING_LOOP_COUNT;
- uint32_t val = 0;
- uint16_t ctrl = 0;
- debug("%s\n", __func__);
- sdhci_writeb(host, 3 << INPSEL_CNFG, PHY_ATDL_CNFG_R);
- val = sdhci_readl(host, AT_CTRL_R);
- val &= ~((1 << CI_SEL) | (1 << RPT_TUNE_ERR)\
- | (1 << SW_TUNE_EN) |(0xf << WIN_EDGE_SEL));
- val |= (1 << AT_EN) | (1 << SWIN_TH_EN) | (1 << TUNE_CLK_STOP_EN)\
- | (1 << PRE_CHANGE_DLY) | (3 << POST_CHANGE_DLY) | (9 << SWIN_TH_VAL);
- sdhci_writel(host, val, AT_CTRL_R);
- val = sdhci_readl(host, AT_CTRL_R);
- if(!(val & (1 << AT_EN))) {
- printf("*****Auto Tuning is NOT Enable!!!\n");
- return -1;
- }
- //Start Tuning
- ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
- ctrl |= SDHCI_CTRL_EXEC_TUNING;
- sdhci_writew(host, ctrl, SDHCI_HOST_CONTROL2);
- mdelay(1);
- do {
- cmd.cmdidx = opcode;
- cmd.resp_type = MMC_RSP_R1;
- cmd.cmdarg = 0;
- data.blocksize = 64;
- data.blocks = 1;
- data.flags = MMC_DATA_READ;
- if (tuning_loop_counter-- == 0)
- break;
- if (cmd.cmdidx == MMC_CMD_SEND_TUNING_BLOCK_HS200 &&
- mmc->bus_width == 8)
- data.blocksize = 128;
- sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG,
- data.blocksize),
- SDHCI_BLOCK_SIZE);
- sdhci_writew(host, data.blocks, SDHCI_BLOCK_COUNT);
- sdhci_writew(host, SDHCI_TRNS_READ, SDHCI_TRANSFER_MODE);
- mmc_send_cmd(mmc, &cmd, NULL);
- ctrl = sdhci_readw(host, SDHCI_HOST_CONTROL2);
- if (cmd.cmdidx == MMC_CMD_SEND_TUNING_BLOCK)
- udelay(1);
- } while (ctrl & SDHCI_CTRL_EXEC_TUNING);
- if (tuning_loop_counter < 0) {
- ctrl &= ~SDHCI_CTRL_TUNED_CLK;
- sdhci_writel(host, ctrl, SDHCI_HOST_CONTROL2);
- }
- if (!(ctrl & SDHCI_CTRL_TUNED_CLK)) {
- printf("%s:Tuning failed\n", __func__);
- return -1;
- }
- return 0;
- }
- const struct sdhci_ops snps_ops = {
- .platform_execute_tuning = &snps_execute_tuning,
- .set_control_reg = &snps_sdhci_set_control_reg,
- };
- static int snps_sdhci_probe(struct udevice *dev)
- {
- struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
- struct snps_sdhci_plat *plat = dev_get_platdata(dev);
- struct sdhci_host *host = dev_get_priv(dev);
- u32 max_clk;
- struct clk clk;
- int ret;
- ret = clk_get_by_index(dev, 0, &clk);
- if (ret)
- return ret;
- plat->pull_up_en = false;
- plat->io_fixed_1v8 = false;
- host->name = dev->name;
- host->ioaddr = (void *)devfdt_get_addr(dev);
- max_clk = clk_get_rate(&clk);
- if (IS_ERR_VALUE(max_clk)) {
- ret = max_clk;
- goto err;
- }
- host->mmc = &plat->mmc;
- host->mmc->dev = dev;
- host->mmc->priv = host;
- upriv->mmc = host->mmc;
- host->ops = &snps_ops;
- ret = mmc_of_parse(dev, &plat->cfg);
- if (ret)
- goto err;
- ret = sdhci_setup_cfg(&plat->cfg, host, 0, 0);
- if (ret)
- goto err;
- host->voltages = MMC_VDD_33_34;
- ret = sdhci_probe(dev);
- if (ret)
- goto err;
- if (dev_read_bool(dev, "pull_up"))
- plat->pull_up_en = true;
- if (dev_read_bool(dev, "io_fixed_1v8"))
- plat->io_fixed_1v8 = true;
- uint16_t val = sdhci_readw(host, SDHCI_HOST_CONTROL2);
- if (host->voltages == MMC_VDD_165_195) {
- val |= SDHCI_CTRL_VDD_180;
- sdhci_writew(host, val, SDHCI_HOST_CONTROL2);
- sdhci_phy_1_8v_init(host);
- } else {
- val &= ~SDHCI_CTRL_VDD_180;
- if (plat->io_fixed_1v8)
- val |= SDHCI_CTRL_VDD_180;
- sdhci_writew(host, val, SDHCI_HOST_CONTROL2);
- sdhci_phy_3_3v_init(host);
- }
- host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
- return 0;
- err:
- clk_disable(&clk);
- clk_free(&clk);
- return ret;
- }
- int snps_sdhci_init(struct mmc *mmc)
- {
- struct sdhci_host *host = dev_get_priv(mmc->dev);
- struct snps_sdhci_plat *plat = dev_get_platdata(host->mmc->dev);
- int ret;
- host->voltages = MMC_VDD_33_34;
- ret = sdhci_probe(mmc->dev);
- if (ret)
- return -1;
- uint16_t val = sdhci_readw(host, SDHCI_HOST_CONTROL2);
- if (host->voltages == MMC_VDD_165_195) {
- val |= SDHCI_CTRL_VDD_180;
- sdhci_writew(host, val, SDHCI_HOST_CONTROL2);
- sdhci_phy_1_8v_init(host);
- } else {
- val &= ~SDHCI_CTRL_VDD_180;
- if (plat->io_fixed_1v8)
- val |= SDHCI_CTRL_VDD_180;
- sdhci_writew(host, val, SDHCI_HOST_CONTROL2);
- sdhci_phy_3_3v_init(host);
- }
- return 0;
- }
- static int snps_sdhci_bind(struct udevice *dev)
- {
- struct snps_sdhci_plat *plat = dev_get_platdata(dev);
- return sdhci_bind(dev, &plat->mmc, &plat->cfg);
- }
- static const struct udevice_id snps_sdhci_ids[] = {
- { .compatible = "snps,dwcmshc-sdhci" },
- { }
- };
- U_BOOT_DRIVER(snps_sdhci_drv) = {
- .name = "snps_sdhci",
- .id = UCLASS_MMC,
- .of_match = snps_sdhci_ids,
- .ops = &sdhci_ops,
- .bind = snps_sdhci_bind,
- .probe = snps_sdhci_probe,
- .priv_auto_alloc_size = sizeof(struct sdhci_host),
- .platdata_auto_alloc_size = sizeof(struct snps_sdhci_plat),
- };
|