[PATCH v1 1/3] mmc: snps_sdhci: Add sdhci driver support for TH1520 SoC

Maxim Kiselev bigunclemax at gmail.com
Mon Dec 9 11:15:20 CET 2024


Hi, Jaehoon

Thanks for your review!

чт, 5 дек. 2024 г. в 02:37, Jaehoon Chung <jh80.chung at samsung.com>:

...[snip]...

> > +
> > +static void sdhci_phy_1_8v_init(struct sdhci_host *host)
> > +{
> > +     struct snps_sdhci_plat *plat = dev_get_plat(host->mmc->dev);
> > +     u32 val;
> > +
> > +     /* deassert phy reset & set tx drive strength */
> > +     val = PHY_CNFG_RSTN_DEASSERT;
> > +     val |= FIELD_PREP(PHY_CNFG_PAD_SP_MASK, PHY_CNFG_PAD_SP);
> > +     val |= FIELD_PREP(PHY_CNFG_PAD_SN_MASK, PHY_CNFG_PAD_SN);
> > +     sdhci_writel(host, val, PHY_CNFG_R);
> > +
> > +     /* disable delay line */
> > +     sdhci_writeb(host, PHY_SDCLKDL_CNFG_UPDATE, PHY_SDCLKDL_CNFG_R);
> > +
> > +     /* set delay line */
> > +     sdhci_writeb(host, plat->delay_line, PHY_SDCLKDL_DC_R);
> > +     sdhci_writeb(host, PHY_DLL_CNFG2_JUMPSTEP, PHY_DLL_CNFG2_R);
> > +
> > +     /* enable delay lane */
> > +     val = sdhci_readb(host, PHY_SDCLKDL_CNFG_R);
> > +     val &= ~(PHY_SDCLKDL_CNFG_UPDATE);
> > +     sdhci_writeb(host, val, PHY_SDCLKDL_CNFG_R);
> > +
> > +     /* configure phy pads */
> > +     val = PHY_PAD_RXSEL_1V8;
> > +     val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP);
> > +     val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
> > +     val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_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 = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
> > +     val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
> > +     sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
> > +
> > +     val = PHY_PAD_RXSEL_1V8;
> > +     val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLDOWN);
> > +     val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
> > +     val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
> > +     sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
> > +
> > +     /* enable data strobe mode */
> > +     sdhci_writeb(host, FIELD_PREP(PHY_DLLDL_CNFG_SLV_INPSEL_MASK, PHY_DLLDL_CNFG_SLV_INPSEL),
> > +                  PHY_DLLDL_CNFG_R);
> > +
> > +     /* enable phy dll */
> > +     sdhci_writeb(host, PHY_DLL_CTRL_ENABLE, PHY_DLL_CTRL_R);
> > +}
> > +
> > +static void sdhci_phy_3_3v_init(struct sdhci_host *host)
> > +{
> > +     struct snps_sdhci_plat *plat = dev_get_plat(host->mmc->dev);
> > +     u32 val;
> > +
> > +     /* deassert phy reset & set tx drive strength */
> > +     val = PHY_CNFG_RSTN_DEASSERT;
> > +     val |= FIELD_PREP(PHY_CNFG_PAD_SP_MASK, PHY_CNFG_PAD_SP);
> > +     val |= FIELD_PREP(PHY_CNFG_PAD_SN_MASK, PHY_CNFG_PAD_SN);
> > +     sdhci_writel(host, val, PHY_CNFG_R);
> > +
> > +     /* disable delay line */
> > +     sdhci_writeb(host, PHY_SDCLKDL_CNFG_UPDATE, PHY_SDCLKDL_CNFG_R);
> > +
> > +     /* set delay line */
> > +     sdhci_writeb(host, plat->delay_line, PHY_SDCLKDL_DC_R);
> > +     sdhci_writeb(host, PHY_DLL_CNFG2_JUMPSTEP, PHY_DLL_CNFG2_R);
> > +
> > +     /* enable delay lane */
> > +     val = sdhci_readb(host, PHY_SDCLKDL_CNFG_R);
> > +     val &= ~(PHY_SDCLKDL_CNFG_UPDATE);
> > +     sdhci_writeb(host, val, PHY_SDCLKDL_CNFG_R);
> > +
> > +     /* configure phy pads */
> > +     val = PHY_PAD_RXSEL_3V3;
> > +     val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLUP);
> > +     val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
> > +     val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_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 = FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
> > +     val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
> > +     sdhci_writew(host, val, PHY_CLKPAD_CNFG_R);
> > +
> > +     val = PHY_PAD_RXSEL_3V3;
> > +     val |= FIELD_PREP(PHY_PAD_WEAKPULL_MASK, PHY_PAD_WEAKPULL_PULLDOWN);
> > +     val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_P_MASK, PHY_PAD_TXSLEW_CTRL_P);
> > +     val |= FIELD_PREP(PHY_PAD_TXSLEW_CTRL_N_MASK, PHY_PAD_TXSLEW_CTRL_N);
> > +     sdhci_writew(host, val, PHY_STBPAD_CNFG_R);
> > +
> > +     /* enable phy dll */
> > +     sdhci_writeb(host, PHY_DLL_CTRL_ENABLE, PHY_DLL_CTRL_R);
> > +}
> > +
> > +static void snps_sdhci_set_phy(struct sdhci_host *host)
> > +{
> > +     struct snps_sdhci_plat *plat = dev_get_plat(host->mmc->dev);
> > +     struct mmc *mmc = host->mmc;
> > +
> > +     /* Before power on, set PHY configs */
> > +     if ((plat->flags & FLAG_IO_FIXED_1V8) ||
> > +         mmc->signal_voltage == MMC_SIGNAL_VOLTAGE_180)
> > +             sdhci_phy_1_8v_init(host);
> > +     else
> > +             sdhci_phy_3_3v_init(host);
>
>
> Well, if my reading is right, sdhci_phy_1_8v_init and sdhci_3_3v_init are different PHY_PAD_RXSEL_1V8 and PHY_PAD_RXSEL_3V3.
> Also  enable data strobe mode part.
>
> I'm not sure why mainline kernel code doesn't re-use some code.
> It can be reducing code.
> Even though There are no objection because of mainline kernel codes, frankly, it's not my preference.
>
> I have posted the kernel patch to reuse code. (I'm not sure if can be accepted.)
> https://patchwork.kernel.org/project/linux-mmc/patch/20241204100507.330025-1-jh80.chung@samsung.com/
>
>

Good catch, I'll wait when your linux patch will be accepted.
And I will make the same changes here.

...[snip]...

> > +static const struct sdhci_ops snps_sdhci_ops = {
> > +     .set_ios_post = snps_sdhci_set_ios_post,
> > +     .platform_execute_tuning = snps_sdhci_execute_tuning,
> > +     .set_enhanced_strobe = snps_sdhci_set_enhanced_strobe,
> > +#if CONFIG_IS_ENABLED(CONFIG_MMC_SDHCI_ADMA_HELPERS)
>
> CONFIG_IS_ENABLED(MMC_SDHCI_ADMA_HELPERS) ?
>

I'll fix it in v2.

> > +     .adma_write_desc = snps_sdhci_adma_write_desc,
> > +#endif
> > +};
> > +
> > +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_plat(dev);
> > +     struct mmc_config *cfg = &plat->cfg;
> > +     struct sdhci_host *host = dev_get_priv(dev);
> > +     struct clk clk;
> > +     int ret;
> > +
> > +     plat->delay_line = PHY_SDCLKDL_DC_DEFAULT;
> > +
> > +     host->max_clk = cfg->f_max;
> > +     ret = clk_get_by_index(dev, 0, &clk);
> > +     if (!ret) {
> > +             ret = clk_set_rate(&clk, host->max_clk);
> > +             if (IS_ERR_VALUE(ret))
> > +                     printf("%s clk set rate fail!\n", __func__);
>
> Even though clock set rate is failed, it doesn't matter to be still going?
>

Well, I took this part from the rockchip sdhci driver as is...
But mmc clk actually is fixed on TH1520 and we can't change it.
So I'll drop this in v2.

> > +     } else {
> > +             printf("%s fail to get clk\n", __func__);
>
> Ditto?
>
> > +     }
> > +
> > +     host->ops = &snps_sdhci_ops;
> > +
> > +     host->mmc = &plat->mmc;
> > +     host->mmc->priv = host;
> > +     host->mmc->dev = dev;
> > +     upriv->mmc = host->mmc;
> > +
> > +     ret = sdhci_setup_cfg(cfg, host, cfg->f_max, EMMC_MIN_FREQ);
> > +     if (ret)
> > +             return ret;
> > +
> > +     if ((dev_read_bool(dev, "mmc-ddr-1_8v")) ||
> > +         (dev_read_bool(dev, "mmc-hs200-1_8v")) ||
> > +         (dev_read_bool(dev, "mmc-hs400-1_8v")))
> > +             plat->flags |= FLAG_IO_FIXED_1V8;
> > +     else
> > +             plat->flags &= ~FLAG_IO_FIXED_1V8;
> > +
> > +     return sdhci_probe(dev);
> > +}
> > +
> > +static int snps_sdhci_of_to_plat(struct udevice *dev)
> > +{
> > +     struct snps_sdhci_plat *plat = dev_get_plat(dev);
> > +     struct mmc_config *cfg = &plat->cfg;
> > +     struct sdhci_host *host = dev_get_priv(dev);
> > +     int ret;
> > +
> > +     host->name = dev->name;
> > +     host->ioaddr = dev_read_addr_ptr(dev);
> > +
> > +     ret = mmc_of_parse(dev, cfg);
> > +     if (ret)
> > +             return ret;
> > +
> > +     return 0;
>
> Is it possible to use "return mmc_of_parse();"?

Yes, it is. Thanks, will be fixed in v2.

>
> Best Regards,
> Jaehoon Chung
>

Best wishes,
Maksim

> > +}
> > +
> > +static int snps_sdhci_bind(struct udevice *dev)
> > +{
> > +     struct snps_sdhci_plat *plat = dev_get_plat(dev);
> > +
> > +     return sdhci_bind(dev, &plat->mmc, &plat->cfg);
> > +}
> > +
> > +static const struct udevice_id snps_sdhci_ids[] = {
> > +     { .compatible = "thead,th1520-dwcmshc" }
> > +};
> > +
> > +U_BOOT_DRIVER(snps_sdhci_drv) = {
> > +     .name           = "snps_sdhci",
> > +     .id             = UCLASS_MMC,
> > +     .of_match       = snps_sdhci_ids,
> > +     .of_to_plat     = snps_sdhci_of_to_plat,
> > +     .ops            = &sdhci_ops,
> > +     .bind           = snps_sdhci_bind,
> > +     .probe          = snps_sdhci_probe,
> > +     .priv_auto = sizeof(struct sdhci_host),
> > +     .plat_auto = sizeof(struct snps_sdhci_plat),
> > +};
> > --
> > 2.45.2


More information about the U-Boot mailing list