11802d0beSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2476bf3d6SAl Cooper /* 3476bf3d6SAl Cooper * sdhci-brcmstb.c Support for SDHCI on Broadcom BRCMSTB SoC's 4476bf3d6SAl Cooper * 5476bf3d6SAl Cooper * Copyright (C) 2015 Broadcom Corporation 6476bf3d6SAl Cooper */ 7476bf3d6SAl Cooper 8476bf3d6SAl Cooper #include <linux/io.h> 9476bf3d6SAl Cooper #include <linux/mmc/host.h> 10476bf3d6SAl Cooper #include <linux/module.h> 11476bf3d6SAl Cooper #include <linux/of.h> 1278ab82fdSAl Cooper #include <linux/bitops.h> 13476bf3d6SAl Cooper 14476bf3d6SAl Cooper #include "sdhci-pltfm.h" 15476bf3d6SAl Cooper 1678ab82fdSAl Cooper #define SDHCI_VENDOR 0x78 1778ab82fdSAl Cooper #define SDHCI_VENDOR_ENHANCED_STRB 0x1 1878ab82fdSAl Cooper 1978ab82fdSAl Cooper #define BRCMSTB_PRIV_FLAGS_NO_64BIT BIT(0) 2078ab82fdSAl Cooper #define BRCMSTB_PRIV_FLAGS_BROKEN_TIMEOUT BIT(1) 2178ab82fdSAl Cooper 2278ab82fdSAl Cooper struct sdhci_brcmstb_priv { 2378ab82fdSAl Cooper void __iomem *cfg_regs; 2478ab82fdSAl Cooper }; 2578ab82fdSAl Cooper 2678ab82fdSAl Cooper struct brcmstb_match_priv { 2778ab82fdSAl Cooper void (*hs400es)(struct mmc_host *mmc, struct mmc_ios *ios); 2878ab82fdSAl Cooper unsigned int flags; 2978ab82fdSAl Cooper }; 3078ab82fdSAl Cooper 3178ab82fdSAl Cooper static void sdhci_brcmstb_hs400es(struct mmc_host *mmc, struct mmc_ios *ios) 3278ab82fdSAl Cooper { 3378ab82fdSAl Cooper struct sdhci_host *host = mmc_priv(mmc); 3478ab82fdSAl Cooper 3578ab82fdSAl Cooper u32 reg; 3678ab82fdSAl Cooper 3778ab82fdSAl Cooper dev_dbg(mmc_dev(mmc), "%s(): Setting HS400-Enhanced-Strobe mode\n", 3878ab82fdSAl Cooper __func__); 3978ab82fdSAl Cooper reg = readl(host->ioaddr + SDHCI_VENDOR); 4078ab82fdSAl Cooper if (ios->enhanced_strobe) 4178ab82fdSAl Cooper reg |= SDHCI_VENDOR_ENHANCED_STRB; 4278ab82fdSAl Cooper else 4378ab82fdSAl Cooper reg &= ~SDHCI_VENDOR_ENHANCED_STRB; 4478ab82fdSAl Cooper writel(reg, host->ioaddr + SDHCI_VENDOR); 4578ab82fdSAl Cooper } 4678ab82fdSAl Cooper 47476bf3d6SAl Cooper static const struct sdhci_ops sdhci_brcmstb_ops = { 48476bf3d6SAl Cooper .set_clock = sdhci_set_clock, 49476bf3d6SAl Cooper .set_bus_width = sdhci_set_bus_width, 50476bf3d6SAl Cooper .reset = sdhci_reset, 51476bf3d6SAl Cooper .set_uhs_signaling = sdhci_set_uhs_signaling, 52476bf3d6SAl Cooper }; 53476bf3d6SAl Cooper 5436719676SJulia Lawall static const struct sdhci_pltfm_data sdhci_brcmstb_pdata = { 55476bf3d6SAl Cooper .ops = &sdhci_brcmstb_ops, 56476bf3d6SAl Cooper }; 57476bf3d6SAl Cooper 5878ab82fdSAl Cooper static const struct brcmstb_match_priv match_priv_7425 = { 5978ab82fdSAl Cooper .flags = BRCMSTB_PRIV_FLAGS_NO_64BIT | 6078ab82fdSAl Cooper BRCMSTB_PRIV_FLAGS_BROKEN_TIMEOUT, 6178ab82fdSAl Cooper }; 6278ab82fdSAl Cooper 6378ab82fdSAl Cooper static const struct brcmstb_match_priv match_priv_7445 = { 6478ab82fdSAl Cooper .flags = BRCMSTB_PRIV_FLAGS_BROKEN_TIMEOUT, 6578ab82fdSAl Cooper }; 6678ab82fdSAl Cooper 6778ab82fdSAl Cooper static const struct brcmstb_match_priv match_priv_7216 = { 6878ab82fdSAl Cooper .hs400es = sdhci_brcmstb_hs400es, 6978ab82fdSAl Cooper }; 7078ab82fdSAl Cooper 7178ab82fdSAl Cooper static const struct of_device_id sdhci_brcm_of_match[] = { 7278ab82fdSAl Cooper { .compatible = "brcm,bcm7425-sdhci", .data = &match_priv_7425 }, 7378ab82fdSAl Cooper { .compatible = "brcm,bcm7445-sdhci", .data = &match_priv_7445 }, 7478ab82fdSAl Cooper { .compatible = "brcm,bcm7216-sdhci", .data = &match_priv_7216 }, 7578ab82fdSAl Cooper {}, 7678ab82fdSAl Cooper }; 7778ab82fdSAl Cooper 78476bf3d6SAl Cooper static int sdhci_brcmstb_probe(struct platform_device *pdev) 79476bf3d6SAl Cooper { 8078ab82fdSAl Cooper const struct brcmstb_match_priv *match_priv; 81476bf3d6SAl Cooper struct sdhci_pltfm_host *pltfm_host; 8278ab82fdSAl Cooper const struct of_device_id *match; 8378ab82fdSAl Cooper struct sdhci_brcmstb_priv *priv; 8478ab82fdSAl Cooper struct sdhci_host *host; 8578ab82fdSAl Cooper struct resource *iomem; 86476bf3d6SAl Cooper struct clk *clk; 87476bf3d6SAl Cooper int res; 88476bf3d6SAl Cooper 8978ab82fdSAl Cooper match = of_match_node(sdhci_brcm_of_match, pdev->dev.of_node); 9078ab82fdSAl Cooper match_priv = match->data; 9178ab82fdSAl Cooper 92476bf3d6SAl Cooper clk = devm_clk_get(&pdev->dev, NULL); 93476bf3d6SAl Cooper if (IS_ERR(clk)) { 94476bf3d6SAl Cooper dev_err(&pdev->dev, "Clock not found in Device Tree\n"); 95476bf3d6SAl Cooper clk = NULL; 96476bf3d6SAl Cooper } 97476bf3d6SAl Cooper res = clk_prepare_enable(clk); 98476bf3d6SAl Cooper if (res) 99476bf3d6SAl Cooper return res; 100476bf3d6SAl Cooper 10178ab82fdSAl Cooper host = sdhci_pltfm_init(pdev, &sdhci_brcmstb_pdata, 10278ab82fdSAl Cooper sizeof(struct sdhci_brcmstb_priv)); 103476bf3d6SAl Cooper if (IS_ERR(host)) { 104476bf3d6SAl Cooper res = PTR_ERR(host); 105476bf3d6SAl Cooper goto err_clk; 106476bf3d6SAl Cooper } 107476bf3d6SAl Cooper 10878ab82fdSAl Cooper pltfm_host = sdhci_priv(host); 10978ab82fdSAl Cooper priv = sdhci_pltfm_priv(pltfm_host); 11078ab82fdSAl Cooper 11178ab82fdSAl Cooper /* Map in the non-standard CFG registers */ 11278ab82fdSAl Cooper iomem = platform_get_resource(pdev, IORESOURCE_MEM, 1); 11378ab82fdSAl Cooper priv->cfg_regs = devm_ioremap_resource(&pdev->dev, iomem); 11478ab82fdSAl Cooper if (IS_ERR(priv->cfg_regs)) { 11578ab82fdSAl Cooper res = PTR_ERR(priv->cfg_regs); 11678ab82fdSAl Cooper goto err; 11778ab82fdSAl Cooper } 11878ab82fdSAl Cooper 119476bf3d6SAl Cooper sdhci_get_of_property(pdev); 1201e20186eSStefan Wahren res = mmc_of_parse(host->mmc); 1211e20186eSStefan Wahren if (res) 1221e20186eSStefan Wahren goto err; 123476bf3d6SAl Cooper 124476bf3d6SAl Cooper /* 12578ab82fdSAl Cooper * If the chip has enhanced strobe and it's enabled, add 12678ab82fdSAl Cooper * callback 12778ab82fdSAl Cooper */ 12878ab82fdSAl Cooper if (match_priv->hs400es && 12978ab82fdSAl Cooper (host->mmc->caps2 & MMC_CAP2_HS400_ES)) 13078ab82fdSAl Cooper host->mmc_host_ops.hs400_enhanced_strobe = match_priv->hs400es; 13178ab82fdSAl Cooper 13278ab82fdSAl Cooper /* 133476bf3d6SAl Cooper * Supply the existing CAPS, but clear the UHS modes. This 134476bf3d6SAl Cooper * will allow these modes to be specified by device tree 135476bf3d6SAl Cooper * properties through mmc_of_parse(). 136476bf3d6SAl Cooper */ 137476bf3d6SAl Cooper host->caps = sdhci_readl(host, SDHCI_CAPABILITIES); 13878ab82fdSAl Cooper if (match_priv->flags & BRCMSTB_PRIV_FLAGS_NO_64BIT) 1396a3d8cedSJaedon Shin host->caps &= ~SDHCI_CAN_64BIT; 140476bf3d6SAl Cooper host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1); 141476bf3d6SAl Cooper host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 | 142476bf3d6SAl Cooper SDHCI_SUPPORT_DDR50); 14378ab82fdSAl Cooper host->quirks |= SDHCI_QUIRK_MISSING_CAPS; 14478ab82fdSAl Cooper 14578ab82fdSAl Cooper if (match_priv->flags & BRCMSTB_PRIV_FLAGS_BROKEN_TIMEOUT) 14678ab82fdSAl Cooper host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; 147476bf3d6SAl Cooper 148476bf3d6SAl Cooper res = sdhci_add_host(host); 149476bf3d6SAl Cooper if (res) 150476bf3d6SAl Cooper goto err; 151476bf3d6SAl Cooper 152476bf3d6SAl Cooper pltfm_host->clk = clk; 153476bf3d6SAl Cooper return res; 154476bf3d6SAl Cooper 155476bf3d6SAl Cooper err: 156476bf3d6SAl Cooper sdhci_pltfm_free(pdev); 157476bf3d6SAl Cooper err_clk: 158476bf3d6SAl Cooper clk_disable_unprepare(clk); 159476bf3d6SAl Cooper return res; 160476bf3d6SAl Cooper } 161476bf3d6SAl Cooper 162476bf3d6SAl Cooper MODULE_DEVICE_TABLE(of, sdhci_brcm_of_match); 163476bf3d6SAl Cooper 164476bf3d6SAl Cooper static struct platform_driver sdhci_brcmstb_driver = { 165476bf3d6SAl Cooper .driver = { 166476bf3d6SAl Cooper .name = "sdhci-brcmstb", 1671ab0d2d7SMasahiro Yamada .pm = &sdhci_pltfm_pmops, 168476bf3d6SAl Cooper .of_match_table = of_match_ptr(sdhci_brcm_of_match), 169476bf3d6SAl Cooper }, 170476bf3d6SAl Cooper .probe = sdhci_brcmstb_probe, 171476bf3d6SAl Cooper .remove = sdhci_pltfm_unregister, 172476bf3d6SAl Cooper }; 173476bf3d6SAl Cooper 174476bf3d6SAl Cooper module_platform_driver(sdhci_brcmstb_driver); 175476bf3d6SAl Cooper 176476bf3d6SAl Cooper MODULE_DESCRIPTION("SDHCI driver for Broadcom BRCMSTB SoCs"); 177476bf3d6SAl Cooper MODULE_AUTHOR("Broadcom"); 178476bf3d6SAl Cooper MODULE_LICENSE("GPL v2"); 179