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.kumar@st.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/platform_device.h> 24 #include <linux/pm.h> 25 #include <linux/slab.h> 26 #include <linux/mmc/host.h> 27 #include <linux/mmc/sdhci-spear.h> 28 #include <linux/io.h> 29 #include "sdhci.h" 30 31 struct spear_sdhci { 32 struct clk *clk; 33 struct sdhci_plat_data *data; 34 }; 35 36 /* sdhci ops */ 37 static struct sdhci_ops sdhci_pltfm_ops = { 38 /* Nothing to do for now. */ 39 }; 40 41 /* gpio card detection interrupt handler */ 42 static irqreturn_t sdhci_gpio_irq(int irq, void *dev_id) 43 { 44 struct platform_device *pdev = dev_id; 45 struct sdhci_host *host = platform_get_drvdata(pdev); 46 struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev); 47 unsigned long gpio_irq_type; 48 int val; 49 50 val = gpio_get_value(sdhci->data->card_int_gpio); 51 52 /* val == 1 -> card removed, val == 0 -> card inserted */ 53 /* if card removed - set irq for low level, else vice versa */ 54 gpio_irq_type = val ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH; 55 irq_set_irq_type(irq, gpio_irq_type); 56 57 if (sdhci->data->card_power_gpio >= 0) { 58 if (!sdhci->data->power_always_enb) { 59 /* if card inserted, give power, otherwise remove it */ 60 val = sdhci->data->power_active_high ? !val : val ; 61 gpio_set_value(sdhci->data->card_power_gpio, val); 62 } 63 } 64 65 /* inform sdhci driver about card insertion/removal */ 66 tasklet_schedule(&host->card_tasklet); 67 68 return IRQ_HANDLED; 69 } 70 71 static int __devinit sdhci_probe(struct platform_device *pdev) 72 { 73 struct sdhci_host *host; 74 struct resource *iomem; 75 struct spear_sdhci *sdhci; 76 int ret; 77 78 BUG_ON(pdev == NULL); 79 80 iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 81 if (!iomem) { 82 ret = -ENOMEM; 83 dev_dbg(&pdev->dev, "memory resource not defined\n"); 84 goto err; 85 } 86 87 if (!request_mem_region(iomem->start, resource_size(iomem), 88 "spear-sdhci")) { 89 ret = -EBUSY; 90 dev_dbg(&pdev->dev, "cannot request region\n"); 91 goto err; 92 } 93 94 sdhci = kzalloc(sizeof(*sdhci), GFP_KERNEL); 95 if (!sdhci) { 96 ret = -ENOMEM; 97 dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n"); 98 goto err_kzalloc; 99 } 100 101 /* clk enable */ 102 sdhci->clk = clk_get(&pdev->dev, NULL); 103 if (IS_ERR(sdhci->clk)) { 104 ret = PTR_ERR(sdhci->clk); 105 dev_dbg(&pdev->dev, "Error getting clock\n"); 106 goto err_clk_get; 107 } 108 109 ret = clk_enable(sdhci->clk); 110 if (ret) { 111 dev_dbg(&pdev->dev, "Error enabling clock\n"); 112 goto err_clk_enb; 113 } 114 115 /* overwrite platform_data */ 116 sdhci->data = dev_get_platdata(&pdev->dev); 117 pdev->dev.platform_data = sdhci; 118 119 if (pdev->dev.parent) 120 host = sdhci_alloc_host(pdev->dev.parent, 0); 121 else 122 host = sdhci_alloc_host(&pdev->dev, 0); 123 124 if (IS_ERR(host)) { 125 ret = PTR_ERR(host); 126 dev_dbg(&pdev->dev, "error allocating host\n"); 127 goto err_alloc_host; 128 } 129 130 host->hw_name = "sdhci"; 131 host->ops = &sdhci_pltfm_ops; 132 host->irq = platform_get_irq(pdev, 0); 133 host->quirks = SDHCI_QUIRK_BROKEN_ADMA; 134 135 host->ioaddr = ioremap(iomem->start, resource_size(iomem)); 136 if (!host->ioaddr) { 137 ret = -ENOMEM; 138 dev_dbg(&pdev->dev, "failed to remap registers\n"); 139 goto err_ioremap; 140 } 141 142 ret = sdhci_add_host(host); 143 if (ret) { 144 dev_dbg(&pdev->dev, "error adding host\n"); 145 goto err_add_host; 146 } 147 148 platform_set_drvdata(pdev, host); 149 150 /* 151 * It is optional to use GPIOs for sdhci Power control & sdhci card 152 * interrupt detection. If sdhci->data is NULL, then use original sdhci 153 * lines otherwise GPIO lines. 154 * If GPIO is selected for power control, then power should be disabled 155 * after card removal and should be enabled when card insertion 156 * interrupt occurs 157 */ 158 if (!sdhci->data) 159 return 0; 160 161 if (sdhci->data->card_power_gpio >= 0) { 162 int val = 0; 163 164 ret = gpio_request(sdhci->data->card_power_gpio, "sdhci"); 165 if (ret < 0) { 166 dev_dbg(&pdev->dev, "gpio request fail: %d\n", 167 sdhci->data->card_power_gpio); 168 goto err_pgpio_request; 169 } 170 171 if (sdhci->data->power_always_enb) 172 val = sdhci->data->power_active_high; 173 else 174 val = !sdhci->data->power_active_high; 175 176 ret = gpio_direction_output(sdhci->data->card_power_gpio, val); 177 if (ret) { 178 dev_dbg(&pdev->dev, "gpio set direction fail: %d\n", 179 sdhci->data->card_power_gpio); 180 goto err_pgpio_direction; 181 } 182 } 183 184 if (sdhci->data->card_int_gpio >= 0) { 185 ret = gpio_request(sdhci->data->card_int_gpio, "sdhci"); 186 if (ret < 0) { 187 dev_dbg(&pdev->dev, "gpio request fail: %d\n", 188 sdhci->data->card_int_gpio); 189 goto err_igpio_request; 190 } 191 192 ret = gpio_direction_input(sdhci->data->card_int_gpio); 193 if (ret) { 194 dev_dbg(&pdev->dev, "gpio set direction fail: %d\n", 195 sdhci->data->card_int_gpio); 196 goto err_igpio_direction; 197 } 198 ret = request_irq(gpio_to_irq(sdhci->data->card_int_gpio), 199 sdhci_gpio_irq, IRQF_TRIGGER_LOW, 200 mmc_hostname(host->mmc), pdev); 201 if (ret) { 202 dev_dbg(&pdev->dev, "gpio request irq fail: %d\n", 203 sdhci->data->card_int_gpio); 204 goto err_igpio_request_irq; 205 } 206 207 } 208 209 return 0; 210 211 err_igpio_request_irq: 212 err_igpio_direction: 213 if (sdhci->data->card_int_gpio >= 0) 214 gpio_free(sdhci->data->card_int_gpio); 215 err_igpio_request: 216 err_pgpio_direction: 217 if (sdhci->data->card_power_gpio >= 0) 218 gpio_free(sdhci->data->card_power_gpio); 219 err_pgpio_request: 220 platform_set_drvdata(pdev, NULL); 221 sdhci_remove_host(host, 1); 222 err_add_host: 223 iounmap(host->ioaddr); 224 err_ioremap: 225 sdhci_free_host(host); 226 err_alloc_host: 227 clk_disable(sdhci->clk); 228 err_clk_enb: 229 clk_put(sdhci->clk); 230 err_clk_get: 231 kfree(sdhci); 232 err_kzalloc: 233 release_mem_region(iomem->start, resource_size(iomem)); 234 err: 235 dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret); 236 return ret; 237 } 238 239 static int __devexit sdhci_remove(struct platform_device *pdev) 240 { 241 struct sdhci_host *host = platform_get_drvdata(pdev); 242 struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 243 struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev); 244 int dead; 245 u32 scratch; 246 247 if (sdhci->data) { 248 if (sdhci->data->card_int_gpio >= 0) { 249 free_irq(gpio_to_irq(sdhci->data->card_int_gpio), pdev); 250 gpio_free(sdhci->data->card_int_gpio); 251 } 252 253 if (sdhci->data->card_power_gpio >= 0) 254 gpio_free(sdhci->data->card_power_gpio); 255 } 256 257 platform_set_drvdata(pdev, NULL); 258 dead = 0; 259 scratch = readl(host->ioaddr + SDHCI_INT_STATUS); 260 if (scratch == (u32)-1) 261 dead = 1; 262 263 sdhci_remove_host(host, dead); 264 iounmap(host->ioaddr); 265 sdhci_free_host(host); 266 clk_disable(sdhci->clk); 267 clk_put(sdhci->clk); 268 kfree(sdhci); 269 if (iomem) 270 release_mem_region(iomem->start, resource_size(iomem)); 271 272 return 0; 273 } 274 275 #ifdef CONFIG_PM 276 static int sdhci_suspend(struct device *dev) 277 { 278 struct sdhci_host *host = dev_get_drvdata(dev); 279 struct spear_sdhci *sdhci = dev_get_platdata(dev); 280 int ret; 281 282 ret = sdhci_suspend_host(host); 283 if (!ret) 284 clk_disable(sdhci->clk); 285 286 return ret; 287 } 288 289 static int sdhci_resume(struct device *dev) 290 { 291 struct sdhci_host *host = dev_get_drvdata(dev); 292 struct spear_sdhci *sdhci = dev_get_platdata(dev); 293 int ret; 294 295 ret = clk_enable(sdhci->clk); 296 if (ret) { 297 dev_dbg(dev, "Resume: Error enabling clock\n"); 298 return ret; 299 } 300 301 return sdhci_resume_host(host); 302 } 303 304 const struct dev_pm_ops sdhci_pm_ops = { 305 .suspend = sdhci_suspend, 306 .resume = sdhci_resume, 307 }; 308 #endif 309 310 static struct platform_driver sdhci_driver = { 311 .driver = { 312 .name = "sdhci", 313 .owner = THIS_MODULE, 314 #ifdef CONFIG_PM 315 .pm = &sdhci_pm_ops, 316 #endif 317 }, 318 .probe = sdhci_probe, 319 .remove = __devexit_p(sdhci_remove), 320 }; 321 322 module_platform_driver(sdhci_driver); 323 324 MODULE_DESCRIPTION("SPEAr Secure Digital Host Controller Interface driver"); 325 MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>"); 326 MODULE_LICENSE("GPL v2"); 327