1985b1aa0SMike Rapoport /* 2985b1aa0SMike Rapoport * sdhci-dove.c Support for SDHCI on Marvell's Dove SoC 3985b1aa0SMike Rapoport * 4985b1aa0SMike Rapoport * Author: Saeed Bishara <saeed@marvell.com> 5985b1aa0SMike Rapoport * Mike Rapoport <mike@compulab.co.il> 6985b1aa0SMike Rapoport * Based on sdhci-cns3xxx.c 7985b1aa0SMike Rapoport * 8985b1aa0SMike Rapoport * This program is free software; you can redistribute it and/or modify 9985b1aa0SMike Rapoport * it under the terms of the GNU General Public License version 2 as 10985b1aa0SMike Rapoport * published by the Free Software Foundation. 11985b1aa0SMike Rapoport * 12985b1aa0SMike Rapoport * This program is distributed in the hope that it will be useful, 13985b1aa0SMike Rapoport * but WITHOUT ANY WARRANTY; without even the implied warranty of 14985b1aa0SMike Rapoport * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15985b1aa0SMike Rapoport * GNU General Public License for more details. 16985b1aa0SMike Rapoport * 17985b1aa0SMike Rapoport * You should have received a copy of the GNU General Public License 18985b1aa0SMike Rapoport * along with this program; if not, write to the Free Software 19985b1aa0SMike Rapoport * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20985b1aa0SMike Rapoport */ 21985b1aa0SMike Rapoport 2230b87c60SSebastian Hesselbarth #include <linux/clk.h> 2330b87c60SSebastian Hesselbarth #include <linux/err.h> 24f8ec589bSRussell King #include <linux/gpio.h> 25f8ec589bSRussell King #include <linux/io.h> 26985b1aa0SMike Rapoport #include <linux/mmc/host.h> 27f8ec589bSRussell King #include <linux/module.h> 284ee7ed0dSSebastian Hesselbarth #include <linux/of.h> 29f8ec589bSRussell King #include <linux/of_gpio.h> 30985b1aa0SMike Rapoport 31985b1aa0SMike Rapoport #include "sdhci-pltfm.h" 32985b1aa0SMike Rapoport 3330b87c60SSebastian Hesselbarth struct sdhci_dove_priv { 3430b87c60SSebastian Hesselbarth struct clk *clk; 35f8ec589bSRussell King int gpio_cd; 3630b87c60SSebastian Hesselbarth }; 3730b87c60SSebastian Hesselbarth 38f8ec589bSRussell King static irqreturn_t sdhci_dove_carddetect_irq(int irq, void *data) 39f8ec589bSRussell King { 40f8ec589bSRussell King struct sdhci_host *host = data; 41f8ec589bSRussell King 42f8ec589bSRussell King tasklet_schedule(&host->card_tasklet); 43f8ec589bSRussell King return IRQ_HANDLED; 44f8ec589bSRussell King } 45f8ec589bSRussell King 46985b1aa0SMike Rapoport static u16 sdhci_dove_readw(struct sdhci_host *host, int reg) 47985b1aa0SMike Rapoport { 48985b1aa0SMike Rapoport u16 ret; 49985b1aa0SMike Rapoport 50985b1aa0SMike Rapoport switch (reg) { 51985b1aa0SMike Rapoport case SDHCI_HOST_VERSION: 52985b1aa0SMike Rapoport case SDHCI_SLOT_INT_STATUS: 53985b1aa0SMike Rapoport /* those registers don't exist */ 54985b1aa0SMike Rapoport return 0; 55985b1aa0SMike Rapoport default: 56985b1aa0SMike Rapoport ret = readw(host->ioaddr + reg); 57985b1aa0SMike Rapoport } 58985b1aa0SMike Rapoport return ret; 59985b1aa0SMike Rapoport } 60985b1aa0SMike Rapoport 61985b1aa0SMike Rapoport static u32 sdhci_dove_readl(struct sdhci_host *host, int reg) 62985b1aa0SMike Rapoport { 63f8ec589bSRussell King struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 64f8ec589bSRussell King struct sdhci_dove_priv *priv = pltfm_host->priv; 65985b1aa0SMike Rapoport u32 ret; 66985b1aa0SMike Rapoport 67f8ec589bSRussell King ret = readl(host->ioaddr + reg); 68f8ec589bSRussell King 69985b1aa0SMike Rapoport switch (reg) { 70985b1aa0SMike Rapoport case SDHCI_CAPABILITIES: 71985b1aa0SMike Rapoport /* Mask the support for 3.0V */ 72985b1aa0SMike Rapoport ret &= ~SDHCI_CAN_VDD_300; 73985b1aa0SMike Rapoport break; 74f8ec589bSRussell King case SDHCI_PRESENT_STATE: 75f8ec589bSRussell King if (gpio_is_valid(priv->gpio_cd)) { 76f8ec589bSRussell King if (gpio_get_value(priv->gpio_cd) == 0) 77f8ec589bSRussell King ret |= SDHCI_CARD_PRESENT; 78f8ec589bSRussell King else 79f8ec589bSRussell King ret &= ~SDHCI_CARD_PRESENT; 80f8ec589bSRussell King } 81f8ec589bSRussell King break; 82985b1aa0SMike Rapoport } 83985b1aa0SMike Rapoport return ret; 84985b1aa0SMike Rapoport } 85985b1aa0SMike Rapoport 86c915568dSLars-Peter Clausen static const struct sdhci_ops sdhci_dove_ops = { 87985b1aa0SMike Rapoport .read_w = sdhci_dove_readw, 88985b1aa0SMike Rapoport .read_l = sdhci_dove_readl, 892317f56cSRussell King .set_bus_width = sdhci_set_bus_width, 90985b1aa0SMike Rapoport }; 91985b1aa0SMike Rapoport 921db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_dove_pdata = { 93985b1aa0SMike Rapoport .ops = &sdhci_dove_ops, 94985b1aa0SMike Rapoport .quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER | 95985b1aa0SMike Rapoport SDHCI_QUIRK_NO_BUSY_IRQ | 96985b1aa0SMike Rapoport SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 97a9ca1d54SSebastian Hesselbarth SDHCI_QUIRK_FORCE_DMA | 98a9ca1d54SSebastian Hesselbarth SDHCI_QUIRK_NO_HISPD_BIT, 99985b1aa0SMike Rapoport }; 10085d6509dSShawn Guo 101c3be1efdSBill Pemberton static int sdhci_dove_probe(struct platform_device *pdev) 10285d6509dSShawn Guo { 10330b87c60SSebastian Hesselbarth struct sdhci_host *host; 10430b87c60SSebastian Hesselbarth struct sdhci_pltfm_host *pltfm_host; 10530b87c60SSebastian Hesselbarth struct sdhci_dove_priv *priv; 10630b87c60SSebastian Hesselbarth int ret; 10730b87c60SSebastian Hesselbarth 10830b87c60SSebastian Hesselbarth priv = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_dove_priv), 10930b87c60SSebastian Hesselbarth GFP_KERNEL); 11030b87c60SSebastian Hesselbarth if (!priv) { 11130b87c60SSebastian Hesselbarth dev_err(&pdev->dev, "unable to allocate private data"); 112ee3298a2SRussell King - ARM Linux return -ENOMEM; 11330b87c60SSebastian Hesselbarth } 11430b87c60SSebastian Hesselbarth 1157430e77eSRussell King priv->clk = devm_clk_get(&pdev->dev, NULL); 116ee3298a2SRussell King - ARM Linux 117f8ec589bSRussell King if (pdev->dev.of_node) { 118f8ec589bSRussell King priv->gpio_cd = of_get_named_gpio(pdev->dev.of_node, 119f8ec589bSRussell King "cd-gpios", 0); 120f8ec589bSRussell King } else { 121f8ec589bSRussell King priv->gpio_cd = -EINVAL; 122f8ec589bSRussell King } 123f8ec589bSRussell King 124f8ec589bSRussell King if (gpio_is_valid(priv->gpio_cd)) { 125f8ec589bSRussell King ret = gpio_request(priv->gpio_cd, "sdhci-cd"); 126f8ec589bSRussell King if (ret) { 127f8ec589bSRussell King dev_err(&pdev->dev, "card detect gpio request failed: %d\n", 128f8ec589bSRussell King ret); 129f8ec589bSRussell King return ret; 130f8ec589bSRussell King } 131f8ec589bSRussell King gpio_direction_input(priv->gpio_cd); 132f8ec589bSRussell King } 133f8ec589bSRussell King 1340e748234SChristian Daudt host = sdhci_pltfm_init(pdev, &sdhci_dove_pdata, 0); 135c430689fSRussell King if (IS_ERR(host)) { 136c430689fSRussell King ret = PTR_ERR(host); 137c430689fSRussell King goto err_sdhci_pltfm_init; 138c430689fSRussell King } 139ee3298a2SRussell King - ARM Linux 14030b87c60SSebastian Hesselbarth pltfm_host = sdhci_priv(host); 14130b87c60SSebastian Hesselbarth pltfm_host->priv = priv; 14230b87c60SSebastian Hesselbarth 143c430689fSRussell King if (!IS_ERR(priv->clk)) 144c430689fSRussell King clk_prepare_enable(priv->clk); 145c430689fSRussell King 146c430689fSRussell King sdhci_get_of_property(pdev); 147c430689fSRussell King 148c430689fSRussell King ret = sdhci_add_host(host); 149c430689fSRussell King if (ret) 150c430689fSRussell King goto err_sdhci_add; 151c430689fSRussell King 152f8ec589bSRussell King /* 153f8ec589bSRussell King * We must request the IRQ after sdhci_add_host(), as the tasklet only 154f8ec589bSRussell King * gets setup in sdhci_add_host() and we oops. 155f8ec589bSRussell King */ 156f8ec589bSRussell King if (gpio_is_valid(priv->gpio_cd)) { 157f8ec589bSRussell King ret = request_irq(gpio_to_irq(priv->gpio_cd), 158f8ec589bSRussell King sdhci_dove_carddetect_irq, 159f8ec589bSRussell King IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, 160f8ec589bSRussell King mmc_hostname(host->mmc), host); 161f8ec589bSRussell King if (ret) { 162f8ec589bSRussell King dev_err(&pdev->dev, "card detect irq request failed: %d\n", 163f8ec589bSRussell King ret); 164f8ec589bSRussell King goto err_request_irq; 165f8ec589bSRussell King } 166f8ec589bSRussell King } 167f8ec589bSRussell King 16830b87c60SSebastian Hesselbarth return 0; 16930b87c60SSebastian Hesselbarth 170f8ec589bSRussell King err_request_irq: 171f8ec589bSRussell King sdhci_remove_host(host, 0); 172c430689fSRussell King err_sdhci_add: 1737430e77eSRussell King if (!IS_ERR(priv->clk)) 174ee3298a2SRussell King - ARM Linux clk_disable_unprepare(priv->clk); 175c430689fSRussell King sdhci_pltfm_free(pdev); 176c430689fSRussell King err_sdhci_pltfm_init: 177f8ec589bSRussell King if (gpio_is_valid(priv->gpio_cd)) 178f8ec589bSRussell King gpio_free(priv->gpio_cd); 17930b87c60SSebastian Hesselbarth return ret; 18085d6509dSShawn Guo } 18185d6509dSShawn Guo 1826e0ee714SBill Pemberton static int sdhci_dove_remove(struct platform_device *pdev) 18385d6509dSShawn Guo { 18430b87c60SSebastian Hesselbarth struct sdhci_host *host = platform_get_drvdata(pdev); 18530b87c60SSebastian Hesselbarth struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 18630b87c60SSebastian Hesselbarth struct sdhci_dove_priv *priv = pltfm_host->priv; 18730b87c60SSebastian Hesselbarth 188ee3298a2SRussell King - ARM Linux sdhci_pltfm_unregister(pdev); 189ee3298a2SRussell King - ARM Linux 190f8ec589bSRussell King if (gpio_is_valid(priv->gpio_cd)) { 191f8ec589bSRussell King free_irq(gpio_to_irq(priv->gpio_cd), host); 192f8ec589bSRussell King gpio_free(priv->gpio_cd); 193f8ec589bSRussell King } 194f8ec589bSRussell King 1957430e77eSRussell King if (!IS_ERR(priv->clk)) 19630b87c60SSebastian Hesselbarth clk_disable_unprepare(priv->clk); 1977430e77eSRussell King 198ee3298a2SRussell King - ARM Linux return 0; 19985d6509dSShawn Guo } 20085d6509dSShawn Guo 201498d83e7SBill Pemberton static const struct of_device_id sdhci_dove_of_match_table[] = { 2024ee7ed0dSSebastian Hesselbarth { .compatible = "marvell,dove-sdhci", }, 2034ee7ed0dSSebastian Hesselbarth {} 2044ee7ed0dSSebastian Hesselbarth }; 2054ee7ed0dSSebastian Hesselbarth MODULE_DEVICE_TABLE(of, sdhci_dove_of_match_table); 2064ee7ed0dSSebastian Hesselbarth 20785d6509dSShawn Guo static struct platform_driver sdhci_dove_driver = { 20885d6509dSShawn Guo .driver = { 20985d6509dSShawn Guo .name = "sdhci-dove", 21085d6509dSShawn Guo .owner = THIS_MODULE, 21129495aa0SManuel Lauss .pm = SDHCI_PLTFM_PMOPS, 2125941fd07SSachin Kamat .of_match_table = sdhci_dove_of_match_table, 21385d6509dSShawn Guo }, 21485d6509dSShawn Guo .probe = sdhci_dove_probe, 2150433c143SBill Pemberton .remove = sdhci_dove_remove, 21685d6509dSShawn Guo }; 21785d6509dSShawn Guo 218d1f81a64SAxel Lin module_platform_driver(sdhci_dove_driver); 21985d6509dSShawn Guo 22085d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Dove"); 22185d6509dSShawn Guo MODULE_AUTHOR("Saeed Bishara <saeed@marvell.com>, " 22285d6509dSShawn Guo "Mike Rapoport <mike@compulab.co.il>"); 22385d6509dSShawn Guo MODULE_LICENSE("GPL v2"); 224