mmc: sdhci-of-dwcmshc: add common bulk optional clocks support

In addition to the required core clock and optional
bus clock, the soc will expand its own clocks, so
the bulk clock mechanism is abstracted.

Note, I call the bulk clocks as "other clocks" due
to the bus clock has been called as "optional".

Signed-off-by: Chen Wang <unicorn_wang@outlook.com>
Tested-by: Drew Fustini <drew@pdp7.com> # TH1520
Tested-by: Inochi Amaoto <inochiama@outlook.com> # Duo and Huashan Pi
Acked-by: Adrian Hunter <adrian.hunter@intel.com>
Link: https://lore.kernel.org/r/e57e8c51da81f176b49608269a884f840903e78e.1722847198.git.unicorn_wang@outlook.com
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
This commit is contained in:
Chen Wang 2024-08-05 17:17:21 +08:00 committed by Ulf Hansson
parent b6db8f1fb4
commit 977849b2d4

View File

@ -108,7 +108,6 @@
#define DLL_LOCK_WO_TMOUT(x) \
((((x) & DWCMSHC_EMMC_DLL_LOCKED) == DWCMSHC_EMMC_DLL_LOCKED) && \
(((x) & DWCMSHC_EMMC_DLL_TIMEOUT) == 0))
#define RK35xx_MAX_CLKS 3
/* PHY register area pointer */
#define DWC_MSHC_PTR_PHY_R 0x300
@ -199,23 +198,54 @@ enum dwcmshc_rk_type {
};
struct rk35xx_priv {
/* Rockchip specified optional clocks */
struct clk_bulk_data rockchip_clks[RK35xx_MAX_CLKS];
struct reset_control *reset;
enum dwcmshc_rk_type devtype;
u8 txclk_tapnum;
};
#define DWCMSHC_MAX_OTHER_CLKS 3
struct dwcmshc_priv {
struct clk *bus_clk;
int vendor_specific_area1; /* P_VENDOR_SPECIFIC_AREA1 reg */
int vendor_specific_area2; /* P_VENDOR_SPECIFIC_AREA2 reg */
int num_other_clks;
struct clk_bulk_data other_clks[DWCMSHC_MAX_OTHER_CLKS];
void *priv; /* pointer to SoC private stuff */
u16 delay_line;
u16 flags;
};
static int dwcmshc_get_enable_other_clks(struct device *dev,
struct dwcmshc_priv *priv,
int num_clks,
const char * const clk_ids[])
{
int err;
if (num_clks > DWCMSHC_MAX_OTHER_CLKS)
return -EINVAL;
for (int i = 0; i < num_clks; i++)
priv->other_clks[i].id = clk_ids[i];
err = devm_clk_bulk_get_optional(dev, num_clks, priv->other_clks);
if (err) {
dev_err(dev, "failed to get clocks %d\n", err);
return err;
}
err = clk_bulk_prepare_enable(num_clks, priv->other_clks);
if (err)
dev_err(dev, "failed to enable clocks %d\n", err);
priv->num_other_clks = num_clks;
return err;
}
/*
* If DMA addr spans 128MB boundary, we split the DMA transfer into two
* so that each DMA transfer doesn't exceed the boundary.
@ -1036,8 +1066,9 @@ dsbl_cqe_caps:
static int dwcmshc_rk35xx_init(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv)
{
int err;
static const char * const clk_ids[] = {"axi", "block", "timer"};
struct rk35xx_priv *priv = dwc_priv->priv;
int err;
priv->reset = devm_reset_control_array_get_optional_exclusive(mmc_dev(host->mmc));
if (IS_ERR(priv->reset)) {
@ -1046,21 +1077,10 @@ static int dwcmshc_rk35xx_init(struct sdhci_host *host, struct dwcmshc_priv *dwc
return err;
}
priv->rockchip_clks[0].id = "axi";
priv->rockchip_clks[1].id = "block";
priv->rockchip_clks[2].id = "timer";
err = devm_clk_bulk_get_optional(mmc_dev(host->mmc), RK35xx_MAX_CLKS,
priv->rockchip_clks);
if (err) {
dev_err(mmc_dev(host->mmc), "failed to get clocks %d\n", err);
err = dwcmshc_get_enable_other_clks(mmc_dev(host->mmc), dwc_priv,
ARRAY_SIZE(clk_ids), clk_ids);
if (err)
return err;
}
err = clk_bulk_prepare_enable(RK35xx_MAX_CLKS, priv->rockchip_clks);
if (err) {
dev_err(mmc_dev(host->mmc), "failed to enable clocks %d\n", err);
return err;
}
if (of_property_read_u8(mmc_dev(host->mmc)->of_node, "rockchip,txclk-tapnum",
&priv->txclk_tapnum))
@ -1280,9 +1300,7 @@ err_rpm:
err_clk:
clk_disable_unprepare(pltfm_host->clk);
clk_disable_unprepare(priv->bus_clk);
if (rk_priv)
clk_bulk_disable_unprepare(RK35xx_MAX_CLKS,
rk_priv->rockchip_clks);
clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks);
free_pltfm:
sdhci_pltfm_free(pdev);
return err;
@ -1304,7 +1322,6 @@ static void dwcmshc_remove(struct platform_device *pdev)
struct sdhci_host *host = platform_get_drvdata(pdev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
struct rk35xx_priv *rk_priv = priv->priv;
pm_runtime_get_sync(&pdev->dev);
pm_runtime_disable(&pdev->dev);
@ -1316,9 +1333,7 @@ static void dwcmshc_remove(struct platform_device *pdev)
clk_disable_unprepare(pltfm_host->clk);
clk_disable_unprepare(priv->bus_clk);
if (rk_priv)
clk_bulk_disable_unprepare(RK35xx_MAX_CLKS,
rk_priv->rockchip_clks);
clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks);
sdhci_pltfm_free(pdev);
}
@ -1328,7 +1343,6 @@ static int dwcmshc_suspend(struct device *dev)
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
struct rk35xx_priv *rk_priv = priv->priv;
int ret;
pm_runtime_resume(dev);
@ -1347,9 +1361,7 @@ static int dwcmshc_suspend(struct device *dev)
if (!IS_ERR(priv->bus_clk))
clk_disable_unprepare(priv->bus_clk);
if (rk_priv)
clk_bulk_disable_unprepare(RK35xx_MAX_CLKS,
rk_priv->rockchip_clks);
clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks);
return ret;
}
@ -1359,7 +1371,6 @@ static int dwcmshc_resume(struct device *dev)
struct sdhci_host *host = dev_get_drvdata(dev);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host);
struct rk35xx_priv *rk_priv = priv->priv;
int ret;
ret = clk_prepare_enable(pltfm_host->clk);
@ -1372,29 +1383,24 @@ static int dwcmshc_resume(struct device *dev)
goto disable_clk;
}
if (rk_priv) {
ret = clk_bulk_prepare_enable(RK35xx_MAX_CLKS,
rk_priv->rockchip_clks);
if (ret)
goto disable_bus_clk;
}
ret = clk_bulk_prepare_enable(priv->num_other_clks, priv->other_clks);
if (ret)
goto disable_bus_clk;
ret = sdhci_resume_host(host);
if (ret)
goto disable_rockchip_clks;
goto disable_other_clks;
if (host->mmc->caps2 & MMC_CAP2_CQE) {
ret = cqhci_resume(host->mmc);
if (ret)
goto disable_rockchip_clks;
goto disable_other_clks;
}
return 0;
disable_rockchip_clks:
if (rk_priv)
clk_bulk_disable_unprepare(RK35xx_MAX_CLKS,
rk_priv->rockchip_clks);
disable_other_clks:
clk_bulk_disable_unprepare(priv->num_other_clks, priv->other_clks);
disable_bus_clk:
if (!IS_ERR(priv->bus_clk))
clk_disable_unprepare(priv->bus_clk);