mmc: sdhci-of-dwcmshc: add command queue support for rockchip SOCs

Signed-off-by: Yifeng Zhao <yifeng.zhao@rock-chips.com>
Change-Id: I96c3dfb56d4dd3af3e70cdd59bbd16403abd2fcb
This commit is contained in:
Yifeng Zhao
2024-05-22 15:20:52 +08:00
committed by Tao Huang
parent 8d00b0267e
commit 2ef0767967

View File

@@ -23,6 +23,7 @@
#include "sdhci-pltfm.h"
#include "mmc_hsq.h"
#include "cqhci.h"
#include "sdhci-cqhci.h"
#define SDHCI_DWCMSHC_ARG2_STUFF GENMASK(31, 16)
@@ -48,6 +49,8 @@
#define DWCMSHC_EMMC_DLL_TXCLK 0x808
#define DWCMSHC_EMMC_DLL_STRBIN 0x80c
#define DECMSHC_EMMC_DLL_CMDOUT 0x810
#define DECMSHC_EMMC_MISC_CON 0x81C
#define MISC_INTCLK_EN BIT(1)
#define DWCMSHC_EMMC_DLL_STATUS0 0x840
#define DWCMSHC_EMMC_DLL_STATUS1 0x844
#define DWCMSHC_EMMC_DLL_START BIT(0)
@@ -299,6 +302,61 @@ static void dwcmshc_sdhci_cqe_enable(struct mmc_host *mmc)
sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL);
}
static void rk35xx_sdhci_cqe_enable(struct mmc_host *mmc)
{
struct sdhci_host *host = mmc_priv(mmc);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
u32 reg;
reg = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
reg |= CQHCI_ENABLE;
sdhci_writel(host, reg, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
while (reg & SDHCI_DATA_AVAILABLE) {
sdhci_readl(host, SDHCI_BUFFER);
reg = sdhci_readl(host, SDHCI_PRESENT_STATE);
}
sdhci_writew(host, DWCMSHC_SDHCI_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE);
sdhci_cqe_enable(mmc);
sdhci_writew(host, DWCMSHC_SDHCI_CQE_TRNS_MODE, SDHCI_TRANSFER_MODE);
}
static void rk35xx_sdhci_cqe_disabled(struct mmc_host *mmc, bool recovery)
{
struct sdhci_host *host = mmc_priv(mmc);
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
unsigned long flags;
u32 ctrl;
mmc->cqe_ops->cqe_wait_for_idle(mmc);
spin_lock_irqsave(&host->lock, flags);
/*
* During CQE command transfers, command complete bit gets latched.
* So s/w should clear command complete interrupt status when CQE is
* either halted or disabled. Otherwise unexpected SDCHI legacy
* interrupt gets triggered when CQE is halted/disabled.
*/
ctrl = sdhci_readl(host, SDHCI_INT_ENABLE);
ctrl |= SDHCI_INT_RESPONSE;
sdhci_writel(host, ctrl, SDHCI_INT_ENABLE);
sdhci_writel(host, SDHCI_INT_RESPONSE, SDHCI_INT_STATUS);
spin_unlock_irqrestore(&host->lock, flags);
sdhci_cqe_disable(mmc, recovery);
ctrl = sdhci_readl(host, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
ctrl &= ~CQHCI_ENABLE;
sdhci_writel(host, ctrl, dwc_priv->vendor_specific_area2 + CQHCI_CFG);
}
static void dwcmshc_set_tran_desc(struct cqhci_host *cq_host, u8 **desc,
dma_addr_t addr, int len, bool end, bool dma64)
{
@@ -469,6 +527,7 @@ static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask)
struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host);
struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host);
struct rk35xx_priv *priv = dwc_priv->priv;
u32 extra = sdhci_readl(host, DECMSHC_EMMC_MISC_CON);
if (mask & SDHCI_RESET_ALL && priv->reset) {
reset_control_assert(priv->reset);
@@ -477,12 +536,17 @@ static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask)
}
sdhci_reset(host, mask);
/* Enable INTERNAL CLOCK */
sdhci_writel(host, MISC_INTCLK_EN | extra, DECMSHC_EMMC_MISC_CON);
}
static void sdhci_dwcmshc_request_done(struct sdhci_host *host, struct mmc_request *mrq)
{
if (mmc_hsq_finalize_request(host->mmc, mrq))
return;
if (!(host->mmc->caps2 & MMC_CAP2_CQE)) {
if (mmc_hsq_finalize_request(host->mmc, mrq))
return;
}
mmc_request_done(host->mmc, mrq);
}
@@ -537,11 +601,19 @@ static const struct cqhci_host_ops dwcmshc_cqhci_ops = {
.set_tran_desc = dwcmshc_set_tran_desc,
};
static const struct cqhci_host_ops rk35xx_cqhci_ops = {
.enable = rk35xx_sdhci_cqe_enable,
.disable = rk35xx_sdhci_cqe_disabled,
.dumpregs = dwcmshc_cqhci_dumpregs,
.set_tran_desc = dwcmshc_set_tran_desc,
};
static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *pdev)
{
struct cqhci_host *cq_host;
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;
bool dma64 = false;
u16 clk;
int err;
@@ -567,7 +639,10 @@ static void dwcmshc_cqhci_init(struct sdhci_host *host, struct platform_device *
}
cq_host->mmio = host->ioaddr + priv->vendor_specific_area2;
cq_host->ops = &dwcmshc_cqhci_ops;
if (rk_priv)
cq_host->ops = &rk35xx_cqhci_ops;
else
cq_host->ops = &dwcmshc_cqhci_ops;
/* Enable using of 128-bit task descriptors */
dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
@@ -811,16 +886,6 @@ static int dwcmshc_probe(struct platform_device *pdev)
host->mmc_host_ops.hs400_enhanced_strobe = dwcmshc_hs400_enhanced_strobe;
host->mmc_host_ops.execute_tuning = dwcmshc_execute_tuning;
hsq = devm_kzalloc(&pdev->dev, sizeof(*hsq), GFP_KERNEL);
if (!hsq) {
err = -ENOMEM;
goto err_clk;
}
err = mmc_hsq_init(hsq, host->mmc);
if (err)
goto err_clk;
if (drv_data->flags & RK_PLATFROM) {
rk_priv = devm_kzalloc(&pdev->dev, sizeof(struct rk35xx_priv), GFP_KERNEL);
if (!rk_priv) {
@@ -859,6 +924,16 @@ static int dwcmshc_probe(struct platform_device *pdev)
sdhci_readw(host, DWCMSHC_P_VENDOR_AREA2);
dwcmshc_cqhci_init(host, pdev);
} else {
hsq = devm_kzalloc(&pdev->dev, sizeof(*hsq), GFP_KERNEL);
if (!hsq) {
err = -ENOMEM;
goto err_setup_host;
}
err = mmc_hsq_init(hsq, host->mmc);
if (err)
goto err_setup_host;
}
if (rk_priv)