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/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 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 (!devm_request_mem_region(&pdev->dev, iomem->start, 86 resource_size(iomem), "spear-sdhci")) { 87 ret = -EBUSY; 88 dev_dbg(&pdev->dev, "cannot request region\n"); 89 goto err; 90 } 91 92 sdhci = devm_kzalloc(&pdev->dev, sizeof(*sdhci), GFP_KERNEL); 93 if (!sdhci) { 94 ret = -ENOMEM; 95 dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n"); 96 goto err; 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; 105 } 106 107 ret = clk_enable(sdhci->clk); 108 if (ret) { 109 dev_dbg(&pdev->dev, "Error enabling clock\n"); 110 goto put_clk; 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 disable_clk; 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 = devm_ioremap(&pdev->dev, iomem->start, 134 resource_size(iomem)); 135 if (!host->ioaddr) { 136 ret = -ENOMEM; 137 dev_dbg(&pdev->dev, "failed to remap registers\n"); 138 goto free_host; 139 } 140 141 ret = sdhci_add_host(host); 142 if (ret) { 143 dev_dbg(&pdev->dev, "error adding host\n"); 144 goto free_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 = devm_gpio_request(&pdev->dev, 164 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 set_drvdata; 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 set_drvdata; 181 } 182 } 183 184 if (sdhci->data->card_int_gpio >= 0) { 185 ret = devm_gpio_request(&pdev->dev, sdhci->data->card_int_gpio, 186 "sdhci"); 187 if (ret < 0) { 188 dev_dbg(&pdev->dev, "gpio request fail: %d\n", 189 sdhci->data->card_int_gpio); 190 goto set_drvdata; 191 } 192 193 ret = gpio_direction_input(sdhci->data->card_int_gpio); 194 if (ret) { 195 dev_dbg(&pdev->dev, "gpio set direction fail: %d\n", 196 sdhci->data->card_int_gpio); 197 goto set_drvdata; 198 } 199 ret = devm_request_irq(&pdev->dev, 200 gpio_to_irq(sdhci->data->card_int_gpio), 201 sdhci_gpio_irq, IRQF_TRIGGER_LOW, 202 mmc_hostname(host->mmc), pdev); 203 if (ret) { 204 dev_dbg(&pdev->dev, "gpio request irq fail: %d\n", 205 sdhci->data->card_int_gpio); 206 goto set_drvdata; 207 } 208 209 } 210 211 return 0; 212 213 set_drvdata: 214 platform_set_drvdata(pdev, NULL); 215 sdhci_remove_host(host, 1); 216 free_host: 217 sdhci_free_host(host); 218 disable_clk: 219 clk_disable(sdhci->clk); 220 put_clk: 221 clk_put(sdhci->clk); 222 err: 223 dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret); 224 return ret; 225 } 226 227 static int __devexit sdhci_remove(struct platform_device *pdev) 228 { 229 struct sdhci_host *host = platform_get_drvdata(pdev); 230 struct spear_sdhci *sdhci = dev_get_platdata(&pdev->dev); 231 int dead = 0; 232 u32 scratch; 233 234 platform_set_drvdata(pdev, NULL); 235 scratch = readl(host->ioaddr + SDHCI_INT_STATUS); 236 if (scratch == (u32)-1) 237 dead = 1; 238 239 sdhci_remove_host(host, dead); 240 sdhci_free_host(host); 241 clk_disable(sdhci->clk); 242 clk_put(sdhci->clk); 243 244 return 0; 245 } 246 247 #ifdef CONFIG_PM 248 static int sdhci_suspend(struct device *dev) 249 { 250 struct sdhci_host *host = dev_get_drvdata(dev); 251 struct spear_sdhci *sdhci = dev_get_platdata(dev); 252 int ret; 253 254 ret = sdhci_suspend_host(host); 255 if (!ret) 256 clk_disable(sdhci->clk); 257 258 return ret; 259 } 260 261 static int sdhci_resume(struct device *dev) 262 { 263 struct sdhci_host *host = dev_get_drvdata(dev); 264 struct spear_sdhci *sdhci = dev_get_platdata(dev); 265 int ret; 266 267 ret = clk_enable(sdhci->clk); 268 if (ret) { 269 dev_dbg(dev, "Resume: Error enabling clock\n"); 270 return ret; 271 } 272 273 return sdhci_resume_host(host); 274 } 275 #endif 276 277 static SIMPLE_DEV_PM_OPS(sdhci_pm_ops, sdhci_suspend, sdhci_resume); 278 279 static struct platform_driver sdhci_driver = { 280 .driver = { 281 .name = "sdhci", 282 .owner = THIS_MODULE, 283 .pm = &sdhci_pm_ops, 284 }, 285 .probe = sdhci_probe, 286 .remove = __devexit_p(sdhci_remove), 287 }; 288 289 module_platform_driver(sdhci_driver); 290 291 MODULE_DESCRIPTION("SPEAr Secure Digital Host Controller Interface driver"); 292 MODULE_AUTHOR("Viresh Kumar <viresh.linux@gmail.com>"); 293 MODULE_LICENSE("GPL v2"); 294