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