1 /* 2 * drivers/mmc/host/sdhci-spear.c 3 * 4 * Support of SDHCI platform devices for spear soc family 5 * 6 * Copyright (C) 2010 ST Microelectronics 7 * Viresh Kumar <viresh.linux@gmail.com> 8 * 9 * Inspired by sdhci-pltfm.c 10 * 11 * This file is licensed under the terms of the GNU General Public 12 * License version 2. This program is licensed "as is" without any 13 * warranty of any kind, whether express or implied. 14 */ 15 16 #include <linux/clk.h> 17 #include <linux/delay.h> 18 #include <linux/gpio.h> 19 #include <linux/highmem.h> 20 #include <linux/module.h> 21 #include <linux/interrupt.h> 22 #include <linux/irq.h> 23 #include <linux/of.h> 24 #include <linux/of_gpio.h> 25 #include <linux/platform_device.h> 26 #include <linux/pm.h> 27 #include <linux/slab.h> 28 #include <linux/mmc/host.h> 29 #include <linux/mmc/sdhci-spear.h> 30 #include <linux/mmc/slot-gpio.h> 31 #include <linux/io.h> 32 #include "sdhci.h" 33 34 struct spear_sdhci { 35 struct clk *clk; 36 struct sdhci_plat_data *data; 37 }; 38 39 /* sdhci ops */ 40 static const struct sdhci_ops sdhci_pltfm_ops = { 41 .set_clock = sdhci_set_clock, 42 .set_bus_width = sdhci_set_bus_width, 43 .reset = sdhci_reset, 44 .set_uhs_signaling = sdhci_set_uhs_signaling, 45 }; 46 47 #ifdef CONFIG_OF 48 static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pdev) 49 { 50 struct device_node *np = pdev->dev.of_node; 51 struct sdhci_plat_data *pdata = NULL; 52 int cd_gpio; 53 54 cd_gpio = of_get_named_gpio(np, "cd-gpios", 0); 55 if (!gpio_is_valid(cd_gpio)) 56 cd_gpio = -1; 57 58 /* If pdata is required */ 59 if (cd_gpio != -1) { 60 pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); 61 if (!pdata) 62 dev_err(&pdev->dev, "DT: kzalloc failed\n"); 63 else 64 pdata->card_int_gpio = cd_gpio; 65 } 66 67 return pdata; 68 } 69 #else 70 static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pdev) 71 { 72 return ERR_PTR(-ENOSYS); 73 } 74 #endif 75 76 static int sdhci_probe(struct platform_device *pdev) 77 { 78 struct device_node *np = pdev->dev.of_node; 79 struct sdhci_host *host; 80 struct resource *iomem; 81 struct spear_sdhci *sdhci; 82 struct device *dev; 83 int ret; 84 85 dev = pdev->dev.parent ? pdev->dev.parent : &pdev->dev; 86 host = sdhci_alloc_host(dev, sizeof(*sdhci)); 87 if (IS_ERR(host)) { 88 ret = PTR_ERR(host); 89 dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n"); 90 goto err; 91 } 92 93 iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 94 host->ioaddr = devm_ioremap_resource(&pdev->dev, iomem); 95 if (IS_ERR(host->ioaddr)) { 96 ret = PTR_ERR(host->ioaddr); 97 dev_dbg(&pdev->dev, "unable to map iomem: %d\n", ret); 98 goto err_host; 99 } 100 101 host->hw_name = "sdhci"; 102 host->ops = &sdhci_pltfm_ops; 103 host->irq = platform_get_irq(pdev, 0); 104 host->quirks = SDHCI_QUIRK_BROKEN_ADMA; 105 106 sdhci = sdhci_priv(host); 107 108 /* clk enable */ 109 sdhci->clk = devm_clk_get(&pdev->dev, NULL); 110 if (IS_ERR(sdhci->clk)) { 111 ret = PTR_ERR(sdhci->clk); 112 dev_dbg(&pdev->dev, "Error getting clock\n"); 113 goto err_host; 114 } 115 116 ret = clk_prepare_enable(sdhci->clk); 117 if (ret) { 118 dev_dbg(&pdev->dev, "Error enabling clock\n"); 119 goto err_host; 120 } 121 122 ret = clk_set_rate(sdhci->clk, 50000000); 123 if (ret) 124 dev_dbg(&pdev->dev, "Error setting desired clk, clk=%lu\n", 125 clk_get_rate(sdhci->clk)); 126 127 if (np) { 128 sdhci->data = sdhci_probe_config_dt(pdev); 129 if (IS_ERR(sdhci->data)) { 130 dev_err(&pdev->dev, "DT: Failed to get pdata\n"); 131 goto disable_clk; 132 } 133 } else { 134 sdhci->data = dev_get_platdata(&pdev->dev); 135 } 136 137 /* 138 * It is optional to use GPIOs for sdhci card detection. If 139 * sdhci->data is NULL, then use original sdhci lines otherwise 140 * GPIO lines. We use the built-in GPIO support for this. 141 */ 142 if (sdhci->data && sdhci->data->card_int_gpio >= 0) { 143 ret = mmc_gpio_request_cd(host->mmc, 144 sdhci->data->card_int_gpio, 0); 145 if (ret < 0) { 146 dev_dbg(&pdev->dev, 147 "failed to request card-detect gpio%d\n", 148 sdhci->data->card_int_gpio); 149 goto disable_clk; 150 } 151 } 152 153 ret = sdhci_add_host(host); 154 if (ret) { 155 dev_dbg(&pdev->dev, "error adding host\n"); 156 goto disable_clk; 157 } 158 159 platform_set_drvdata(pdev, host); 160 161 return 0; 162 163 disable_clk: 164 clk_disable_unprepare(sdhci->clk); 165 err_host: 166 sdhci_free_host(host); 167 err: 168 dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret); 169 return ret; 170 } 171 172 static int sdhci_remove(struct platform_device *pdev) 173 { 174 struct sdhci_host *host = platform_get_drvdata(pdev); 175 struct spear_sdhci *sdhci = sdhci_priv(host); 176 int dead = 0; 177 u32 scratch; 178 179 scratch = readl(host->ioaddr + SDHCI_INT_STATUS); 180 if (scratch == (u32)-1) 181 dead = 1; 182 183 sdhci_remove_host(host, dead); 184 clk_disable_unprepare(sdhci->clk); 185 sdhci_free_host(host); 186 187 return 0; 188 } 189 190 #ifdef CONFIG_PM_SLEEP 191 static int sdhci_suspend(struct device *dev) 192 { 193 struct sdhci_host *host = dev_get_drvdata(dev); 194 struct spear_sdhci *sdhci = sdhci_priv(host); 195 int ret; 196 197 ret = sdhci_suspend_host(host); 198 if (!ret) 199 clk_disable(sdhci->clk); 200 201 return ret; 202 } 203 204 static int sdhci_resume(struct device *dev) 205 { 206 struct sdhci_host *host = dev_get_drvdata(dev); 207 struct spear_sdhci *sdhci = sdhci_priv(host); 208 int ret; 209 210 ret = clk_enable(sdhci->clk); 211 if (ret) { 212 dev_dbg(dev, "Resume: Error enabling clock\n"); 213 return ret; 214 } 215 216 return sdhci_resume_host(host); 217 } 218 #endif 219 220 static SIMPLE_DEV_PM_OPS(sdhci_pm_ops, sdhci_suspend, sdhci_resume); 221 222 #ifdef CONFIG_OF 223 static const struct of_device_id sdhci_spear_id_table[] = { 224 { .compatible = "st,spear300-sdhci" }, 225 {} 226 }; 227 MODULE_DEVICE_TABLE(of, sdhci_spear_id_table); 228 #endif 229 230 static struct platform_driver sdhci_driver = { 231 .driver = { 232 .name = "sdhci", 233 .owner = THIS_MODULE, 234 .pm = &sdhci_pm_ops, 235 .of_match_table = of_match_ptr(sdhci_spear_id_table), 236 }, 237 .probe = sdhci_probe, 238 .remove = sdhci_remove, 239 }; 240 241 module_platform_driver(sdhci_driver); 242 243 MODULE_DESCRIPTION("SPEAr Secure Digital Host Controller Interface driver"); 244 MODULE_AUTHOR("Viresh Kumar <viresh.linux@gmail.com>"); 245 MODULE_LICENSE("GPL v2"); 246