2019-05-28 19:57:05 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2009-06-04 15:57:29 +04:00
/*
* sdhci - pltfm . c Support for SDHCI platform devices
* Copyright ( c ) 2009 Intel Corporation
*
2012-01-16 10:13:03 +04:00
* Copyright ( c ) 2007 , 2011 Freescale Semiconductor , Inc .
2011-05-27 19:48:14 +04:00
* Copyright ( c ) 2009 MontaVista Software , Inc .
*
* Authors : Xiaobo Xie < X . Xie @ freescale . com >
* Anton Vorontsov < avorontsov @ ru . mvista . com >
2009-06-04 15:57:29 +04:00
*/
/* Supports:
* SDHCI platform devices
*
* Inspired by sdhci - pci . c , by Pierre Ossman
*/
2011-05-27 19:48:12 +04:00
# include <linux/err.h>
2011-07-03 23:15:51 +04:00
# include <linux/module.h>
2018-08-05 10:52:51 +03:00
# include <linux/property.h>
2011-05-27 19:48:14 +04:00
# include <linux/of.h>
# ifdef CONFIG_PPC
# include <asm/machdep.h>
# endif
2010-08-11 05:01:47 +04:00
# include "sdhci-pltfm.h"
2009-06-04 15:57:29 +04:00
2013-01-28 22:27:12 +04:00
unsigned int sdhci_pltfm_clk_get_max_clock ( struct sdhci_host * host )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
return clk_get_rate ( pltfm_host - > clk ) ;
}
EXPORT_SYMBOL_GPL ( sdhci_pltfm_clk_get_max_clock ) ;
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 ,
2009-06-04 15:57:29 +04:00
} ;
2018-08-05 10:52:51 +03:00
static bool sdhci_wp_inverted ( struct device * dev )
2011-05-27 19:48:14 +04:00
{
2018-08-05 10:52:51 +03:00
if ( device_property_present ( dev , " sdhci,wp-inverted " ) | |
device_property_present ( dev , " wp-inverted " ) )
2011-05-27 19:48:14 +04:00
return true ;
/* Old device trees don't have the wp-inverted property. */
# ifdef CONFIG_PPC
return machine_is ( mpc837x_rdb ) | | machine_is ( mpc837x_mds ) ;
# else
return false ;
# endif /* CONFIG_PPC */
}
2018-08-05 10:52:51 +03:00
# ifdef CONFIG_OF
static void sdhci_get_compatibility ( struct platform_device * pdev )
2011-05-27 19:48:14 +04:00
{
2018-08-05 10:52:51 +03:00
struct sdhci_host * host = platform_get_drvdata ( pdev ) ;
2011-05-27 19:48:14 +04:00
struct device_node * np = pdev - > dev . of_node ;
2018-08-05 10:52:51 +03:00
if ( ! np )
return ;
if ( of_device_is_compatible ( np , " fsl,p2020-rev1-esdhc " ) )
host - > quirks | = SDHCI_QUIRK_BROKEN_DMA ;
if ( of_device_is_compatible ( np , " fsl,p2020-esdhc " ) | |
of_device_is_compatible ( np , " fsl,p1010-esdhc " ) | |
of_device_is_compatible ( np , " fsl,t4240-esdhc " ) | |
of_device_is_compatible ( np , " fsl,mpc8536-esdhc " ) )
host - > quirks | = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL ;
}
# else
void sdhci_get_compatibility ( struct platform_device * pdev ) { }
# endif /* CONFIG_OF */
void sdhci_get_property ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
2011-05-27 19:48:14 +04:00
struct sdhci_host * host = platform_get_drvdata ( pdev ) ;
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
2012-05-13 08:14:24 +04:00
u32 bus_width ;
2011-05-27 19:48:14 +04:00
2018-08-05 10:52:51 +03:00
if ( device_property_present ( dev , " sdhci,auto-cmd12 " ) )
2015-02-04 09:33:53 +03:00
host - > quirks | = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 ;
2011-05-27 19:48:14 +04:00
2018-08-05 10:52:51 +03:00
if ( device_property_present ( dev , " sdhci,1-bit-only " ) | |
( device_property_read_u32 ( dev , " bus-width " , & bus_width ) = = 0 & &
2015-02-04 09:33:53 +03:00
bus_width = = 1 ) )
host - > quirks | = SDHCI_QUIRK_FORCE_1_BIT_DATA ;
2011-05-27 19:48:14 +04:00
2018-08-05 10:52:51 +03:00
if ( sdhci_wp_inverted ( dev ) )
2015-02-04 09:33:53 +03:00
host - > quirks | = SDHCI_QUIRK_INVERTED_WRITE_PROTECT ;
2011-05-27 19:48:14 +04:00
2018-08-05 10:52:51 +03:00
if ( device_property_present ( dev , " broken-cd " ) )
2015-02-04 09:33:53 +03:00
host - > quirks | = SDHCI_QUIRK_BROKEN_CARD_DETECTION ;
2012-09-15 08:20:00 +04:00
2018-08-05 10:52:51 +03:00
if ( device_property_present ( dev , " no-1-8-v " ) )
2015-02-04 09:33:53 +03:00
host - > quirks2 | = SDHCI_QUIRK2_NO_1_8_V ;
2012-11-25 22:02:54 +04:00
2018-08-05 10:52:51 +03:00
sdhci_get_compatibility ( pdev ) ;
2012-01-16 10:13:04 +04:00
2018-08-05 10:52:51 +03:00
device_property_read_u32 ( dev , " clock-frequency " , & pltfm_host - > clock ) ;
2012-11-19 08:56:20 +04:00
2018-08-05 10:52:51 +03:00
if ( device_property_present ( dev , " keep-power-in-suspend " ) )
2015-02-04 09:33:53 +03:00
host - > mmc - > pm_caps | = MMC_PM_KEEP_POWER ;
2012-11-19 08:56:20 +04:00
2018-08-05 10:52:51 +03:00
if ( device_property_read_bool ( dev , " wakeup-source " ) | |
device_property_read_bool ( dev , " enable-sdio-wakeup " ) ) /* legacy */
2015-02-04 09:33:53 +03:00
host - > mmc - > pm_caps | = MMC_PM_WAKE_SDIO_IRQ ;
2011-05-27 19:48:14 +04:00
}
2018-08-05 10:52:51 +03:00
EXPORT_SYMBOL_GPL ( sdhci_get_property ) ;
2011-05-27 19:48:14 +04:00
2011-05-27 19:48:12 +04:00
struct sdhci_host * sdhci_pltfm_init ( struct platform_device * pdev ,
2013-05-30 00:50:05 +04:00
const struct sdhci_pltfm_data * pdata ,
size_t priv_size )
2009-06-04 15:57:29 +04:00
{
struct sdhci_host * host ;
2016-04-20 05:16:30 +03:00
void __iomem * ioaddr ;
2016-04-20 05:16:31 +03:00
int irq , ret ;
2009-06-04 15:57:29 +04:00
2019-07-25 10:28:22 +03:00
ioaddr = devm_platform_ioremap_resource ( pdev , 0 ) ;
2016-04-20 05:16:30 +03:00
if ( IS_ERR ( ioaddr ) ) {
ret = PTR_ERR ( ioaddr ) ;
2009-06-04 15:57:29 +04:00
goto err ;
}
2016-04-20 05:16:31 +03:00
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 ) {
ret = irq ;
goto err ;
}
2014-08-12 14:37:52 +04:00
host = sdhci_alloc_host ( & pdev - > dev ,
sizeof ( struct sdhci_pltfm_host ) + priv_size ) ;
2009-06-04 15:57:29 +04:00
if ( IS_ERR ( host ) ) {
ret = PTR_ERR ( host ) ;
goto err ;
}
2016-04-20 05:16:30 +03:00
host - > ioaddr = ioaddr ;
2016-04-20 05:16:31 +03:00
host - > irq = irq ;
2011-05-27 19:48:12 +04:00
host - > hw_name = dev_name ( & pdev - > dev ) ;
2010-05-27 01:41:55 +04:00
if ( pdata & & pdata - > ops )
host - > ops = pdata - > ops ;
else
host - > ops = & sdhci_pltfm_ops ;
2013-05-26 21:59:26 +04:00
if ( pdata ) {
2010-05-27 01:41:55 +04:00
host - > quirks = pdata - > quirks ;
2013-05-26 21:59:26 +04:00
host - > quirks2 = pdata - > quirks2 ;
}
2009-06-04 15:57:29 +04:00
platform_set_drvdata ( pdev , host ) ;
2011-05-27 19:48:12 +04:00
return host ;
2009-06-04 15:57:29 +04:00
err :
2011-05-27 19:48:12 +04:00
dev_err ( & pdev - > dev , " %s failed %d \n " , __func__ , ret ) ;
return ERR_PTR ( ret ) ;
2009-06-04 15:57:29 +04:00
}
2011-06-02 06:57:50 +04:00
EXPORT_SYMBOL_GPL ( sdhci_pltfm_init ) ;
2009-06-04 15:57:29 +04:00
2011-05-27 19:48:12 +04:00
void sdhci_pltfm_free ( struct platform_device * pdev )
2009-06-04 15:57:29 +04:00
{
struct sdhci_host * host = platform_get_drvdata ( pdev ) ;
sdhci_free_host ( host ) ;
2011-05-27 19:48:12 +04:00
}
2011-06-02 06:57:50 +04:00
EXPORT_SYMBOL_GPL ( sdhci_pltfm_free ) ;
2009-06-04 15:57:29 +04:00
2011-05-27 19:48:12 +04:00
int sdhci_pltfm_register ( struct platform_device * pdev ,
2013-05-30 00:50:05 +04:00
const struct sdhci_pltfm_data * pdata ,
size_t priv_size )
2011-05-27 19:48:12 +04:00
{
struct sdhci_host * host ;
int ret = 0 ;
2013-05-30 00:50:05 +04:00
host = sdhci_pltfm_init ( pdev , pdata , priv_size ) ;
2011-05-27 19:48:12 +04:00
if ( IS_ERR ( host ) )
return PTR_ERR ( host ) ;
2018-08-05 10:52:51 +03:00
sdhci_get_property ( pdev ) ;
2011-05-27 19:48:14 +04:00
2011-05-27 19:48:12 +04:00
ret = sdhci_add_host ( host ) ;
if ( ret )
sdhci_pltfm_free ( pdev ) ;
return ret ;
2009-06-04 15:57:29 +04:00
}
2011-06-02 06:57:50 +04:00
EXPORT_SYMBOL_GPL ( sdhci_pltfm_register ) ;
2009-06-04 15:57:29 +04:00
2011-05-27 19:48:12 +04:00
int sdhci_pltfm_unregister ( struct platform_device * pdev )
{
struct sdhci_host * host = platform_get_drvdata ( pdev ) ;
2015-02-27 10:47:30 +03:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
2011-05-27 19:48:12 +04:00
int dead = ( readl ( host - > ioaddr + SDHCI_INT_STATUS ) = = 0xffffffff ) ;
sdhci_remove_host ( host , dead ) ;
2015-02-27 10:47:30 +03:00
clk_disable_unprepare ( pltfm_host - > clk ) ;
2011-05-27 19:48:12 +04:00
sdhci_pltfm_free ( pdev ) ;
return 0 ;
}
2011-06-02 06:57:50 +04:00
EXPORT_SYMBOL_GPL ( sdhci_pltfm_unregister ) ;
2010-08-11 05:01:47 +04:00
2016-07-27 13:48:04 +03:00
# ifdef CONFIG_PM_SLEEP
2017-08-23 07:15:03 +03:00
int sdhci_pltfm_suspend ( struct device * dev )
2010-09-28 12:41:27 +04:00
{
2011-11-03 14:09:45 +04:00
struct sdhci_host * host = dev_get_drvdata ( dev ) ;
2017-08-23 07:15:02 +03:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
int ret ;
2010-09-28 12:41:27 +04:00
2017-03-20 20:50:32 +03:00
if ( host - > tuning_mode ! = SDHCI_TUNING_MODE_3 )
mmc_retune_needed ( host - > mmc ) ;
2017-08-23 07:15:02 +03:00
ret = sdhci_suspend_host ( host ) ;
if ( ret )
return ret ;
clk_disable_unprepare ( pltfm_host - > clk ) ;
return 0 ;
2010-09-28 12:41:27 +04:00
}
2017-08-23 07:15:03 +03:00
EXPORT_SYMBOL_GPL ( sdhci_pltfm_suspend ) ;
2010-09-28 12:41:27 +04:00
2017-08-23 07:15:03 +03:00
int sdhci_pltfm_resume ( struct device * dev )
2010-09-28 12:41:27 +04:00
{
2011-11-03 14:09:45 +04:00
struct sdhci_host * host = dev_get_drvdata ( dev ) ;
2017-08-23 07:15:02 +03:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
int ret ;
ret = clk_prepare_enable ( pltfm_host - > clk ) ;
if ( ret )
return ret ;
2010-09-28 12:41:27 +04:00
2017-08-23 07:15:02 +03:00
ret = sdhci_resume_host ( host ) ;
if ( ret )
clk_disable_unprepare ( pltfm_host - > clk ) ;
return ret ;
2010-09-28 12:41:27 +04:00
}
2017-08-23 07:15:03 +03:00
EXPORT_SYMBOL_GPL ( sdhci_pltfm_resume ) ;
2016-07-27 13:48:04 +03:00
# endif
2011-11-03 14:09:45 +04:00
const struct dev_pm_ops sdhci_pltfm_pmops = {
2016-07-27 13:48:04 +03:00
SET_SYSTEM_SLEEP_PM_OPS ( sdhci_pltfm_suspend , sdhci_pltfm_resume )
2011-11-03 14:09:45 +04:00
} ;
EXPORT_SYMBOL_GPL ( sdhci_pltfm_pmops ) ;
2011-06-02 06:57:50 +04:00
static int __init sdhci_pltfm_drv_init ( void )
{
pr_info ( " sdhci-pltfm: SDHCI platform and OF driver helper \n " ) ;
return 0 ;
}
module_init ( sdhci_pltfm_drv_init ) ;
static void __exit sdhci_pltfm_drv_exit ( void )
{
}
module_exit ( sdhci_pltfm_drv_exit ) ;
MODULE_DESCRIPTION ( " SDHCI platform and OF driver helper " ) ;
MODULE_AUTHOR ( " Intel Corporation " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;