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 86985b1aa0SMike Rapoport static struct sdhci_ops sdhci_dove_ops = { 87985b1aa0SMike Rapoport .read_w = sdhci_dove_readw, 88985b1aa0SMike Rapoport .read_l = sdhci_dove_readl, 89985b1aa0SMike Rapoport }; 90985b1aa0SMike Rapoport 9185d6509dSShawn Guo static struct sdhci_pltfm_data sdhci_dove_pdata = { 92985b1aa0SMike Rapoport .ops = &sdhci_dove_ops, 93985b1aa0SMike Rapoport .quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER | 94985b1aa0SMike Rapoport SDHCI_QUIRK_NO_BUSY_IRQ | 95985b1aa0SMike Rapoport SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 96a9ca1d54SSebastian Hesselbarth SDHCI_QUIRK_FORCE_DMA | 97a9ca1d54SSebastian Hesselbarth SDHCI_QUIRK_NO_HISPD_BIT, 98985b1aa0SMike Rapoport }; 9985d6509dSShawn Guo 10085d6509dSShawn Guo static int __devinit sdhci_dove_probe(struct platform_device *pdev) 10185d6509dSShawn Guo { 10230b87c60SSebastian Hesselbarth struct sdhci_host *host; 10330b87c60SSebastian Hesselbarth struct sdhci_pltfm_host *pltfm_host; 10430b87c60SSebastian Hesselbarth struct sdhci_dove_priv *priv; 10530b87c60SSebastian Hesselbarth int ret; 10630b87c60SSebastian Hesselbarth 10730b87c60SSebastian Hesselbarth priv = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_dove_priv), 10830b87c60SSebastian Hesselbarth GFP_KERNEL); 10930b87c60SSebastian Hesselbarth if (!priv) { 11030b87c60SSebastian Hesselbarth dev_err(&pdev->dev, "unable to allocate private data"); 111ee3298a2SRussell King - ARM Linux return -ENOMEM; 11230b87c60SSebastian Hesselbarth } 11330b87c60SSebastian Hesselbarth 1147430e77eSRussell King priv->clk = devm_clk_get(&pdev->dev, NULL); 115ee3298a2SRussell King - ARM Linux 116f8ec589bSRussell King if (pdev->dev.of_node) { 117f8ec589bSRussell King priv->gpio_cd = of_get_named_gpio(pdev->dev.of_node, 118f8ec589bSRussell King "cd-gpios", 0); 119f8ec589bSRussell King } else { 120f8ec589bSRussell King priv->gpio_cd = -EINVAL; 121f8ec589bSRussell King } 122f8ec589bSRussell King 123f8ec589bSRussell King if (gpio_is_valid(priv->gpio_cd)) { 124f8ec589bSRussell King ret = gpio_request(priv->gpio_cd, "sdhci-cd"); 125f8ec589bSRussell King if (ret) { 126f8ec589bSRussell King dev_err(&pdev->dev, "card detect gpio request failed: %d\n", 127f8ec589bSRussell King ret); 128f8ec589bSRussell King return ret; 129f8ec589bSRussell King } 130f8ec589bSRussell King gpio_direction_input(priv->gpio_cd); 131f8ec589bSRussell King } 132f8ec589bSRussell King 133c430689fSRussell King host = sdhci_pltfm_init(pdev, &sdhci_dove_pdata); 134c430689fSRussell King if (IS_ERR(host)) { 135c430689fSRussell King ret = PTR_ERR(host); 136c430689fSRussell King goto err_sdhci_pltfm_init; 137c430689fSRussell King } 138ee3298a2SRussell King - ARM Linux 13930b87c60SSebastian Hesselbarth pltfm_host = sdhci_priv(host); 14030b87c60SSebastian Hesselbarth pltfm_host->priv = priv; 14130b87c60SSebastian Hesselbarth 142c430689fSRussell King if (!IS_ERR(priv->clk)) 143c430689fSRussell King clk_prepare_enable(priv->clk); 144c430689fSRussell King 145c430689fSRussell King sdhci_get_of_property(pdev); 146c430689fSRussell King 147c430689fSRussell King ret = sdhci_add_host(host); 148c430689fSRussell King if (ret) 149c430689fSRussell King goto err_sdhci_add; 150c430689fSRussell King 151f8ec589bSRussell King /* 152f8ec589bSRussell King * We must request the IRQ after sdhci_add_host(), as the tasklet only 153f8ec589bSRussell King * gets setup in sdhci_add_host() and we oops. 154f8ec589bSRussell King */ 155f8ec589bSRussell King if (gpio_is_valid(priv->gpio_cd)) { 156f8ec589bSRussell King ret = request_irq(gpio_to_irq(priv->gpio_cd), 157f8ec589bSRussell King sdhci_dove_carddetect_irq, 158f8ec589bSRussell King IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, 159f8ec589bSRussell King mmc_hostname(host->mmc), host); 160f8ec589bSRussell King if (ret) { 161f8ec589bSRussell King dev_err(&pdev->dev, "card detect irq request failed: %d\n", 162f8ec589bSRussell King ret); 163f8ec589bSRussell King goto err_request_irq; 164f8ec589bSRussell King } 165f8ec589bSRussell King } 166f8ec589bSRussell King 16730b87c60SSebastian Hesselbarth return 0; 16830b87c60SSebastian Hesselbarth 169f8ec589bSRussell King err_request_irq: 170f8ec589bSRussell King sdhci_remove_host(host, 0); 171c430689fSRussell King err_sdhci_add: 1727430e77eSRussell King if (!IS_ERR(priv->clk)) 173ee3298a2SRussell King - ARM Linux clk_disable_unprepare(priv->clk); 174c430689fSRussell King sdhci_pltfm_free(pdev); 175c430689fSRussell King err_sdhci_pltfm_init: 176f8ec589bSRussell King if (gpio_is_valid(priv->gpio_cd)) 177f8ec589bSRussell King gpio_free(priv->gpio_cd); 17830b87c60SSebastian Hesselbarth return ret; 17985d6509dSShawn Guo } 18085d6509dSShawn Guo 18185d6509dSShawn Guo static int __devexit sdhci_dove_remove(struct platform_device *pdev) 18285d6509dSShawn Guo { 18330b87c60SSebastian Hesselbarth struct sdhci_host *host = platform_get_drvdata(pdev); 18430b87c60SSebastian Hesselbarth struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 18530b87c60SSebastian Hesselbarth struct sdhci_dove_priv *priv = pltfm_host->priv; 18630b87c60SSebastian Hesselbarth 187ee3298a2SRussell King - ARM Linux sdhci_pltfm_unregister(pdev); 188ee3298a2SRussell King - ARM Linux 189f8ec589bSRussell King if (gpio_is_valid(priv->gpio_cd)) { 190f8ec589bSRussell King free_irq(gpio_to_irq(priv->gpio_cd), host); 191f8ec589bSRussell King gpio_free(priv->gpio_cd); 192f8ec589bSRussell King } 193f8ec589bSRussell King 1947430e77eSRussell King if (!IS_ERR(priv->clk)) 19530b87c60SSebastian Hesselbarth clk_disable_unprepare(priv->clk); 1967430e77eSRussell King 197ee3298a2SRussell King - ARM Linux return 0; 19885d6509dSShawn Guo } 19985d6509dSShawn Guo 2004ee7ed0dSSebastian Hesselbarth static const struct of_device_id sdhci_dove_of_match_table[] __devinitdata = { 2014ee7ed0dSSebastian Hesselbarth { .compatible = "marvell,dove-sdhci", }, 2024ee7ed0dSSebastian Hesselbarth {} 2034ee7ed0dSSebastian Hesselbarth }; 2044ee7ed0dSSebastian Hesselbarth MODULE_DEVICE_TABLE(of, sdhci_dove_of_match_table); 2054ee7ed0dSSebastian Hesselbarth 20685d6509dSShawn Guo static struct platform_driver sdhci_dove_driver = { 20785d6509dSShawn Guo .driver = { 20885d6509dSShawn Guo .name = "sdhci-dove", 20985d6509dSShawn Guo .owner = THIS_MODULE, 21029495aa0SManuel Lauss .pm = SDHCI_PLTFM_PMOPS, 2114ee7ed0dSSebastian Hesselbarth .of_match_table = of_match_ptr(sdhci_dove_of_match_table), 21285d6509dSShawn Guo }, 21385d6509dSShawn Guo .probe = sdhci_dove_probe, 21485d6509dSShawn Guo .remove = __devexit_p(sdhci_dove_remove), 21585d6509dSShawn Guo }; 21685d6509dSShawn Guo 217d1f81a64SAxel Lin module_platform_driver(sdhci_dove_driver); 21885d6509dSShawn Guo 21985d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Dove"); 22085d6509dSShawn Guo MODULE_AUTHOR("Saeed Bishara <saeed@marvell.com>, " 22185d6509dSShawn Guo "Mike Rapoport <mike@compulab.co.il>"); 22285d6509dSShawn Guo MODULE_LICENSE("GPL v2"); 223