1 /* 2 * sdhci-brcmstb.c Support for SDHCI on Broadcom BRCMSTB SoC's 3 * 4 * Copyright (C) 2015 Broadcom Corporation 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 2 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 */ 16 17 #include <linux/io.h> 18 #include <linux/mmc/host.h> 19 #include <linux/module.h> 20 #include <linux/of.h> 21 22 #include "sdhci-pltfm.h" 23 24 #ifdef CONFIG_PM_SLEEP 25 26 static int sdhci_brcmstb_suspend(struct device *dev) 27 { 28 struct sdhci_host *host = dev_get_drvdata(dev); 29 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 30 int res; 31 32 res = sdhci_suspend_host(host); 33 if (res) 34 return res; 35 clk_disable_unprepare(pltfm_host->clk); 36 return res; 37 } 38 39 static int sdhci_brcmstb_resume(struct device *dev) 40 { 41 struct sdhci_host *host = dev_get_drvdata(dev); 42 struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 43 int err; 44 45 err = clk_prepare_enable(pltfm_host->clk); 46 if (err) 47 return err; 48 return sdhci_resume_host(host); 49 } 50 51 #endif /* CONFIG_PM_SLEEP */ 52 53 static SIMPLE_DEV_PM_OPS(sdhci_brcmstb_pmops, sdhci_brcmstb_suspend, 54 sdhci_brcmstb_resume); 55 56 static const struct sdhci_ops sdhci_brcmstb_ops = { 57 .set_clock = sdhci_set_clock, 58 .set_bus_width = sdhci_set_bus_width, 59 .reset = sdhci_reset, 60 .set_uhs_signaling = sdhci_set_uhs_signaling, 61 }; 62 63 static struct sdhci_pltfm_data sdhci_brcmstb_pdata = { 64 .ops = &sdhci_brcmstb_ops, 65 }; 66 67 static int sdhci_brcmstb_probe(struct platform_device *pdev) 68 { 69 struct sdhci_host *host; 70 struct sdhci_pltfm_host *pltfm_host; 71 struct clk *clk; 72 int res; 73 74 clk = devm_clk_get(&pdev->dev, NULL); 75 if (IS_ERR(clk)) { 76 dev_err(&pdev->dev, "Clock not found in Device Tree\n"); 77 clk = NULL; 78 } 79 res = clk_prepare_enable(clk); 80 if (res) 81 return res; 82 83 host = sdhci_pltfm_init(pdev, &sdhci_brcmstb_pdata, 0); 84 if (IS_ERR(host)) { 85 res = PTR_ERR(host); 86 goto err_clk; 87 } 88 89 /* Enable MMC_CAP2_HC_ERASE_SZ for better max discard calculations */ 90 host->mmc->caps2 |= MMC_CAP2_HC_ERASE_SZ; 91 92 sdhci_get_of_property(pdev); 93 mmc_of_parse(host->mmc); 94 95 /* 96 * Supply the existing CAPS, but clear the UHS modes. This 97 * will allow these modes to be specified by device tree 98 * properties through mmc_of_parse(). 99 */ 100 host->caps = sdhci_readl(host, SDHCI_CAPABILITIES); 101 if (of_device_is_compatible(pdev->dev.of_node, "brcm,bcm7425-sdhci")) 102 host->caps &= ~SDHCI_CAN_64BIT; 103 host->caps1 = sdhci_readl(host, SDHCI_CAPABILITIES_1); 104 host->caps1 &= ~(SDHCI_SUPPORT_SDR50 | SDHCI_SUPPORT_SDR104 | 105 SDHCI_SUPPORT_DDR50); 106 host->quirks |= SDHCI_QUIRK_MISSING_CAPS | 107 SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; 108 109 res = sdhci_add_host(host); 110 if (res) 111 goto err; 112 113 pltfm_host = sdhci_priv(host); 114 pltfm_host->clk = clk; 115 return res; 116 117 err: 118 sdhci_pltfm_free(pdev); 119 err_clk: 120 clk_disable_unprepare(clk); 121 return res; 122 } 123 124 static const struct of_device_id sdhci_brcm_of_match[] = { 125 { .compatible = "brcm,bcm7425-sdhci" }, 126 { .compatible = "brcm,bcm7445-sdhci" }, 127 {}, 128 }; 129 MODULE_DEVICE_TABLE(of, sdhci_brcm_of_match); 130 131 static struct platform_driver sdhci_brcmstb_driver = { 132 .driver = { 133 .name = "sdhci-brcmstb", 134 .pm = &sdhci_brcmstb_pmops, 135 .of_match_table = of_match_ptr(sdhci_brcm_of_match), 136 }, 137 .probe = sdhci_brcmstb_probe, 138 .remove = sdhci_pltfm_unregister, 139 }; 140 141 module_platform_driver(sdhci_brcmstb_driver); 142 143 MODULE_DESCRIPTION("SDHCI driver for Broadcom BRCMSTB SoCs"); 144 MODULE_AUTHOR("Broadcom"); 145 MODULE_LICENSE("GPL v2"); 146