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/highmem.h> 1988b47679SPaul Gortmaker #include <linux/module.h> 20c63b3cbaSViresh KUMAR #include <linux/interrupt.h> 21c63b3cbaSViresh KUMAR #include <linux/irq.h> 22067bf748SViresh Kumar #include <linux/of.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> 27b42b9b12SRussell King #include <linux/mmc/slot-gpio.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 }; 34c63b3cbaSViresh KUMAR 35c63b3cbaSViresh KUMAR /* sdhci ops */ 36c915568dSLars-Peter Clausen static const struct sdhci_ops sdhci_pltfm_ops = { 371771059cSRussell King .set_clock = sdhci_set_clock, 382317f56cSRussell King .set_bus_width = sdhci_set_bus_width, 3903231f9bSRussell King .reset = sdhci_reset, 4096d7b78cSRussell King .set_uhs_signaling = sdhci_set_uhs_signaling, 41c63b3cbaSViresh KUMAR }; 42c63b3cbaSViresh KUMAR 43c3be1efdSBill Pemberton static int sdhci_probe(struct platform_device *pdev) 44c63b3cbaSViresh KUMAR { 45c63b3cbaSViresh KUMAR struct sdhci_host *host; 46c63b3cbaSViresh KUMAR struct spear_sdhci *sdhci; 47fcdb7c8fSRussell King struct device *dev; 48c63b3cbaSViresh KUMAR int ret; 49c63b3cbaSViresh KUMAR 50fcdb7c8fSRussell King dev = pdev->dev.parent ? pdev->dev.parent : &pdev->dev; 51fcdb7c8fSRussell King host = sdhci_alloc_host(dev, sizeof(*sdhci)); 52fcdb7c8fSRussell King if (IS_ERR(host)) { 53fcdb7c8fSRussell King ret = PTR_ERR(host); 54c63b3cbaSViresh KUMAR dev_dbg(&pdev->dev, "cannot allocate memory for sdhci\n"); 556ebaf8f2SViresh Kumar goto err; 56c63b3cbaSViresh KUMAR } 57c63b3cbaSViresh KUMAR 58b47c43f6SYangtao Li host->ioaddr = devm_platform_ioremap_resource(pdev, 0); 59475d9e3eSRussell King if (IS_ERR(host->ioaddr)) { 60475d9e3eSRussell King ret = PTR_ERR(host->ioaddr); 61475d9e3eSRussell King dev_dbg(&pdev->dev, "unable to map iomem: %d\n", ret); 62475d9e3eSRussell King goto err_host; 63475d9e3eSRussell King } 64475d9e3eSRussell King 65475d9e3eSRussell King host->hw_name = "sdhci"; 66475d9e3eSRussell King host->ops = &sdhci_pltfm_ops; 67475d9e3eSRussell King host->irq = platform_get_irq(pdev, 0); 68682798a5SArvind Yadav if (host->irq <= 0) { 69682798a5SArvind Yadav ret = -EINVAL; 70682798a5SArvind Yadav goto err_host; 71682798a5SArvind Yadav } 72475d9e3eSRussell King host->quirks = SDHCI_QUIRK_BROKEN_ADMA; 73475d9e3eSRussell King 74fcdb7c8fSRussell King sdhci = sdhci_priv(host); 75fcdb7c8fSRussell King 76c63b3cbaSViresh KUMAR /* clk enable */ 77142dbab9SRussell King sdhci->clk = devm_clk_get(&pdev->dev, NULL); 78c63b3cbaSViresh KUMAR if (IS_ERR(sdhci->clk)) { 79c63b3cbaSViresh KUMAR ret = PTR_ERR(sdhci->clk); 80c63b3cbaSViresh KUMAR dev_dbg(&pdev->dev, "Error getting clock\n"); 81fcdb7c8fSRussell King goto err_host; 82c63b3cbaSViresh KUMAR } 83c63b3cbaSViresh KUMAR 84da764f97SViresh Kumar ret = clk_prepare_enable(sdhci->clk); 85c63b3cbaSViresh KUMAR if (ret) { 86c63b3cbaSViresh KUMAR dev_dbg(&pdev->dev, "Error enabling clock\n"); 87fcdb7c8fSRussell King goto err_host; 88c63b3cbaSViresh KUMAR } 89c63b3cbaSViresh KUMAR 90257f9df1SVipul Kumar Samar ret = clk_set_rate(sdhci->clk, 50000000); 91257f9df1SVipul Kumar Samar if (ret) 92257f9df1SVipul Kumar Samar dev_dbg(&pdev->dev, "Error setting desired clk, clk=%lu\n", 93257f9df1SVipul Kumar Samar clk_get_rate(sdhci->clk)); 94257f9df1SVipul Kumar Samar 95b42b9b12SRussell King /* 96b007c4ceSLinus Walleij * It is optional to use GPIOs for sdhci card detection. If we 97b007c4ceSLinus Walleij * find a descriptor using slot GPIO, we use it. 98b42b9b12SRussell King */ 99d0052ad9SMichał Mirosław ret = mmc_gpiod_request_cd(host->mmc, "cd", 0, false, 0); 100b007c4ceSLinus Walleij if (ret == -EPROBE_DEFER) 101b42b9b12SRussell King goto disable_clk; 102b42b9b12SRussell King 103c63b3cbaSViresh KUMAR ret = sdhci_add_host(host); 104fb8617e1SJisheng Zhang if (ret) 105fcdb7c8fSRussell King goto disable_clk; 106c63b3cbaSViresh KUMAR 107c63b3cbaSViresh KUMAR platform_set_drvdata(pdev, host); 108c63b3cbaSViresh KUMAR 109c63b3cbaSViresh KUMAR return 0; 110c63b3cbaSViresh KUMAR 1116ebaf8f2SViresh Kumar disable_clk: 112da764f97SViresh Kumar clk_disable_unprepare(sdhci->clk); 113fcdb7c8fSRussell King err_host: 114fcdb7c8fSRussell King sdhci_free_host(host); 115c63b3cbaSViresh KUMAR err: 116c63b3cbaSViresh KUMAR dev_err(&pdev->dev, "spear-sdhci probe failed: %d\n", ret); 117c63b3cbaSViresh KUMAR return ret; 118c63b3cbaSViresh KUMAR } 119c63b3cbaSViresh KUMAR 1206e0ee714SBill Pemberton static int sdhci_remove(struct platform_device *pdev) 121c63b3cbaSViresh KUMAR { 122c63b3cbaSViresh KUMAR struct sdhci_host *host = platform_get_drvdata(pdev); 123fcdb7c8fSRussell King struct spear_sdhci *sdhci = sdhci_priv(host); 1246ebaf8f2SViresh Kumar int dead = 0; 125c63b3cbaSViresh KUMAR u32 scratch; 126c63b3cbaSViresh KUMAR 127c63b3cbaSViresh KUMAR scratch = readl(host->ioaddr + SDHCI_INT_STATUS); 128c63b3cbaSViresh KUMAR if (scratch == (u32)-1) 129c63b3cbaSViresh KUMAR dead = 1; 130c63b3cbaSViresh KUMAR 131c63b3cbaSViresh KUMAR sdhci_remove_host(host, dead); 132da764f97SViresh Kumar clk_disable_unprepare(sdhci->clk); 133fcdb7c8fSRussell King sdhci_free_host(host); 134c63b3cbaSViresh KUMAR 135c63b3cbaSViresh KUMAR return 0; 136c63b3cbaSViresh KUMAR } 137c63b3cbaSViresh KUMAR 138b0dd099cSJingoo Han #ifdef CONFIG_PM_SLEEP 139b70a7fabSViresh Kumar static int sdhci_suspend(struct device *dev) 140b70a7fabSViresh Kumar { 141b70a7fabSViresh Kumar struct sdhci_host *host = dev_get_drvdata(dev); 142fcdb7c8fSRussell King struct spear_sdhci *sdhci = sdhci_priv(host); 143b70a7fabSViresh Kumar int ret; 144b70a7fabSViresh Kumar 145d38dcad4SAdrian Hunter if (host->tuning_mode != SDHCI_TUNING_MODE_3) 146d38dcad4SAdrian Hunter mmc_retune_needed(host->mmc); 147d38dcad4SAdrian Hunter 148984589e5SViresh Kumar ret = sdhci_suspend_host(host); 149b70a7fabSViresh Kumar if (!ret) 15006960a1bSViresh Kumar clk_disable(sdhci->clk); 151b70a7fabSViresh Kumar 152b70a7fabSViresh Kumar return ret; 153b70a7fabSViresh Kumar } 154b70a7fabSViresh Kumar 155b70a7fabSViresh Kumar static int sdhci_resume(struct device *dev) 156b70a7fabSViresh Kumar { 157b70a7fabSViresh Kumar struct sdhci_host *host = dev_get_drvdata(dev); 158fcdb7c8fSRussell King struct spear_sdhci *sdhci = sdhci_priv(host); 159b70a7fabSViresh Kumar int ret; 160b70a7fabSViresh Kumar 16106960a1bSViresh Kumar ret = clk_enable(sdhci->clk); 162b70a7fabSViresh Kumar if (ret) { 163b70a7fabSViresh Kumar dev_dbg(dev, "Resume: Error enabling clock\n"); 164b70a7fabSViresh Kumar return ret; 165b70a7fabSViresh Kumar } 166b70a7fabSViresh Kumar 167b70a7fabSViresh Kumar return sdhci_resume_host(host); 168b70a7fabSViresh Kumar } 169b70a7fabSViresh Kumar #endif 170b70a7fabSViresh Kumar 1714b1a6170SShiraz Hashim static SIMPLE_DEV_PM_OPS(sdhci_pm_ops, sdhci_suspend, sdhci_resume); 1724b1a6170SShiraz Hashim 173067bf748SViresh Kumar #ifdef CONFIG_OF 174067bf748SViresh Kumar static const struct of_device_id sdhci_spear_id_table[] = { 175067bf748SViresh Kumar { .compatible = "st,spear300-sdhci" }, 176067bf748SViresh Kumar {} 177067bf748SViresh Kumar }; 178067bf748SViresh Kumar MODULE_DEVICE_TABLE(of, sdhci_spear_id_table); 179067bf748SViresh Kumar #endif 180067bf748SViresh Kumar 181c63b3cbaSViresh KUMAR static struct platform_driver sdhci_driver = { 182c63b3cbaSViresh KUMAR .driver = { 183c63b3cbaSViresh KUMAR .name = "sdhci", 18421b2cec6SDouglas Anderson .probe_type = PROBE_PREFER_ASYNCHRONOUS, 185b70a7fabSViresh Kumar .pm = &sdhci_pm_ops, 186067bf748SViresh Kumar .of_match_table = of_match_ptr(sdhci_spear_id_table), 187c63b3cbaSViresh KUMAR }, 188c63b3cbaSViresh KUMAR .probe = sdhci_probe, 1890433c143SBill Pemberton .remove = sdhci_remove, 190c63b3cbaSViresh KUMAR }; 191c63b3cbaSViresh KUMAR 192d1f81a64SAxel Lin module_platform_driver(sdhci_driver); 193c63b3cbaSViresh KUMAR 194c63b3cbaSViresh KUMAR MODULE_DESCRIPTION("SPEAr Secure Digital Host Controller Interface driver"); 195da89947bSViresh Kumar MODULE_AUTHOR("Viresh Kumar <vireshk@kernel.org>"); 196c63b3cbaSViresh KUMAR MODULE_LICENSE("GPL v2"); 197