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