2010-05-27 01:42:10 +04:00
/*
* drivers / mmc / host / sdhci - spear . c
*
* Support of SDHCI platform devices for spear soc family
*
* Copyright ( C ) 2010 ST Microelectronics
2015-07-18 02:23:50 +03:00
* Viresh Kumar < vireshk @ kernel . org >
2010-05-27 01:42:10 +04:00
*
* Inspired by sdhci - pltfm . c
*
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
# include <linux/clk.h>
# include <linux/delay.h>
# include <linux/gpio.h>
# include <linux/highmem.h>
2011-07-03 23:15:51 +04:00
# include <linux/module.h>
2010-05-27 01:42:10 +04:00
# include <linux/interrupt.h>
# include <linux/irq.h>
2012-09-28 14:28:22 +04:00
# include <linux/of.h>
# include <linux/of_gpio.h>
2010-05-27 01:42:10 +04:00
# include <linux/platform_device.h>
2011-11-15 14:54:40 +04:00
# include <linux/pm.h>
2010-05-27 01:42:10 +04:00
# include <linux/slab.h>
# include <linux/mmc/host.h>
2014-03-01 01:32:49 +04:00
# include <linux/mmc/slot-gpio.h>
2010-05-27 01:42:10 +04:00
# include <linux/io.h>
# include "sdhci.h"
struct spear_sdhci {
struct clk * clk ;
2015-03-04 18:30:07 +03:00
int card_int_gpio ;
2010-05-27 01:42:10 +04:00
} ;
/* sdhci ops */
2013-03-13 22:26:05 +04:00
static const struct sdhci_ops sdhci_pltfm_ops = {
2014-04-25 15:58:55 +04:00
. set_clock = sdhci_set_clock ,
2014-04-25 15:57:07 +04:00
. set_bus_width = sdhci_set_bus_width ,
2014-04-25 15:57:12 +04:00
. reset = sdhci_reset ,
2014-04-25 15:59:26 +04:00
. set_uhs_signaling = sdhci_set_uhs_signaling ,
2010-05-27 01:42:10 +04:00
} ;
2015-03-04 18:30:07 +03:00
static void sdhci_probe_config_dt ( struct device_node * np ,
struct spear_sdhci * host )
2012-09-28 14:28:22 +04:00
{
int cd_gpio ;
cd_gpio = of_get_named_gpio ( np , " cd-gpios " , 0 ) ;
if ( ! gpio_is_valid ( cd_gpio ) )
cd_gpio = - 1 ;
2015-03-04 18:30:07 +03:00
host - > card_int_gpio = cd_gpio ;
2012-09-28 14:28:22 +04:00
}
2012-11-19 22:23:06 +04:00
static int sdhci_probe ( struct platform_device * pdev )
2010-05-27 01:42:10 +04:00
{
struct sdhci_host * host ;
struct resource * iomem ;
struct spear_sdhci * sdhci ;
2014-03-01 01:32:34 +04:00
struct device * dev ;
2010-05-27 01:42:10 +04:00
int ret ;
2014-03-01 01:32:34 +04:00
dev = pdev - > dev . parent ? pdev - > dev . parent : & pdev - > dev ;
host = sdhci_alloc_host ( dev , sizeof ( * sdhci ) ) ;
if ( IS_ERR ( host ) ) {
ret = PTR_ERR ( host ) ;
2010-05-27 01:42:10 +04:00
dev_dbg ( & pdev - > dev , " cannot allocate memory for sdhci \n " ) ;
2012-03-27 07:10:35 +04:00
goto err ;
2010-05-27 01:42:10 +04:00
}
2014-03-01 01:32:39 +04:00
iomem = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
host - > ioaddr = devm_ioremap_resource ( & pdev - > dev , iomem ) ;
if ( IS_ERR ( host - > ioaddr ) ) {
ret = PTR_ERR ( host - > ioaddr ) ;
dev_dbg ( & pdev - > dev , " unable to map iomem: %d \n " , ret ) ;
goto err_host ;
}
host - > hw_name = " sdhci " ;
host - > ops = & sdhci_pltfm_ops ;
host - > irq = platform_get_irq ( pdev , 0 ) ;
2017-11-19 07:52:46 +03:00
if ( host - > irq < = 0 ) {
ret = - EINVAL ;
goto err_host ;
}
2014-03-01 01:32:39 +04:00
host - > quirks = SDHCI_QUIRK_BROKEN_ADMA ;
2014-03-01 01:32:34 +04:00
sdhci = sdhci_priv ( host ) ;
2010-05-27 01:42:10 +04:00
/* clk enable */
2014-03-01 01:32:29 +04:00
sdhci - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
2010-05-27 01:42:10 +04:00
if ( IS_ERR ( sdhci - > clk ) ) {
ret = PTR_ERR ( sdhci - > clk ) ;
dev_dbg ( & pdev - > dev , " Error getting clock \n " ) ;
2014-03-01 01:32:34 +04:00
goto err_host ;
2010-05-27 01:42:10 +04:00
}
2012-09-28 14:28:23 +04:00
ret = clk_prepare_enable ( sdhci - > clk ) ;
2010-05-27 01:42:10 +04:00
if ( ret ) {
dev_dbg ( & pdev - > dev , " Error enabling clock \n " ) ;
2014-03-01 01:32:34 +04:00
goto err_host ;
2010-05-27 01:42:10 +04:00
}
2012-11-08 19:09:09 +04:00
ret = clk_set_rate ( sdhci - > clk , 50000000 ) ;
if ( ret )
dev_dbg ( & pdev - > dev , " Error setting desired clk, clk=%lu \n " ,
clk_get_rate ( sdhci - > clk ) ) ;
2015-03-04 18:30:07 +03:00
sdhci_probe_config_dt ( pdev - > dev . of_node , sdhci ) ;
2014-03-01 01:32:49 +04:00
/*
* It is optional to use GPIOs for sdhci card detection . If
2015-03-04 18:30:07 +03:00
* sdhci - > card_int_gpio < 0 , then use original sdhci lines otherwise
2014-03-01 01:32:49 +04:00
* GPIO lines . We use the built - in GPIO support for this .
*/
2015-03-04 18:30:07 +03:00
if ( sdhci - > card_int_gpio > = 0 ) {
ret = mmc_gpio_request_cd ( host - > mmc , sdhci - > card_int_gpio , 0 ) ;
2014-03-01 01:32:49 +04:00
if ( ret < 0 ) {
dev_dbg ( & pdev - > dev ,
" failed to request card-detect gpio%d \n " ,
2015-03-04 18:30:07 +03:00
sdhci - > card_int_gpio ) ;
2014-03-01 01:32:49 +04:00
goto disable_clk ;
}
}
2010-05-27 01:42:10 +04:00
ret = sdhci_add_host ( host ) ;
2018-05-25 10:15:09 +03:00
if ( ret )
2014-03-01 01:32:34 +04:00
goto disable_clk ;
2010-05-27 01:42:10 +04:00
platform_set_drvdata ( pdev , host ) ;
return 0 ;
2012-03-27 07:10:35 +04:00
disable_clk :
2012-09-28 14:28:23 +04:00
clk_disable_unprepare ( sdhci - > clk ) ;
2014-03-01 01:32:34 +04:00
err_host :
sdhci_free_host ( host ) ;
2010-05-27 01:42:10 +04:00
err :
dev_err ( & pdev - > dev , " spear-sdhci probe failed: %d \n " , ret ) ;
return ret ;
}
2012-11-19 22:26:03 +04:00
static int sdhci_remove ( struct platform_device * pdev )
2010-05-27 01:42:10 +04:00
{
struct sdhci_host * host = platform_get_drvdata ( pdev ) ;
2014-03-01 01:32:34 +04:00
struct spear_sdhci * sdhci = sdhci_priv ( host ) ;
2012-03-27 07:10:35 +04:00
int dead = 0 ;
2010-05-27 01:42:10 +04:00
u32 scratch ;
scratch = readl ( host - > ioaddr + SDHCI_INT_STATUS ) ;
if ( scratch = = ( u32 ) - 1 )
dead = 1 ;
sdhci_remove_host ( host , dead ) ;
2012-09-28 14:28:23 +04:00
clk_disable_unprepare ( sdhci - > clk ) ;
2014-03-01 01:32:34 +04:00
sdhci_free_host ( host ) ;
2010-05-27 01:42:10 +04:00
return 0 ;
}
2013-03-29 11:08:00 +04:00
# ifdef CONFIG_PM_SLEEP
2011-11-15 14:54:40 +04:00
static int sdhci_suspend ( struct device * dev )
{
struct sdhci_host * host = dev_get_drvdata ( dev ) ;
2014-03-01 01:32:34 +04:00
struct spear_sdhci * sdhci = sdhci_priv ( host ) ;
2011-11-15 14:54:40 +04:00
int ret ;
2017-03-20 20:50:32 +03:00
if ( host - > tuning_mode ! = SDHCI_TUNING_MODE_3 )
mmc_retune_needed ( host - > mmc ) ;
2012-01-04 10:18:42 +04:00
ret = sdhci_suspend_host ( host ) ;
2011-11-15 14:54:40 +04:00
if ( ! ret )
2012-11-08 19:09:10 +04:00
clk_disable ( sdhci - > clk ) ;
2011-11-15 14:54:40 +04:00
return ret ;
}
static int sdhci_resume ( struct device * dev )
{
struct sdhci_host * host = dev_get_drvdata ( dev ) ;
2014-03-01 01:32:34 +04:00
struct spear_sdhci * sdhci = sdhci_priv ( host ) ;
2011-11-15 14:54:40 +04:00
int ret ;
2012-11-08 19:09:10 +04:00
ret = clk_enable ( sdhci - > clk ) ;
2011-11-15 14:54:40 +04:00
if ( ret ) {
dev_dbg ( dev , " Resume: Error enabling clock \n " ) ;
return ret ;
}
return sdhci_resume_host ( host ) ;
}
# endif
2012-02-24 10:25:35 +04:00
static SIMPLE_DEV_PM_OPS ( sdhci_pm_ops , sdhci_suspend , sdhci_resume ) ;
2012-09-28 14:28:22 +04:00
# ifdef CONFIG_OF
static const struct of_device_id sdhci_spear_id_table [ ] = {
{ . compatible = " st,spear300-sdhci " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , sdhci_spear_id_table ) ;
# endif
2010-05-27 01:42:10 +04:00
static struct platform_driver sdhci_driver = {
. driver = {
. name = " sdhci " ,
2011-11-15 14:54:40 +04:00
. pm = & sdhci_pm_ops ,
2012-09-28 14:28:22 +04:00
. of_match_table = of_match_ptr ( sdhci_spear_id_table ) ,
2010-05-27 01:42:10 +04:00
} ,
. probe = sdhci_probe ,
2012-11-19 22:20:26 +04:00
. remove = sdhci_remove ,
2010-05-27 01:42:10 +04:00
} ;
2011-11-26 08:55:43 +04:00
module_platform_driver ( sdhci_driver ) ;
2010-05-27 01:42:10 +04:00
MODULE_DESCRIPTION ( " SPEAr Secure Digital Host Controller Interface driver " ) ;
2015-07-18 02:23:50 +03:00
MODULE_AUTHOR ( " Viresh Kumar <vireshk@kernel.org> " ) ;
2010-05-27 01:42:10 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;