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/interrupt.h> 21 #include <linux/irq.h> 22 #include <linux/platform_device.h> 23 #include <linux/slab.h> 24 #include <linux/mmc/host.h> 25 #include <linux/mmc/sdhci-spear.h> 26 #include <linux/io.h> 27 #include "sdhci.h" 28 29 struct spear_sdhci { 30 struct clk *clk; 31 struct sdhci_plat_data *data; 32 }; 33 34 /* sdhci ops */ 35 static struct sdhci_ops sdhci_pltfm_ops = { 36 /* Nothing to do for now. */ 37 }; 38 39 /* gpio card detection interrupt handler */ 40 static irqreturn_t sdhci_gpio_irq(int irq, void *dev_id) 41 { 42 struct platform_device *pdev = dev_id; 43 struct sdhci_host *host = platform_get_drvdata(pdev); 44 struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev); 45 unsigned long gpio_irq_type; 46 int val; 47 48 val = gpio_get_value(sdhci->data->card_int_gpio); 49 50 /* val == 1 -> card removed, val == 0 -> card inserted */ 51 /* if card removed - set irq for low level, else vice versa */ 52 gpio_irq_type = val ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH; 53 set_irq_type(irq, gpio_irq_type); 54 55 if (sdhci->data->card_power_gpio >= 0) { 56 if (!sdhci->data->power_always_enb) { 57 /* if card inserted, give power, otherwise remove it */ 58 val = sdhci->data->power_active_high ? !val : val ; 59 gpio_set_value(sdhci->data->card_power_gpio, val); 60 } 61 } 62 63 /* inform sdhci driver about card insertion/removal */ 64 tasklet_schedule(&host->card_tasklet); 65 66 return IRQ_HANDLED; 67 } 68 69 static int __devinit sdhci_probe(struct platform_device *pdev) 70 { 71 struct sdhci_host *host; 72 struct resource *iomem; 73 struct spear_sdhci *sdhci; 74 int ret; 75 76 BUG_ON(pdev == NULL); 77 78 iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 79 if (!iomem) { 80 ret = -ENOMEM; 81 dev_dbg(&pdev->dev, "memory resource not defined\n"); 82 goto err; 83 } 84 85 if (!request_mem_region(iomem->start, resource_size(iomem), 86 "spear-sdhci")) { 87 ret = -EBUSY; 88 dev_dbg(&pdev->dev, "cannot request region\n"); 89 goto err; 90 } 91 92 sdhci = kzalloc(sizeof(*sdhci), GFP_KERNEL); 93 if (!sdhci) { 94 ret = -ENOMEM; 95 dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n"); 96 goto err_kzalloc; 97 } 98 99 /* clk enable */ 100 sdhci->clk = clk_get(&pdev->dev, NULL); 101 if (IS_ERR(sdhci->clk)) { 102 ret = PTR_ERR(sdhci->clk); 103 dev_dbg(&pdev->dev, "Error getting clock\n"); 104 goto err_clk_get; 105 } 106 107 ret = clk_enable(sdhci->clk); 108 if (ret) { 109 dev_dbg(&pdev->dev, "Error enabling clock\n"); 110 goto err_clk_enb; 111 } 112 113 /* overwrite platform_data */ 114 sdhci->data = dev_get_platdata(&pdev->dev); 115 pdev->dev.platform_data = sdhci; 116 117 if (pdev->dev.parent) 118 host = sdhci_alloc_host(pdev->dev.parent, 0); 119 else 120 host = sdhci_alloc_host(&pdev->dev, 0); 121 122 if (IS_ERR(host)) { 123 ret = PTR_ERR(host); 124 dev_dbg(&pdev->dev, "error allocating host\n"); 125 goto err_alloc_host; 126 } 127 128 host->hw_name = "sdhci"; 129 host->ops = &sdhci_pltfm_ops; 130 host->irq = platform_get_irq(pdev, 0); 131 host->quirks = SDHCI_QUIRK_BROKEN_ADMA; 132 133 host->ioaddr = ioremap(iomem->start, resource_size(iomem)); 134 if (!host->ioaddr) { 135 ret = -ENOMEM; 136 dev_dbg(&pdev->dev, "failed to remap registers\n"); 137 goto err_ioremap; 138 } 139 140 ret = sdhci_add_host(host); 141 if (ret) { 142 dev_dbg(&pdev->dev, "error adding host\n"); 143 goto err_add_host; 144 } 145 146 platform_set_drvdata(pdev, host); 147 148 /* 149 * It is optional to use GPIOs for sdhci Power control & sdhci card 150 * interrupt detection. If sdhci->data is NULL, then use original sdhci 151 * lines otherwise GPIO lines. 152 * If GPIO is selected for power control, then power should be disabled 153 * after card removal and should be enabled when card insertion 154 * interrupt occurs 155 */ 156 if (!sdhci->data) 157 return 0; 158 159 if (sdhci->data->card_power_gpio >= 0) { 160 int val = 0; 161 162 ret = gpio_request(sdhci->data->card_power_gpio, "sdhci"); 163 if (ret < 0) { 164 dev_dbg(&pdev->dev, "gpio request fail: %d\n", 165 sdhci->data->card_power_gpio); 166 goto err_pgpio_request; 167 } 168 169 if (sdhci->data->power_always_enb) 170 val = sdhci->data->power_active_high; 171 else 172 val = !sdhci->data->power_active_high; 173 174 ret = gpio_direction_output(sdhci->data->card_power_gpio, val); 175 if (ret) { 176 dev_dbg(&pdev->dev, "gpio set direction fail: %d\n", 177 sdhci->data->card_power_gpio); 178 goto err_pgpio_direction; 179 } 180 181 gpio_set_value(sdhci->data->card_power_gpio, 1); 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 static struct platform_driver sdhci_driver = { 276 .driver = { 277 .name = "sdhci", 278 .owner = THIS_MODULE, 279 }, 280 .probe = sdhci_probe, 281 .remove = __devexit_p(sdhci_remove), 282 }; 283 284 static int __init sdhci_init(void) 285 { 286 return platform_driver_register(&sdhci_driver); 287 } 288 module_init(sdhci_init); 289 290 static void __exit sdhci_exit(void) 291 { 292 platform_driver_unregister(&sdhci_driver); 293 } 294 module_exit(sdhci_exit); 295 296 MODULE_DESCRIPTION("SPEAr Secure Digital Host Controller Interface driver"); 297 MODULE_AUTHOR("Viresh Kumar <viresh.kumar@st.com>"); 298 MODULE_LICENSE("GPL v2"); 299