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, 891771059cSRussell King .set_clock = sdhci_set_clock, 902317f56cSRussell King .set_bus_width = sdhci_set_bus_width, 9103231f9bSRussell King .reset = sdhci_reset, 9296d7b78cSRussell King .set_uhs_signaling = sdhci_set_uhs_signaling, 93985b1aa0SMike Rapoport }; 94985b1aa0SMike Rapoport 951db5eebfSLars-Peter Clausen static const struct sdhci_pltfm_data sdhci_dove_pdata = { 96985b1aa0SMike Rapoport .ops = &sdhci_dove_ops, 97985b1aa0SMike Rapoport .quirks = SDHCI_QUIRK_NO_SIMULT_VDD_AND_POWER | 98985b1aa0SMike Rapoport SDHCI_QUIRK_NO_BUSY_IRQ | 99985b1aa0SMike Rapoport SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | 100a9ca1d54SSebastian Hesselbarth SDHCI_QUIRK_FORCE_DMA | 101a9ca1d54SSebastian Hesselbarth SDHCI_QUIRK_NO_HISPD_BIT, 102985b1aa0SMike Rapoport }; 10385d6509dSShawn Guo 104c3be1efdSBill Pemberton static int sdhci_dove_probe(struct platform_device *pdev) 10585d6509dSShawn Guo { 10630b87c60SSebastian Hesselbarth struct sdhci_host *host; 10730b87c60SSebastian Hesselbarth struct sdhci_pltfm_host *pltfm_host; 10830b87c60SSebastian Hesselbarth struct sdhci_dove_priv *priv; 10930b87c60SSebastian Hesselbarth int ret; 11030b87c60SSebastian Hesselbarth 11130b87c60SSebastian Hesselbarth priv = devm_kzalloc(&pdev->dev, sizeof(struct sdhci_dove_priv), 11230b87c60SSebastian Hesselbarth GFP_KERNEL); 11330b87c60SSebastian Hesselbarth if (!priv) { 11430b87c60SSebastian Hesselbarth dev_err(&pdev->dev, "unable to allocate private data"); 115ee3298a2SRussell King - ARM Linux return -ENOMEM; 11630b87c60SSebastian Hesselbarth } 11730b87c60SSebastian Hesselbarth 1187430e77eSRussell King priv->clk = devm_clk_get(&pdev->dev, NULL); 119ee3298a2SRussell King - ARM Linux 120f8ec589bSRussell King if (pdev->dev.of_node) { 121f8ec589bSRussell King priv->gpio_cd = of_get_named_gpio(pdev->dev.of_node, 122f8ec589bSRussell King "cd-gpios", 0); 123f8ec589bSRussell King } else { 124f8ec589bSRussell King priv->gpio_cd = -EINVAL; 125f8ec589bSRussell King } 126f8ec589bSRussell King 127f8ec589bSRussell King if (gpio_is_valid(priv->gpio_cd)) { 128f8ec589bSRussell King ret = gpio_request(priv->gpio_cd, "sdhci-cd"); 129f8ec589bSRussell King if (ret) { 130f8ec589bSRussell King dev_err(&pdev->dev, "card detect gpio request failed: %d\n", 131f8ec589bSRussell King ret); 132f8ec589bSRussell King return ret; 133f8ec589bSRussell King } 134f8ec589bSRussell King gpio_direction_input(priv->gpio_cd); 135f8ec589bSRussell King } 136f8ec589bSRussell King 1370e748234SChristian Daudt host = sdhci_pltfm_init(pdev, &sdhci_dove_pdata, 0); 138c430689fSRussell King if (IS_ERR(host)) { 139c430689fSRussell King ret = PTR_ERR(host); 140c430689fSRussell King goto err_sdhci_pltfm_init; 141c430689fSRussell King } 142ee3298a2SRussell King - ARM Linux 14330b87c60SSebastian Hesselbarth pltfm_host = sdhci_priv(host); 14430b87c60SSebastian Hesselbarth pltfm_host->priv = priv; 14530b87c60SSebastian Hesselbarth 146c430689fSRussell King if (!IS_ERR(priv->clk)) 147c430689fSRussell King clk_prepare_enable(priv->clk); 148c430689fSRussell King 149c430689fSRussell King sdhci_get_of_property(pdev); 150c430689fSRussell King 151c430689fSRussell King ret = sdhci_add_host(host); 152c430689fSRussell King if (ret) 153c430689fSRussell King goto err_sdhci_add; 154c430689fSRussell King 155f8ec589bSRussell King /* 156f8ec589bSRussell King * We must request the IRQ after sdhci_add_host(), as the tasklet only 157f8ec589bSRussell King * gets setup in sdhci_add_host() and we oops. 158f8ec589bSRussell King */ 159f8ec589bSRussell King if (gpio_is_valid(priv->gpio_cd)) { 160f8ec589bSRussell King ret = request_irq(gpio_to_irq(priv->gpio_cd), 161f8ec589bSRussell King sdhci_dove_carddetect_irq, 162f8ec589bSRussell King IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, 163f8ec589bSRussell King mmc_hostname(host->mmc), host); 164f8ec589bSRussell King if (ret) { 165f8ec589bSRussell King dev_err(&pdev->dev, "card detect irq request failed: %d\n", 166f8ec589bSRussell King ret); 167f8ec589bSRussell King goto err_request_irq; 168f8ec589bSRussell King } 169f8ec589bSRussell King } 170f8ec589bSRussell King 17130b87c60SSebastian Hesselbarth return 0; 17230b87c60SSebastian Hesselbarth 173f8ec589bSRussell King err_request_irq: 174f8ec589bSRussell King sdhci_remove_host(host, 0); 175c430689fSRussell King err_sdhci_add: 1767430e77eSRussell King if (!IS_ERR(priv->clk)) 177ee3298a2SRussell King - ARM Linux clk_disable_unprepare(priv->clk); 178c430689fSRussell King sdhci_pltfm_free(pdev); 179c430689fSRussell King err_sdhci_pltfm_init: 180f8ec589bSRussell King if (gpio_is_valid(priv->gpio_cd)) 181f8ec589bSRussell King gpio_free(priv->gpio_cd); 18230b87c60SSebastian Hesselbarth return ret; 18385d6509dSShawn Guo } 18485d6509dSShawn Guo 1856e0ee714SBill Pemberton static int sdhci_dove_remove(struct platform_device *pdev) 18685d6509dSShawn Guo { 18730b87c60SSebastian Hesselbarth struct sdhci_host *host = platform_get_drvdata(pdev); 18830b87c60SSebastian Hesselbarth struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); 18930b87c60SSebastian Hesselbarth struct sdhci_dove_priv *priv = pltfm_host->priv; 19030b87c60SSebastian Hesselbarth 191ee3298a2SRussell King - ARM Linux sdhci_pltfm_unregister(pdev); 192ee3298a2SRussell King - ARM Linux 193f8ec589bSRussell King if (gpio_is_valid(priv->gpio_cd)) { 194f8ec589bSRussell King free_irq(gpio_to_irq(priv->gpio_cd), host); 195f8ec589bSRussell King gpio_free(priv->gpio_cd); 196f8ec589bSRussell King } 197f8ec589bSRussell King 1987430e77eSRussell King if (!IS_ERR(priv->clk)) 19930b87c60SSebastian Hesselbarth clk_disable_unprepare(priv->clk); 2007430e77eSRussell King 201ee3298a2SRussell King - ARM Linux return 0; 20285d6509dSShawn Guo } 20385d6509dSShawn Guo 204498d83e7SBill Pemberton static const struct of_device_id sdhci_dove_of_match_table[] = { 2054ee7ed0dSSebastian Hesselbarth { .compatible = "marvell,dove-sdhci", }, 2064ee7ed0dSSebastian Hesselbarth {} 2074ee7ed0dSSebastian Hesselbarth }; 2084ee7ed0dSSebastian Hesselbarth MODULE_DEVICE_TABLE(of, sdhci_dove_of_match_table); 2094ee7ed0dSSebastian Hesselbarth 21085d6509dSShawn Guo static struct platform_driver sdhci_dove_driver = { 21185d6509dSShawn Guo .driver = { 21285d6509dSShawn Guo .name = "sdhci-dove", 21385d6509dSShawn Guo .owner = THIS_MODULE, 21429495aa0SManuel Lauss .pm = SDHCI_PLTFM_PMOPS, 2155941fd07SSachin Kamat .of_match_table = sdhci_dove_of_match_table, 21685d6509dSShawn Guo }, 21785d6509dSShawn Guo .probe = sdhci_dove_probe, 2180433c143SBill Pemberton .remove = sdhci_dove_remove, 21985d6509dSShawn Guo }; 22085d6509dSShawn Guo 221d1f81a64SAxel Lin module_platform_driver(sdhci_dove_driver); 22285d6509dSShawn Guo 22385d6509dSShawn Guo MODULE_DESCRIPTION("SDHCI driver for Dove"); 22485d6509dSShawn Guo MODULE_AUTHOR("Saeed Bishara <saeed@marvell.com>, " 22585d6509dSShawn Guo "Mike Rapoport <mike@compulab.co.il>"); 22685d6509dSShawn Guo MODULE_LICENSE("GPL v2"); 227