diff options
Diffstat (limited to 'drivers/mmc/host/sdhci-dove.c')
| -rw-r--r-- | drivers/mmc/host/sdhci-dove.c | 114 | 
1 files changed, 91 insertions, 23 deletions
diff --git a/drivers/mmc/host/sdhci-dove.c b/drivers/mmc/host/sdhci-dove.c index 8fd50a21103..169fab91778 100644 --- a/drivers/mmc/host/sdhci-dove.c +++ b/drivers/mmc/host/sdhci-dove.c @@ -19,20 +19,30 @@   * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.   */ -#include <linux/err.h> -#include <linux/io.h>  #include <linux/clk.h>  #include <linux/err.h> -#include <linux/module.h> +#include <linux/gpio.h> +#include <linux/io.h>  #include <linux/mmc/host.h> +#include <linux/module.h>  #include <linux/of.h> +#include <linux/of_gpio.h>  #include "sdhci-pltfm.h"  struct sdhci_dove_priv {  	struct clk *clk; +	int gpio_cd;  }; +static irqreturn_t sdhci_dove_carddetect_irq(int irq, void *data) +{ +	struct sdhci_host *host = data; + +	tasklet_schedule(&host->card_tasklet); +	return IRQ_HANDLED; +} +  static u16 sdhci_dove_readw(struct sdhci_host *host, int reg)  {  	u16 ret; @@ -50,16 +60,25 @@ static u16 sdhci_dove_readw(struct sdhci_host *host, int reg)  static u32 sdhci_dove_readl(struct sdhci_host *host, int reg)  { +	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); +	struct sdhci_dove_priv *priv = pltfm_host->priv;  	u32 ret; +	ret = readl(host->ioaddr + reg); +  	switch (reg) {  	case SDHCI_CAPABILITIES: -		ret = readl(host->ioaddr + reg);  		/* Mask the support for 3.0V */  		ret &= ~SDHCI_CAN_VDD_300;  		break; -	default: -		ret = readl(host->ioaddr + reg); +	case SDHCI_PRESENT_STATE: +		if (gpio_is_valid(priv->gpio_cd)) { +			if (gpio_get_value(priv->gpio_cd) == 0) +				ret |= SDHCI_CARD_PRESENT; +			else +				ret &= ~SDHCI_CARD_PRESENT; +		} +		break;  	}  	return ret;  } @@ -78,7 +97,7 @@ static struct sdhci_pltfm_data sdhci_dove_pdata = {  		  SDHCI_QUIRK_NO_HISPD_BIT,  }; -static int __devinit sdhci_dove_probe(struct platform_device *pdev) +static int sdhci_dove_probe(struct platform_device *pdev)  {  	struct sdhci_host *host;  	struct sdhci_pltfm_host *pltfm_host; @@ -92,29 +111,74 @@ static int __devinit sdhci_dove_probe(struct platform_device *pdev)  		return -ENOMEM;  	} -	priv->clk = clk_get(&pdev->dev, NULL); +	priv->clk = devm_clk_get(&pdev->dev, NULL); + +	if (pdev->dev.of_node) { +		priv->gpio_cd = of_get_named_gpio(pdev->dev.of_node, +						  "cd-gpios", 0); +	} else { +		priv->gpio_cd = -EINVAL; +	} + +	if (gpio_is_valid(priv->gpio_cd)) { +		ret = gpio_request(priv->gpio_cd, "sdhci-cd"); +		if (ret) { +			dev_err(&pdev->dev, "card detect gpio request failed: %d\n", +				ret); +			return ret; +		} +		gpio_direction_input(priv->gpio_cd); +	} + +	host = sdhci_pltfm_init(pdev, &sdhci_dove_pdata); +	if (IS_ERR(host)) { +		ret = PTR_ERR(host); +		goto err_sdhci_pltfm_init; +	} + +	pltfm_host = sdhci_priv(host); +	pltfm_host->priv = priv; +  	if (!IS_ERR(priv->clk))  		clk_prepare_enable(priv->clk); -	ret = sdhci_pltfm_register(pdev, &sdhci_dove_pdata); +	sdhci_get_of_property(pdev); + +	ret = sdhci_add_host(host);  	if (ret) -		goto sdhci_dove_register_fail; +		goto err_sdhci_add; -	host = platform_get_drvdata(pdev); -	pltfm_host = sdhci_priv(host); -	pltfm_host->priv = priv; +	/* +	 * We must request the IRQ after sdhci_add_host(), as the tasklet only +	 * gets setup in sdhci_add_host() and we oops. +	 */ +	if (gpio_is_valid(priv->gpio_cd)) { +		ret = request_irq(gpio_to_irq(priv->gpio_cd), +				  sdhci_dove_carddetect_irq, +				  IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, +				  mmc_hostname(host->mmc), host); +		if (ret) { +			dev_err(&pdev->dev, "card detect irq request failed: %d\n", +				ret); +			goto err_request_irq; +		} +	}  	return 0; -sdhci_dove_register_fail: -	if (!IS_ERR(priv->clk)) { +err_request_irq: +	sdhci_remove_host(host, 0); +err_sdhci_add: +	if (!IS_ERR(priv->clk))  		clk_disable_unprepare(priv->clk); -		clk_put(priv->clk); -	} +	sdhci_pltfm_free(pdev); +err_sdhci_pltfm_init: +	if (gpio_is_valid(priv->gpio_cd)) +		gpio_free(priv->gpio_cd);  	return ret;  } -static int __devexit sdhci_dove_remove(struct platform_device *pdev) +static int sdhci_dove_remove(struct platform_device *pdev)  {  	struct sdhci_host *host = platform_get_drvdata(pdev);  	struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); @@ -122,14 +186,18 @@ static int __devexit sdhci_dove_remove(struct platform_device *pdev)  	sdhci_pltfm_unregister(pdev); -	if (!IS_ERR(priv->clk)) { -		clk_disable_unprepare(priv->clk); -		clk_put(priv->clk); +	if (gpio_is_valid(priv->gpio_cd)) { +		free_irq(gpio_to_irq(priv->gpio_cd), host); +		gpio_free(priv->gpio_cd);  	} + +	if (!IS_ERR(priv->clk)) +		clk_disable_unprepare(priv->clk); +  	return 0;  } -static const struct of_device_id sdhci_dove_of_match_table[] __devinitdata = { +static const struct of_device_id sdhci_dove_of_match_table[] = {  	{ .compatible = "marvell,dove-sdhci", },  	{}  }; @@ -143,7 +211,7 @@ static struct platform_driver sdhci_dove_driver = {  		.of_match_table = of_match_ptr(sdhci_dove_of_match_table),  	},  	.probe		= sdhci_dove_probe, -	.remove		= __devexit_p(sdhci_dove_remove), +	.remove		= sdhci_dove_remove,  };  module_platform_driver(sdhci_dove_driver);  |