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 7da89947bSViresh Kumar * Viresh Kumar <vireshk@kernel.org> 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> 23067bf748SViresh Kumar #include <linux/of.h> 24067bf748SViresh Kumar #include <linux/of_gpio.h> 25c63b3cbaSViresh KUMAR #include <linux/platform_device.h> 26b70a7fabSViresh Kumar #include <linux/pm.h> 27c63b3cbaSViresh KUMAR #include <linux/slab.h> 28c63b3cbaSViresh KUMAR #include <linux/mmc/host.h> 29b42b9b12SRussell King #include <linux/mmc/slot-gpio.h> 30c63b3cbaSViresh KUMAR #include <linux/io.h> 31c63b3cbaSViresh KUMAR #include "sdhci.h" 32c63b3cbaSViresh KUMAR 33c63b3cbaSViresh KUMAR struct spear_sdhci { 34c63b3cbaSViresh KUMAR struct clk *clk; 3503a6d291SUlf Hansson int card_int_gpio; 36c63b3cbaSViresh KUMAR }; 37c63b3cbaSViresh KUMAR 38c63b3cbaSViresh KUMAR /* sdhci ops */ 39c915568dSLars-Peter Clausen static const struct sdhci_ops sdhci_pltfm_ops = { 401771059cSRussell King .set_clock = sdhci_set_clock, 412317f56cSRussell King .set_bus_width = sdhci_set_bus_width, 4203231f9bSRussell King .reset = sdhci_reset, 4396d7b78cSRussell King .set_uhs_signaling = sdhci_set_uhs_signaling, 44c63b3cbaSViresh KUMAR }; 45c63b3cbaSViresh KUMAR 4603a6d291SUlf Hansson static void sdhci_probe_config_dt(struct device_node *np, 4703a6d291SUlf Hansson struct spear_sdhci *host) 48067bf748SViresh Kumar { 49067bf748SViresh Kumar int cd_gpio; 50067bf748SViresh Kumar 51067bf748SViresh Kumar cd_gpio = of_get_named_gpio(np, "cd-gpios", 0); 52067bf748SViresh Kumar if (!gpio_is_valid(cd_gpio)) 53067bf748SViresh Kumar cd_gpio = -1; 54067bf748SViresh Kumar 5503a6d291SUlf Hansson host->card_int_gpio = cd_gpio; 56067bf748SViresh Kumar } 57067bf748SViresh Kumar 58c3be1efdSBill Pemberton static int sdhci_probe(struct platform_device *pdev) 59c63b3cbaSViresh KUMAR { 60c63b3cbaSViresh KUMAR struct sdhci_host *host; 61c63b3cbaSViresh KUMAR struct resource *iomem; 62c63b3cbaSViresh KUMAR struct spear_sdhci *sdhci; 63fcdb7c8fSRussell King struct device *dev; 64c63b3cbaSViresh KUMAR int ret; 65c63b3cbaSViresh KUMAR 66fcdb7c8fSRussell King dev = pdev->dev.parent ? pdev->dev.parent : &pdev->dev; 67fcdb7c8fSRussell King host = sdhci_alloc_host(dev, sizeof(*sdhci)); 68fcdb7c8fSRussell King if (IS_ERR(host)) { 69fcdb7c8fSRussell King ret = PTR_ERR(host); 70c63b3cbaSViresh KUMAR dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n"); 716ebaf8f2SViresh Kumar goto err; 72c63b3cbaSViresh KUMAR } 73c63b3cbaSViresh KUMAR 74475d9e3eSRussell King iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 75475d9e3eSRussell King host->ioaddr = devm_ioremap_resource(&pdev->dev, iomem); 76475d9e3eSRussell King if (IS_ERR(host->ioaddr)) { 77475d9e3eSRussell King ret = PTR_ERR(host->ioaddr); 78475d9e3eSRussell King dev_dbg(&pdev->dev, "unable to map iomem: %d\n", ret); 79475d9e3eSRussell King goto err_host; 80475d9e3eSRussell King } 81475d9e3eSRussell King 82475d9e3eSRussell King host->hw_name = "sdhci"; 83475d9e3eSRussell King host->ops = &sdhci_pltfm_ops; 84475d9e3eSRussell King host->irq = platform_get_irq(pdev, 0); 85475d9e3eSRussell King host->quirks = SDHCI_QUIRK_BROKEN_ADMA; 86475d9e3eSRussell King 87fcdb7c8fSRussell King sdhci = sdhci_priv(host); 88fcdb7c8fSRussell King 89c63b3cbaSViresh KUMAR /* clk enable */ 90142dbab9SRussell King sdhci->clk = devm_clk_get(&pdev->dev, NULL); 91c63b3cbaSViresh KUMAR if (IS_ERR(sdhci->clk)) { 92c63b3cbaSViresh KUMAR ret = PTR_ERR(sdhci->clk); 93c63b3cbaSViresh KUMAR dev_dbg(&pdev->dev, "Error getting clock\n"); 94fcdb7c8fSRussell King goto err_host; 95c63b3cbaSViresh KUMAR } 96c63b3cbaSViresh KUMAR 97da764f97SViresh Kumar ret = clk_prepare_enable(sdhci->clk); 98c63b3cbaSViresh KUMAR if (ret) { 99c63b3cbaSViresh KUMAR dev_dbg(&pdev->dev, "Error enabling clock\n"); 100fcdb7c8fSRussell King goto err_host; 101c63b3cbaSViresh KUMAR } 102c63b3cbaSViresh KUMAR 103257f9df1SVipul Kumar Samar ret = clk_set_rate(sdhci->clk, 50000000); 104257f9df1SVipul Kumar Samar if (ret) 105257f9df1SVipul Kumar Samar dev_dbg(&pdev->dev, "Error setting desired clk, clk=%lu\n", 106257f9df1SVipul Kumar Samar clk_get_rate(sdhci->clk)); 107257f9df1SVipul Kumar Samar 10803a6d291SUlf Hansson sdhci_probe_config_dt(pdev->dev.of_node, sdhci); 109b42b9b12SRussell King /* 110b42b9b12SRussell King * It is optional to use GPIOs for sdhci card detection. If 11103a6d291SUlf Hansson * sdhci->card_int_gpio < 0, then use original sdhci lines otherwise 112b42b9b12SRussell King * GPIO lines. We use the built-in GPIO support for this. 113b42b9b12SRussell King */ 11403a6d291SUlf Hansson if (sdhci->card_int_gpio >= 0) { 11503a6d291SUlf Hansson ret = mmc_gpio_request_cd(host->mmc, sdhci->card_int_gpio, 0); 116b42b9b12SRussell King if (ret < 0) { 117b42b9b12SRussell King dev_dbg(&pdev->dev, 118b42b9b12SRussell King "failed to request card-detect gpio%d\n", 11903a6d291SUlf Hansson sdhci->card_int_gpio); 120b42b9b12SRussell King goto disable_clk; 121b42b9b12SRussell King } 122b42b9b12SRussell King } 123b42b9b12SRussell King 124c63b3cbaSViresh KUMAR ret = sdhci_add_host(host); 125c63b3cbaSViresh KUMAR if (ret) { 126c63b3cbaSViresh KUMAR dev_dbg(&pdev->dev, "error adding host\n"); 127fcdb7c8fSRussell King goto disable_clk; 128c63b3cbaSViresh KUMAR } 129c63b3cbaSViresh KUMAR 130c63b3cbaSViresh KUMAR platform_set_drvdata(pdev, host); 131c63b3cbaSViresh KUMAR 132c63b3cbaSViresh KUMAR return 0; 133c63b3cbaSViresh KUMAR 1346ebaf8f2SViresh Kumar disable_clk: 135da764f97SViresh Kumar clk_disable_unprepare(sdhci->clk); 136fcdb7c8fSRussell King err_host: 137fcdb7c8fSRussell King sdhci_free_host(host); 138c63b3cbaSViresh KUMAR err: 139c63b3cbaSViresh KUMAR dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret); 140c63b3cbaSViresh KUMAR return ret; 141c63b3cbaSViresh KUMAR } 142c63b3cbaSViresh KUMAR 1436e0ee714SBill Pemberton static int sdhci_remove(struct platform_device *pdev) 144c63b3cbaSViresh KUMAR { 145c63b3cbaSViresh KUMAR struct sdhci_host *host = platform_get_drvdata(pdev); 146fcdb7c8fSRussell King struct spear_sdhci *sdhci = sdhci_priv(host); 1476ebaf8f2SViresh Kumar int dead = 0; 148c63b3cbaSViresh KUMAR u32 scratch; 149c63b3cbaSViresh KUMAR 150c63b3cbaSViresh KUMAR scratch = readl(host->ioaddr + SDHCI_INT_STATUS); 151c63b3cbaSViresh KUMAR if (scratch == (u32)-1) 152c63b3cbaSViresh KUMAR dead = 1; 153c63b3cbaSViresh KUMAR 154c63b3cbaSViresh KUMAR sdhci_remove_host(host, dead); 155da764f97SViresh Kumar clk_disable_unprepare(sdhci->clk); 156fcdb7c8fSRussell King sdhci_free_host(host); 157c63b3cbaSViresh KUMAR 158c63b3cbaSViresh KUMAR return 0; 159c63b3cbaSViresh KUMAR } 160c63b3cbaSViresh KUMAR 161b0dd099cSJingoo Han #ifdef CONFIG_PM_SLEEP 162b70a7fabSViresh Kumar static int sdhci_suspend(struct device *dev) 163b70a7fabSViresh Kumar { 164b70a7fabSViresh Kumar struct sdhci_host *host = dev_get_drvdata(dev); 165fcdb7c8fSRussell King struct spear_sdhci *sdhci = sdhci_priv(host); 166b70a7fabSViresh Kumar int ret; 167b70a7fabSViresh Kumar 168984589e5SViresh Kumar ret = sdhci_suspend_host(host); 169b70a7fabSViresh Kumar if (!ret) 17006960a1bSViresh Kumar clk_disable(sdhci->clk); 171b70a7fabSViresh Kumar 172b70a7fabSViresh Kumar return ret; 173b70a7fabSViresh Kumar } 174b70a7fabSViresh Kumar 175b70a7fabSViresh Kumar static int sdhci_resume(struct device *dev) 176b70a7fabSViresh Kumar { 177b70a7fabSViresh Kumar struct sdhci_host *host = dev_get_drvdata(dev); 178fcdb7c8fSRussell King struct spear_sdhci *sdhci = sdhci_priv(host); 179b70a7fabSViresh Kumar int ret; 180b70a7fabSViresh Kumar 18106960a1bSViresh Kumar ret = clk_enable(sdhci->clk); 182b70a7fabSViresh Kumar if (ret) { 183b70a7fabSViresh Kumar dev_dbg(dev, "Resume: Error enabling clock\n"); 184b70a7fabSViresh Kumar return ret; 185b70a7fabSViresh Kumar } 186b70a7fabSViresh Kumar 187b70a7fabSViresh Kumar return sdhci_resume_host(host); 188b70a7fabSViresh Kumar } 189b70a7fabSViresh Kumar #endif 190b70a7fabSViresh Kumar 1914b1a6170SShiraz Hashim static SIMPLE_DEV_PM_OPS(sdhci_pm_ops, sdhci_suspend, sdhci_resume); 1924b1a6170SShiraz Hashim 193067bf748SViresh Kumar #ifdef CONFIG_OF 194067bf748SViresh Kumar static const struct of_device_id sdhci_spear_id_table[] = { 195067bf748SViresh Kumar { .compatible = "st,spear300-sdhci" }, 196067bf748SViresh Kumar {} 197067bf748SViresh Kumar }; 198067bf748SViresh Kumar MODULE_DEVICE_TABLE(of, sdhci_spear_id_table); 199067bf748SViresh Kumar #endif 200067bf748SViresh Kumar 201c63b3cbaSViresh KUMAR static struct platform_driver sdhci_driver = { 202c63b3cbaSViresh KUMAR .driver = { 203c63b3cbaSViresh KUMAR .name = "sdhci", 204b70a7fabSViresh Kumar .pm = &sdhci_pm_ops, 205067bf748SViresh Kumar .of_match_table = of_match_ptr(sdhci_spear_id_table), 206c63b3cbaSViresh KUMAR }, 207c63b3cbaSViresh KUMAR .probe = sdhci_probe, 2080433c143SBill Pemberton .remove = sdhci_remove, 209c63b3cbaSViresh KUMAR }; 210c63b3cbaSViresh KUMAR 211d1f81a64SAxel Lin module_platform_driver(sdhci_driver); 212c63b3cbaSViresh KUMAR 213c63b3cbaSViresh KUMAR MODULE_DESCRIPTION("SPEAr Secure Digital Host Controller Interface driver"); 214da89947bSViresh Kumar MODULE_AUTHOR("Viresh Kumar <vireshk@kernel.org>"); 215c63b3cbaSViresh KUMAR MODULE_LICENSE("GPL v2"); 216