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 /* Nothing to do for now. */ 42 }; 43 44 #ifdef CONFIG_OF 45 static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pdev) 46 { 47 struct device_node *np = pdev->dev.of_node; 48 struct sdhci_plat_data *pdata = NULL; 49 int cd_gpio; 50 51 cd_gpio = of_get_named_gpio(np, "cd-gpios", 0); 52 if (!gpio_is_valid(cd_gpio)) 53 cd_gpio = -1; 54 55 /* If pdata is required */ 56 if (cd_gpio != -1) { 57 pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); 58 if (!pdata) 59 dev_err(&pdev->dev, "DT: kzalloc failed\n"); 60 else 61 pdata->card_int_gpio = cd_gpio; 62 } 63 64 return pdata; 65 } 66 #else 67 static struct sdhci_plat_data *sdhci_probe_config_dt(struct platform_device *pdev) 68 { 69 return ERR_PTR(-ENOSYS); 70 } 71 #endif 72 73 static int sdhci_probe(struct platform_device *pdev) 74 { 75 struct device_node *np = pdev->dev.of_node; 76 struct sdhci_host *host; 77 struct resource *iomem; 78 struct spear_sdhci *sdhci; 79 struct device *dev; 80 int ret; 81 82 dev = pdev->dev.parent ? pdev->dev.parent : &pdev->dev; 83 host = sdhci_alloc_host(dev, sizeof(*sdhci)); 84 if (IS_ERR(host)) { 85 ret = PTR_ERR(host); 86 dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n"); 87 goto err; 88 } 89 90 iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 91 host->ioaddr = devm_ioremap_resource(&pdev->dev, iomem); 92 if (IS_ERR(host->ioaddr)) { 93 ret = PTR_ERR(host->ioaddr); 94 dev_dbg(&pdev->dev, "unable to map iomem: %d\n", ret); 95 goto err_host; 96 } 97 98 host->hw_name = "sdhci"; 99 host->ops = &sdhci_pltfm_ops; 100 host->irq = platform_get_irq(pdev, 0); 101 host->quirks = SDHCI_QUIRK_BROKEN_ADMA; 102 103 sdhci = sdhci_priv(host); 104 105 /* clk enable */ 106 sdhci->clk = devm_clk_get(&pdev->dev, NULL); 107 if (IS_ERR(sdhci->clk)) { 108 ret = PTR_ERR(sdhci->clk); 109 dev_dbg(&pdev->dev, "Error getting clock\n"); 110 goto err_host; 111 } 112 113 ret = clk_prepare_enable(sdhci->clk); 114 if (ret) { 115 dev_dbg(&pdev->dev, "Error enabling clock\n"); 116 goto err_host; 117 } 118 119 ret = clk_set_rate(sdhci->clk, 50000000); 120 if (ret) 121 dev_dbg(&pdev->dev, "Error setting desired clk, clk=%lu\n", 122 clk_get_rate(sdhci->clk)); 123 124 if (np) { 125 sdhci->data = sdhci_probe_config_dt(pdev); 126 if (IS_ERR(sdhci->data)) { 127 dev_err(&pdev->dev, "DT: Failed to get pdata\n"); 128 goto disable_clk; 129 } 130 } else { 131 sdhci->data = dev_get_platdata(&pdev->dev); 132 } 133 134 /* 135 * It is optional to use GPIOs for sdhci card detection. If 136 * sdhci->data is NULL, then use original sdhci lines otherwise 137 * GPIO lines. We use the built-in GPIO support for this. 138 */ 139 if (sdhci->data && sdhci->data->card_int_gpio >= 0) { 140 ret = mmc_gpio_request_cd(host->mmc, 141 sdhci->data->card_int_gpio, 0); 142 if (ret < 0) { 143 dev_dbg(&pdev->dev, 144 "failed to request card-detect gpio%d\n", 145 sdhci->data->card_int_gpio); 146 goto disable_clk; 147 } 148 } 149 150 ret = sdhci_add_host(host); 151 if (ret) { 152 dev_dbg(&pdev->dev, "error adding host\n"); 153 goto disable_clk; 154 } 155 156 platform_set_drvdata(pdev, host); 157 158 return 0; 159 160 disable_clk: 161 clk_disable_unprepare(sdhci->clk); 162 err_host: 163 sdhci_free_host(host); 164 err: 165 dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret); 166 return ret; 167 } 168 169 static int sdhci_remove(struct platform_device *pdev) 170 { 171 struct sdhci_host *host = platform_get_drvdata(pdev); 172 struct spear_sdhci *sdhci = sdhci_priv(host); 173 int dead = 0; 174 u32 scratch; 175 176 scratch = readl(host->ioaddr + SDHCI_INT_STATUS); 177 if (scratch == (u32)-1) 178 dead = 1; 179 180 sdhci_remove_host(host, dead); 181 clk_disable_unprepare(sdhci->clk); 182 sdhci_free_host(host); 183 184 return 0; 185 } 186 187 #ifdef CONFIG_PM_SLEEP 188 static int sdhci_suspend(struct device *dev) 189 { 190 struct sdhci_host *host = dev_get_drvdata(dev); 191 struct spear_sdhci *sdhci = sdhci_priv(host); 192 int ret; 193 194 ret = sdhci_suspend_host(host); 195 if (!ret) 196 clk_disable(sdhci->clk); 197 198 return ret; 199 } 200 201 static int sdhci_resume(struct device *dev) 202 { 203 struct sdhci_host *host = dev_get_drvdata(dev); 204 struct spear_sdhci *sdhci = sdhci_priv(host); 205 int ret; 206 207 ret = clk_enable(sdhci->clk); 208 if (ret) { 209 dev_dbg(dev, "Resume: Error enabling clock\n"); 210 return ret; 211 } 212 213 return sdhci_resume_host(host); 214 } 215 #endif 216 217 static SIMPLE_DEV_PM_OPS(sdhci_pm_ops, sdhci_suspend, sdhci_resume); 218 219 #ifdef CONFIG_OF 220 static const struct of_device_id sdhci_spear_id_table[] = { 221 { .compatible = "st,spear300-sdhci" }, 222 {} 223 }; 224 MODULE_DEVICE_TABLE(of, sdhci_spear_id_table); 225 #endif 226 227 static struct platform_driver sdhci_driver = { 228 .driver = { 229 .name = "sdhci", 230 .owner = THIS_MODULE, 231 .pm = &sdhci_pm_ops, 232 .of_match_table = of_match_ptr(sdhci_spear_id_table), 233 }, 234 .probe = sdhci_probe, 235 .remove = sdhci_remove, 236 }; 237 238 module_platform_driver(sdhci_driver); 239 240 MODULE_DESCRIPTION("SPEAr Secure Digital Host Controller Interface driver"); 241 MODULE_AUTHOR("Viresh Kumar <viresh.linux@gmail.com>"); 242 MODULE_LICENSE("GPL v2"); 243