Skip to content

Commit

Permalink
mmc: sdhci-brcmstb: Add support for Command Queuing (CQE)
Browse files Browse the repository at this point in the history
The latest Arasan controller first used in the 7216 now supports
CQE so enable this feature.

Signed-off-by: Al Cooper <alcooperx@gmail.com>
Link: https://lore.kernel.org/r/20200113210706.11972-6-alcooperx@gmail.com
Signed-off-by: Ulf Hansson <ulf.hansson@linaro.org>
  • Loading branch information
Al Cooper authored and Ulf Hansson committed Jan 20, 2020
1 parent e7b5d63 commit d46ba2d
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 8 deletions.
1 change: 1 addition & 0 deletions drivers/mmc/host/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -990,6 +990,7 @@ config MMC_SDHCI_BRCMSTB
tristate "Broadcom SDIO/SD/MMC support"
depends on ARCH_BRCMSTB || BMIPS_GENERIC
depends on MMC_SDHCI_PLTFM
select MMC_CQHCI
default y
help
This selects support for the SDIO/SD/MMC Host Controller on
Expand Down
140 changes: 132 additions & 8 deletions drivers/mmc/host/sdhci-brcmstb.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,27 @@
#include <linux/module.h>
#include <linux/of.h>
#include <linux/bitops.h>
#include <linux/delay.h>

#include "sdhci-pltfm.h"
#include "cqhci.h"

#define SDHCI_VENDOR 0x78
#define SDHCI_VENDOR_ENHANCED_STRB 0x1

#define BRCMSTB_PRIV_FLAGS_NO_64BIT BIT(0)
#define BRCMSTB_PRIV_FLAGS_BROKEN_TIMEOUT BIT(1)

#define SDHCI_ARASAN_CQE_BASE_ADDR 0x200

struct sdhci_brcmstb_priv {
void __iomem *cfg_regs;
bool has_cqe;
};

struct brcmstb_match_priv {
void (*hs400es)(struct mmc_host *mmc, struct mmc_ios *ios);
struct sdhci_ops *ops;
unsigned int flags;
};

Expand All @@ -44,28 +50,74 @@ static void sdhci_brcmstb_hs400es(struct mmc_host *mmc, struct mmc_ios *ios)
writel(reg, host->ioaddr + SDHCI_VENDOR);
}

static const struct sdhci_ops sdhci_brcmstb_ops = {
static void sdhci_brcmstb_set_clock(struct sdhci_host *host, unsigned int clock)
{
u16 clk;

host->mmc->actual_clock = 0;

clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock);
sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL);

if (clock == 0)
return;

sdhci_enable_clk(host, clk);
}

static void sdhci_brcmstb_dumpregs(struct mmc_host *mmc)
{
sdhci_dumpregs(mmc_priv(mmc));
}

static void sdhci_brcmstb_cqe_enable(struct mmc_host *mmc)
{
struct sdhci_host *host = mmc_priv(mmc);
u32 reg;

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_cqe_enable(mmc);
}

static const struct cqhci_host_ops sdhci_brcmstb_cqhci_ops = {
.enable = sdhci_brcmstb_cqe_enable,
.disable = sdhci_cqe_disable,
.dumpregs = sdhci_brcmstb_dumpregs,
};

static struct sdhci_ops sdhci_brcmstb_ops = {
.set_clock = sdhci_set_clock,
.set_bus_width = sdhci_set_bus_width,
.reset = sdhci_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
};

