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