162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * sdhci-brcmstb.c Support for SDHCI on Broadcom BRCMSTB SoC's 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2015 Broadcom Corporation 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/io.h> 962306a36Sopenharmony_ci#include <linux/mmc/host.h> 1062306a36Sopenharmony_ci#include <linux/module.h> 1162306a36Sopenharmony_ci#include <linux/of.h> 1262306a36Sopenharmony_ci#include <linux/bitops.h> 1362306a36Sopenharmony_ci#include <linux/delay.h> 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#include "sdhci-cqhci.h" 1662306a36Sopenharmony_ci#include "sdhci-pltfm.h" 1762306a36Sopenharmony_ci#include "cqhci.h" 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define SDHCI_VENDOR 0x78 2062306a36Sopenharmony_ci#define SDHCI_VENDOR_ENHANCED_STRB 0x1 2162306a36Sopenharmony_ci#define SDHCI_VENDOR_GATE_SDCLK_EN 0x2 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci#define BRCMSTB_MATCH_FLAGS_NO_64BIT BIT(0) 2462306a36Sopenharmony_ci#define BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT BIT(1) 2562306a36Sopenharmony_ci#define BRCMSTB_MATCH_FLAGS_HAS_CLOCK_GATE BIT(2) 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#define BRCMSTB_PRIV_FLAGS_HAS_CQE BIT(0) 2862306a36Sopenharmony_ci#define BRCMSTB_PRIV_FLAGS_GATE_CLOCK BIT(1) 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define SDHCI_ARASAN_CQE_BASE_ADDR 0x200 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_cistruct sdhci_brcmstb_priv { 3362306a36Sopenharmony_ci void __iomem *cfg_regs; 3462306a36Sopenharmony_ci unsigned int flags; 3562306a36Sopenharmony_ci struct clk *base_clk; 3662306a36Sopenharmony_ci u32 base_freq_hz; 3762306a36Sopenharmony_ci}; 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_cistruct brcmstb_match_priv { 4062306a36Sopenharmony_ci void (*hs400es)(struct mmc_host *mmc, struct mmc_ios *ios); 4162306a36Sopenharmony_ci struct sdhci_ops *ops; 4262306a36Sopenharmony_ci const unsigned int flags; 4362306a36Sopenharmony_ci}; 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic inline void enable_clock_gating(struct sdhci_host *host) 4662306a36Sopenharmony_ci{ 4762306a36Sopenharmony_ci u32 reg; 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci reg = sdhci_readl(host, SDHCI_VENDOR); 5062306a36Sopenharmony_ci reg |= SDHCI_VENDOR_GATE_SDCLK_EN; 5162306a36Sopenharmony_ci sdhci_writel(host, reg, SDHCI_VENDOR); 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic void brcmstb_reset(struct sdhci_host *host, u8 mask) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 5762306a36Sopenharmony_ci struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host); 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci sdhci_and_cqhci_reset(host, mask); 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci /* Reset will clear this, so re-enable it */ 6262306a36Sopenharmony_ci if (priv->flags & BRCMSTB_PRIV_FLAGS_GATE_CLOCK) 6362306a36Sopenharmony_ci enable_clock_gating(host); 6462306a36Sopenharmony_ci} 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_cistatic void sdhci_brcmstb_hs400es(struct mmc_host *mmc, struct mmc_ios *ios) 6762306a36Sopenharmony_ci{ 6862306a36Sopenharmony_ci struct sdhci_host *host = mmc_priv(mmc); 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci u32 reg; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci dev_dbg(mmc_dev(mmc), "%s(): Setting HS400-Enhanced-Strobe mode\n", 7362306a36Sopenharmony_ci __func__); 7462306a36Sopenharmony_ci reg = readl(host->ioaddr + SDHCI_VENDOR); 7562306a36Sopenharmony_ci if (ios->enhanced_strobe) 7662306a36Sopenharmony_ci reg |= SDHCI_VENDOR_ENHANCED_STRB; 7762306a36Sopenharmony_ci else 7862306a36Sopenharmony_ci reg &= ~SDHCI_VENDOR_ENHANCED_STRB; 7962306a36Sopenharmony_ci writel(reg, host->ioaddr + SDHCI_VENDOR); 8062306a36Sopenharmony_ci} 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_cistatic void sdhci_brcmstb_set_clock(struct sdhci_host *host, unsigned int clock) 8362306a36Sopenharmony_ci{ 8462306a36Sopenharmony_ci u16 clk; 8562306a36Sopenharmony_ci 8662306a36Sopenharmony_ci host->mmc->actual_clock = 0; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci clk = sdhci_calc_clk(host, clock, &host->mmc->actual_clock); 8962306a36Sopenharmony_ci sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (clock == 0) 9262306a36Sopenharmony_ci return; 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci sdhci_enable_clk(host, clk); 9562306a36Sopenharmony_ci} 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_cistatic void sdhci_brcmstb_set_uhs_signaling(struct sdhci_host *host, 9862306a36Sopenharmony_ci unsigned int timing) 9962306a36Sopenharmony_ci{ 10062306a36Sopenharmony_ci u16 ctrl_2; 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci dev_dbg(mmc_dev(host->mmc), "%s: Setting UHS signaling for %d timing\n", 10362306a36Sopenharmony_ci __func__, timing); 10462306a36Sopenharmony_ci ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); 10562306a36Sopenharmony_ci /* Select Bus Speed Mode for host */ 10662306a36Sopenharmony_ci ctrl_2 &= ~SDHCI_CTRL_UHS_MASK; 10762306a36Sopenharmony_ci if ((timing == MMC_TIMING_MMC_HS200) || 10862306a36Sopenharmony_ci (timing == MMC_TIMING_UHS_SDR104)) 10962306a36Sopenharmony_ci ctrl_2 |= SDHCI_CTRL_UHS_SDR104; 11062306a36Sopenharmony_ci else if (timing == MMC_TIMING_UHS_SDR12) 11162306a36Sopenharmony_ci ctrl_2 |= SDHCI_CTRL_UHS_SDR12; 11262306a36Sopenharmony_ci else if (timing == MMC_TIMING_SD_HS || 11362306a36Sopenharmony_ci timing == MMC_TIMING_MMC_HS || 11462306a36Sopenharmony_ci timing == MMC_TIMING_UHS_SDR25) 11562306a36Sopenharmony_ci ctrl_2 |= SDHCI_CTRL_UHS_SDR25; 11662306a36Sopenharmony_ci else if (timing == MMC_TIMING_UHS_SDR50) 11762306a36Sopenharmony_ci ctrl_2 |= SDHCI_CTRL_UHS_SDR50; 11862306a36Sopenharmony_ci else if ((timing == MMC_TIMING_UHS_DDR50) || 11962306a36Sopenharmony_ci (timing == MMC_TIMING_MMC_DDR52)) 12062306a36Sopenharmony_ci ctrl_2 |= SDHCI_CTRL_UHS_DDR50; 12162306a36Sopenharmony_ci else if (timing == MMC_TIMING_MMC_HS400) 12262306a36Sopenharmony_ci ctrl_2 |= SDHCI_CTRL_HS400; /* Non-standard */ 12362306a36Sopenharmony_ci sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cistatic void sdhci_brcmstb_dumpregs(struct mmc_host *mmc) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci sdhci_dumpregs(mmc_priv(mmc)); 12962306a36Sopenharmony_ci} 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_cistatic void sdhci_brcmstb_cqe_enable(struct mmc_host *mmc) 13262306a36Sopenharmony_ci{ 13362306a36Sopenharmony_ci struct sdhci_host *host = mmc_priv(mmc); 13462306a36Sopenharmony_ci u32 reg; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci reg = sdhci_readl(host, SDHCI_PRESENT_STATE); 13762306a36Sopenharmony_ci while (reg & SDHCI_DATA_AVAILABLE) { 13862306a36Sopenharmony_ci sdhci_readl(host, SDHCI_BUFFER); 13962306a36Sopenharmony_ci reg = sdhci_readl(host, SDHCI_PRESENT_STATE); 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci sdhci_cqe_enable(mmc); 14362306a36Sopenharmony_ci} 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_cistatic const struct cqhci_host_ops sdhci_brcmstb_cqhci_ops = { 14662306a36Sopenharmony_ci .enable = sdhci_brcmstb_cqe_enable, 14762306a36Sopenharmony_ci .disable = sdhci_cqe_disable, 14862306a36Sopenharmony_ci .dumpregs = sdhci_brcmstb_dumpregs, 14962306a36Sopenharmony_ci}; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_cistatic struct sdhci_ops sdhci_brcmstb_ops = { 15262306a36Sopenharmony_ci .set_clock = sdhci_set_clock, 15362306a36Sopenharmony_ci .set_bus_width = sdhci_set_bus_width, 15462306a36Sopenharmony_ci .reset = sdhci_reset, 15562306a36Sopenharmony_ci .set_uhs_signaling = sdhci_set_uhs_signaling, 15662306a36Sopenharmony_ci}; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_cistatic struct sdhci_ops sdhci_brcmstb_ops_7216 = { 15962306a36Sopenharmony_ci .set_clock = sdhci_brcmstb_set_clock, 16062306a36Sopenharmony_ci .set_bus_width = sdhci_set_bus_width, 16162306a36Sopenharmony_ci .reset = brcmstb_reset, 16262306a36Sopenharmony_ci .set_uhs_signaling = sdhci_brcmstb_set_uhs_signaling, 16362306a36Sopenharmony_ci}; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_cistatic struct brcmstb_match_priv match_priv_7425 = { 16662306a36Sopenharmony_ci .flags = BRCMSTB_MATCH_FLAGS_NO_64BIT | 16762306a36Sopenharmony_ci BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT, 16862306a36Sopenharmony_ci .ops = &sdhci_brcmstb_ops, 16962306a36Sopenharmony_ci}; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic struct brcmstb_match_priv match_priv_7445 = { 17262306a36Sopenharmony_ci .flags = BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT, 17362306a36Sopenharmony_ci .ops = &sdhci_brcmstb_ops, 17462306a36Sopenharmony_ci}; 17562306a36Sopenharmony_ci 17662306a36Sopenharmony_cistatic const struct brcmstb_match_priv match_priv_7216 = { 17762306a36Sopenharmony_ci .flags = BRCMSTB_MATCH_FLAGS_HAS_CLOCK_GATE, 17862306a36Sopenharmony_ci .hs400es = sdhci_brcmstb_hs400es, 17962306a36Sopenharmony_ci .ops = &sdhci_brcmstb_ops_7216, 18062306a36Sopenharmony_ci}; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_cistatic const struct of_device_id __maybe_unused sdhci_brcm_of_match[] = { 18362306a36Sopenharmony_ci { .compatible = "brcm,bcm7425-sdhci", .data = &match_priv_7425 }, 18462306a36Sopenharmony_ci { .compatible = "brcm,bcm7445-sdhci", .data = &match_priv_7445 }, 18562306a36Sopenharmony_ci { .compatible = "brcm,bcm7216-sdhci", .data = &match_priv_7216 }, 18662306a36Sopenharmony_ci {}, 18762306a36Sopenharmony_ci}; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic u32 sdhci_brcmstb_cqhci_irq(struct sdhci_host *host, u32 intmask) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci int cmd_error = 0; 19262306a36Sopenharmony_ci int data_error = 0; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error)) 19562306a36Sopenharmony_ci return intmask; 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci cqhci_irq(host->mmc, intmask, cmd_error, data_error); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci return 0; 20062306a36Sopenharmony_ci} 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_cistatic int sdhci_brcmstb_add_host(struct sdhci_host *host, 20362306a36Sopenharmony_ci struct sdhci_brcmstb_priv *priv) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci struct cqhci_host *cq_host; 20662306a36Sopenharmony_ci bool dma64; 20762306a36Sopenharmony_ci int ret; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci if ((priv->flags & BRCMSTB_PRIV_FLAGS_HAS_CQE) == 0) 21062306a36Sopenharmony_ci return sdhci_add_host(host); 21162306a36Sopenharmony_ci 21262306a36Sopenharmony_ci dev_dbg(mmc_dev(host->mmc), "CQE is enabled\n"); 21362306a36Sopenharmony_ci host->mmc->caps2 |= MMC_CAP2_CQE | MMC_CAP2_CQE_DCMD; 21462306a36Sopenharmony_ci ret = sdhci_setup_host(host); 21562306a36Sopenharmony_ci if (ret) 21662306a36Sopenharmony_ci return ret; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci cq_host = devm_kzalloc(mmc_dev(host->mmc), 21962306a36Sopenharmony_ci sizeof(*cq_host), GFP_KERNEL); 22062306a36Sopenharmony_ci if (!cq_host) { 22162306a36Sopenharmony_ci ret = -ENOMEM; 22262306a36Sopenharmony_ci goto cleanup; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci cq_host->mmio = host->ioaddr + SDHCI_ARASAN_CQE_BASE_ADDR; 22662306a36Sopenharmony_ci cq_host->ops = &sdhci_brcmstb_cqhci_ops; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci dma64 = host->flags & SDHCI_USE_64_BIT_DMA; 22962306a36Sopenharmony_ci if (dma64) { 23062306a36Sopenharmony_ci dev_dbg(mmc_dev(host->mmc), "Using 64 bit DMA\n"); 23162306a36Sopenharmony_ci cq_host->caps |= CQHCI_TASK_DESC_SZ_128; 23262306a36Sopenharmony_ci } 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci ret = cqhci_init(cq_host, host->mmc, dma64); 23562306a36Sopenharmony_ci if (ret) 23662306a36Sopenharmony_ci goto cleanup; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci ret = __sdhci_add_host(host); 23962306a36Sopenharmony_ci if (ret) 24062306a36Sopenharmony_ci goto cleanup; 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci return 0; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_cicleanup: 24562306a36Sopenharmony_ci sdhci_cleanup_host(host); 24662306a36Sopenharmony_ci return ret; 24762306a36Sopenharmony_ci} 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cistatic int sdhci_brcmstb_probe(struct platform_device *pdev) 25062306a36Sopenharmony_ci{ 25162306a36Sopenharmony_ci const struct brcmstb_match_priv *match_priv; 25262306a36Sopenharmony_ci struct sdhci_pltfm_data brcmstb_pdata; 25362306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host; 25462306a36Sopenharmony_ci const struct of_device_id *match; 25562306a36Sopenharmony_ci struct sdhci_brcmstb_priv *priv; 25662306a36Sopenharmony_ci u32 actual_clock_mhz; 25762306a36Sopenharmony_ci struct sdhci_host *host; 25862306a36Sopenharmony_ci struct clk *clk; 25962306a36Sopenharmony_ci struct clk *base_clk = NULL; 26062306a36Sopenharmony_ci int res; 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci match = of_match_node(sdhci_brcm_of_match, pdev->dev.of_node); 26362306a36Sopenharmony_ci match_priv = match->data; 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Probe found match for %s\n", match->compatible); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci clk = devm_clk_get_optional_enabled(&pdev->dev, NULL); 26862306a36Sopenharmony_ci if (IS_ERR(clk)) 26962306a36Sopenharmony_ci return dev_err_probe(&pdev->dev, PTR_ERR(clk), 27062306a36Sopenharmony_ci "Failed to get and enable clock from Device Tree\n"); 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci memset(&brcmstb_pdata, 0, sizeof(brcmstb_pdata)); 27362306a36Sopenharmony_ci brcmstb_pdata.ops = match_priv->ops; 27462306a36Sopenharmony_ci host = sdhci_pltfm_init(pdev, &brcmstb_pdata, 27562306a36Sopenharmony_ci sizeof(struct sdhci_brcmstb_priv)); 27662306a36Sopenharmony_ci if (IS_ERR(host)) 27762306a36Sopenharmony_ci return PTR_ERR(host); 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci pltfm_host = sdhci_priv(host); 28062306a36Sopenharmony_ci priv = sdhci_pltfm_priv(pltfm_host); 28162306a36Sopenharmony_ci if (device_property_read_bool(&pdev->dev, "supports-cqe")) { 28262306a36Sopenharmony_ci priv->flags |= BRCMSTB_PRIV_FLAGS_HAS_CQE; 28362306a36Sopenharmony_ci match_priv->ops->irq = sdhci_brcmstb_cqhci_irq; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci /* Map in the non-standard CFG registers */ 28762306a36Sopenharmony_ci priv->cfg_regs = devm_platform_get_and_ioremap_resource(pdev, 1, NULL); 28862306a36Sopenharmony_ci if (IS_ERR(priv->cfg_regs)) { 28962306a36Sopenharmony_ci res = PTR_ERR(priv->cfg_regs); 29062306a36Sopenharmony_ci goto err; 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci sdhci_get_of_property(pdev); 29462306a36Sopenharmony_ci res = mmc_of_parse(host->mmc); 29562306a36Sopenharmony_ci if (res) 29662306a36Sopenharmony_ci goto err; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci /* 29962306a36Sopenharmony_ci * Automatic clock gating does not work for SD cards that may 30062306a36Sopenharmony_ci * voltage switch so only enable it for non-removable devices. 30162306a36Sopenharmony_ci */ 30262306a36Sopenharmony_ci if ((match_priv->flags & BRCMSTB_MATCH_FLAGS_HAS_CLOCK_GATE) && 30362306a36Sopenharmony_ci (host->mmc->caps & MMC_CAP_NONREMOVABLE)) 30462306a36Sopenharmony_ci priv->flags |= BRCMSTB_PRIV_FLAGS_GATE_CLOCK; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* 30762306a36Sopenharmony_ci * If the chip has enhanced strobe and it's enabled, add 30862306a36Sopenharmony_ci * callback 30962306a36Sopenharmony_ci */ 31062306a36Sopenharmony_ci if (match_priv->hs400es && 31162306a36Sopenharmony_ci (host->mmc->caps2 & MMC_CAP2_HS400_ES)) 31262306a36Sopenharmony_ci host->mmc_host_ops.hs400_enhanced_strobe = match_priv->hs400es; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci /* 31562306a36Sopenharmony_ci * Supply the existing CAPS, but clear the UHS modes. This 31662306a36Sopenharmony_ci * will allow these modes to be specified by device tree 31762306a36Sopenharmony_ci * properties through mmc_of_parse(). 31862306a36Sopenharmony_ci */ 31962306a36Sopenharmony_ci sdhci_read_caps(host); 32062306a36Sopenharmony_ci if (match_priv->flags & BRCMSTB_MATCH_FLAGS_NO_64BIT) 32162306a36Sopenharmony_ci host->caps &= ~SDHCI_CAN_64BIT; 32262306a36Sopenharmony_ci host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 | 32362306a36Sopenharmony_ci SDHCI_SUPPORT_DDR50); 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci if (match_priv->flags & BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT) 32662306a36Sopenharmony_ci host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; 32762306a36Sopenharmony_ci 32862306a36Sopenharmony_ci /* Change the base clock frequency if the DT property exists */ 32962306a36Sopenharmony_ci if (device_property_read_u32(&pdev->dev, "clock-frequency", 33062306a36Sopenharmony_ci &priv->base_freq_hz) != 0) 33162306a36Sopenharmony_ci goto add_host; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci base_clk = devm_clk_get_optional(&pdev->dev, "sdio_freq"); 33462306a36Sopenharmony_ci if (IS_ERR(base_clk)) { 33562306a36Sopenharmony_ci dev_warn(&pdev->dev, "Clock for \"sdio_freq\" not found\n"); 33662306a36Sopenharmony_ci goto add_host; 33762306a36Sopenharmony_ci } 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci res = clk_prepare_enable(base_clk); 34062306a36Sopenharmony_ci if (res) 34162306a36Sopenharmony_ci goto err; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci /* set improved clock rate */ 34462306a36Sopenharmony_ci clk_set_rate(base_clk, priv->base_freq_hz); 34562306a36Sopenharmony_ci actual_clock_mhz = clk_get_rate(base_clk) / 1000000; 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_ci host->caps &= ~SDHCI_CLOCK_V3_BASE_MASK; 34862306a36Sopenharmony_ci host->caps |= (actual_clock_mhz << SDHCI_CLOCK_BASE_SHIFT); 34962306a36Sopenharmony_ci /* Disable presets because they are now incorrect */ 35062306a36Sopenharmony_ci host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci dev_dbg(&pdev->dev, "Base Clock Frequency changed to %dMHz\n", 35362306a36Sopenharmony_ci actual_clock_mhz); 35462306a36Sopenharmony_ci priv->base_clk = base_clk; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ciadd_host: 35762306a36Sopenharmony_ci res = sdhci_brcmstb_add_host(host, priv); 35862306a36Sopenharmony_ci if (res) 35962306a36Sopenharmony_ci goto err; 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_ci pltfm_host->clk = clk; 36262306a36Sopenharmony_ci return res; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_cierr: 36562306a36Sopenharmony_ci sdhci_pltfm_free(pdev); 36662306a36Sopenharmony_ci clk_disable_unprepare(base_clk); 36762306a36Sopenharmony_ci return res; 36862306a36Sopenharmony_ci} 36962306a36Sopenharmony_ci 37062306a36Sopenharmony_cistatic void sdhci_brcmstb_shutdown(struct platform_device *pdev) 37162306a36Sopenharmony_ci{ 37262306a36Sopenharmony_ci sdhci_pltfm_suspend(&pdev->dev); 37362306a36Sopenharmony_ci} 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, sdhci_brcm_of_match); 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 37862306a36Sopenharmony_cistatic int sdhci_brcmstb_suspend(struct device *dev) 37962306a36Sopenharmony_ci{ 38062306a36Sopenharmony_ci struct sdhci_host *host = dev_get_drvdata(dev); 38162306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 38262306a36Sopenharmony_ci struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host); 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci clk_disable_unprepare(priv->base_clk); 38562306a36Sopenharmony_ci return sdhci_pltfm_suspend(dev); 38662306a36Sopenharmony_ci} 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_cistatic int sdhci_brcmstb_resume(struct device *dev) 38962306a36Sopenharmony_ci{ 39062306a36Sopenharmony_ci struct sdhci_host *host = dev_get_drvdata(dev); 39162306a36Sopenharmony_ci struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 39262306a36Sopenharmony_ci struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host); 39362306a36Sopenharmony_ci int ret; 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci ret = sdhci_pltfm_resume(dev); 39662306a36Sopenharmony_ci if (!ret && priv->base_freq_hz) { 39762306a36Sopenharmony_ci ret = clk_prepare_enable(priv->base_clk); 39862306a36Sopenharmony_ci /* 39962306a36Sopenharmony_ci * Note: using clk_get_rate() below as clk_get_rate() 40062306a36Sopenharmony_ci * honors CLK_GET_RATE_NOCACHE attribute, but clk_set_rate() 40162306a36Sopenharmony_ci * may do implicit get_rate() calls that do not honor 40262306a36Sopenharmony_ci * CLK_GET_RATE_NOCACHE. 40362306a36Sopenharmony_ci */ 40462306a36Sopenharmony_ci if (!ret && 40562306a36Sopenharmony_ci (clk_get_rate(priv->base_clk) != priv->base_freq_hz)) 40662306a36Sopenharmony_ci ret = clk_set_rate(priv->base_clk, priv->base_freq_hz); 40762306a36Sopenharmony_ci } 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return ret; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci#endif 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_cistatic const struct dev_pm_ops sdhci_brcmstb_pmops = { 41462306a36Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(sdhci_brcmstb_suspend, sdhci_brcmstb_resume) 41562306a36Sopenharmony_ci}; 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic struct platform_driver sdhci_brcmstb_driver = { 41862306a36Sopenharmony_ci .driver = { 41962306a36Sopenharmony_ci .name = "sdhci-brcmstb", 42062306a36Sopenharmony_ci .probe_type = PROBE_PREFER_ASYNCHRONOUS, 42162306a36Sopenharmony_ci .pm = &sdhci_brcmstb_pmops, 42262306a36Sopenharmony_ci .of_match_table = of_match_ptr(sdhci_brcm_of_match), 42362306a36Sopenharmony_ci }, 42462306a36Sopenharmony_ci .probe = sdhci_brcmstb_probe, 42562306a36Sopenharmony_ci .remove_new = sdhci_pltfm_remove, 42662306a36Sopenharmony_ci .shutdown = sdhci_brcmstb_shutdown, 42762306a36Sopenharmony_ci}; 42862306a36Sopenharmony_ci 42962306a36Sopenharmony_cimodule_platform_driver(sdhci_brcmstb_driver); 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ciMODULE_DESCRIPTION("SDHCI driver for Broadcom BRCMSTB SoCs"); 43262306a36Sopenharmony_ciMODULE_AUTHOR("Broadcom"); 43362306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 434