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