static const struct sdhci_pltfm_data sdhci_brcmstb_pdata = {
.ops = &sdhci_brcmstb_ops,
static struct sdhci_ops sdhci_brcmstb_ops_7216 = {
.set_clock = sdhci_brcmstb_set_clock,
.set_bus_width = sdhci_set_bus_width,
.reset = sdhci_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
};

static const struct brcmstb_match_priv match_priv_7425 = {
static struct brcmstb_match_priv match_priv_7425 = {
.flags = BRCMSTB_PRIV_FLAGS_NO_64BIT |
BRCMSTB_PRIV_FLAGS_BROKEN_TIMEOUT,
.ops = &sdhci_brcmstb_ops,
};

static const struct brcmstb_match_priv match_priv_7445 = {
static struct brcmstb_match_priv match_priv_7445 = {
.flags = BRCMSTB_PRIV_FLAGS_BROKEN_TIMEOUT,
.ops = &sdhci_brcmstb_ops,
};

static const struct brcmstb_match_priv match_priv_7216 = {
.hs400es = sdhci_brcmstb_hs400es,
.ops = &sdhci_brcmstb_ops_7216,
};

static const struct of_device_id sdhci_brcm_of_match[] = {
Expand All @@ -75,20 +127,85 @@ static const struct of_device_id sdhci_brcm_of_match[] = {
{},
};

static u32 sdhci_brcmstb_cqhci_irq(struct sdhci_host *host, u32 intmask)
{
int cmd_error = 0;
int data_error = 0;

if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
return intmask;

cqhci_irq(host->mmc, intmask, cmd_error, data_error);

return 0;
}

static int sdhci_brcmstb_add_host(struct sdhci_host *host,
struct sdhci_brcmstb_priv *priv)
{
struct cqhci_host *cq_host;
bool dma64;
int ret;

if (!priv->has_cqe)
return sdhci_add_host(host);

dev_dbg(mmc_dev(host->mmc), "CQE is enabled\n");
host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD;
ret = sdhci_setup_host(host);
if (ret)
return ret;

cq_host = devm_kzalloc(mmc_dev(host->mmc),
sizeof(*cq_host), GFP_KERNEL);
if (!cq_host) {
ret = -ENOMEM;
goto cleanup;
}

cq_host->mmio = host->ioaddr + SDHCI_ARASAN_CQE_BASE_ADDR;
cq_host->ops = &sdhci_brcmstb_cqhci_ops;

dma64 = host->flags & SDHCI_USE_64_BIT_DMA;
if (dma64) {
dev_dbg(mmc_dev(host->mmc), "Using 64 bit DMA\n");
cq_host->caps |= CQHCI_TASK_DESC_SZ_128;
cq_host->quirks |= CQHCI_QUIRK_SHORT_TXFR_DESC_SZ;
}

ret = cqhci_init(cq_host, host->mmc, dma64);
if (ret)
goto cleanup;

ret = __sdhci_add_host(host);
if (ret)
goto cleanup;

return 0;

cleanup:
sdhci_cleanup_host(host);
return ret;
}

static int sdhci_brcmstb_probe(struct platform_device *pdev)
{
const struct brcmstb_match_priv *match_priv;
struct sdhci_pltfm_data brcmstb_pdata;
struct sdhci_pltfm_host *pltfm_host;
const struct of_device_id *match;
struct sdhci_brcmstb_priv *priv;
struct sdhci_host *host;
struct resource *iomem;
bool has_cqe = false;
struct clk *clk;
int res;

match = of_match_node(sdhci_brcm_of_match, pdev->dev.of_node);
match_priv = match->data;

dev_dbg(&pdev->dev, "Probe found match for %s\n", match->compatible);

clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(clk)) {
if (PTR_ERR(clk) == -EPROBE_DEFER)
Expand All @@ -100,7 +217,13 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
if (res)
return res;

host = sdhci_pltfm_init(pdev, &sdhci_brcmstb_pdata,
memset(&brcmstb_pdata, 0, sizeof(brcmstb_pdata));
if (device_property_read_bool(&pdev->dev, "supports-cqe")) {
has_cqe = true;
match_priv->ops->irq = sdhci_brcmstb_cqhci_irq;
}
brcmstb_pdata.ops = match_priv->ops;
host = sdhci_pltfm_init(pdev, &brcmstb_pdata,
sizeof(struct sdhci_brcmstb_priv));
if (IS_ERR(host)) {
res = PTR_ERR(host);
Expand All @@ -109,6 +232,7 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)

pltfm_host = sdhci_priv(host);
priv = sdhci_pltfm_priv(pltfm_host);
priv->has_cqe = has_cqe;

/* Map in the non-standard CFG registers */
iomem = platform_get_resource(pdev, IORESOURCE_MEM, 1);
Expand Down Expand Up @@ -141,13 +265,13 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev)
host->caps &= ~SDHCI_CAN_64BIT;
host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1);
host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 |
SDHCI_SUPPORT_DDR50);
SDHCI_SUPPORT_DDR50);
host->quirks |= SDHCI_QUIRK_MISSING_CAPS;

if (match_priv->flags & BRCMSTB_PRIV_FLAGS_BROKEN_TIMEOUT)
host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL;

res = sdhci_add_host(host);
res = sdhci_brcmstb_add_host(host, priv);
if (res)
goto err;

Expand Down

0 comments on commit d46ba2d

Please sign in to comment.