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
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
/* 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>
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 ) ;
2009-06-04 15:57:29 +04:00
static struct sdhci_ops sdhci_pltfm_ops = {
} ;
2011-05-27 19:48:14 +04:00
# ifdef CONFIG_OF
static bool sdhci_of_wp_inverted ( struct device_node * np )
{
2012-05-13 08:14:24 +04:00
if ( of_get_property ( np , " sdhci,wp-inverted " , NULL ) | |
of_get_property ( np , " wp-inverted " , NULL ) )
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 */
}
void sdhci_get_of_property ( struct platform_device * pdev )
{
struct device_node * np = pdev - > dev . of_node ;
struct sdhci_host * host = platform_get_drvdata ( pdev ) ;
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
const __be32 * clk ;
2012-05-13 08:14:24 +04:00
u32 bus_width ;
2011-05-27 19:48:14 +04:00
int size ;
if ( of_device_is_available ( np ) ) {
if ( of_get_property ( np , " sdhci,auto-cmd12 " , NULL ) )
host - > quirks | = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 ;
2012-05-13 08:14:24 +04:00
if ( of_get_property ( np , " sdhci,1-bit-only " , NULL ) | |
( of_property_read_u32 ( np , " bus-width " , & bus_width ) = = 0 & &
bus_width = = 1 ) )
2011-05-27 19:48:14 +04:00
host - > quirks | = SDHCI_QUIRK_FORCE_1_BIT_DATA ;
if ( sdhci_of_wp_inverted ( np ) )
host - > quirks | = SDHCI_QUIRK_INVERTED_WRITE_PROTECT ;
2012-09-15 08:20:00 +04:00
if ( of_get_property ( np , " broken-cd " , NULL ) )
host - > quirks | = SDHCI_QUIRK_BROKEN_CARD_DETECTION ;
2012-11-25 22:02:54 +04:00
if ( of_get_property ( np , " no-1-8-v " , NULL ) )
host - > quirks2 | = SDHCI_QUIRK2_NO_1_8_V ;
2012-01-16 10:13:03 +04:00
if ( of_device_is_compatible ( np , " fsl,p2020-rev1-esdhc " ) )
host - > quirks | = SDHCI_QUIRK_BROKEN_DMA ;
2012-01-16 10:13:04 +04:00
if ( of_device_is_compatible ( np , " fsl,p2020-esdhc " ) | |
of_device_is_compatible ( np , " fsl,p1010-esdhc " ) | |
2013-03-08 12:14:10 +04:00
of_device_is_compatible ( np , " fsl,t4240-esdhc " ) | |
2012-01-16 10:13:04 +04:00
of_device_is_compatible ( np , " fsl,mpc8536-esdhc " ) )
host - > quirks | = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL ;
2011-05-27 19:48:14 +04:00
clk = of_get_property ( np , " clock-frequency " , & size ) ;
if ( clk & & size = = sizeof ( * clk ) & & * clk )
pltfm_host - > clock = be32_to_cpup ( clk ) ;
2012-11-19 08:56:20 +04:00
if ( of_find_property ( np , " keep-power-in-suspend " , NULL ) )
host - > mmc - > pm_caps | = MMC_PM_KEEP_POWER ;
if ( of_find_property ( np , " enable-sdio-wakeup " , NULL ) )
host - > mmc - > pm_caps | = MMC_PM_WAKE_SDIO_IRQ ;
2011-05-27 19:48:14 +04:00
}
}
# else
void sdhci_get_of_property ( struct platform_device * pdev ) { }
# endif /* CONFIG_OF */
2011-06-02 06:57:50 +04:00
EXPORT_SYMBOL_GPL ( sdhci_get_of_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 ,
struct sdhci_pltfm_data * pdata )
2009-06-04 15:57:29 +04:00
{
struct sdhci_host * host ;
2010-10-15 14:20:59 +04:00
struct sdhci_pltfm_host * pltfm_host ;
2011-07-01 05:35:28 +04:00
struct device_node * np = pdev - > dev . of_node ;
2009-06-04 15:57:29 +04:00
struct resource * iomem ;
int ret ;
iomem = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( ! iomem ) {
ret = - ENOMEM ;
goto err ;
}
2010-05-27 01:41:56 +04:00
if ( resource_size ( iomem ) < 0x100 )
2011-05-27 19:48:12 +04:00
dev_err ( & pdev - > dev , " Invalid iomem size! \n " ) ;
2009-06-04 15:57:29 +04:00
2010-10-15 14:20:59 +04:00
/* Some PCI-based MFD need the parent here */
2011-07-01 05:35:28 +04:00
if ( pdev - > dev . parent ! = & platform_bus & & ! np )
2010-10-15 14:20:59 +04:00
host = sdhci_alloc_host ( pdev - > dev . parent , sizeof ( * pltfm_host ) ) ;
2009-06-04 15:57:29 +04:00
else
2010-10-15 14:20:59 +04:00
host = sdhci_alloc_host ( & pdev - > dev , sizeof ( * pltfm_host ) ) ;
2009-06-04 15:57:29 +04:00
if ( IS_ERR ( host ) ) {
ret = PTR_ERR ( host ) ;
goto err ;
}
2010-10-15 14:20:59 +04:00
pltfm_host = sdhci_priv ( host ) ;
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 ;
if ( pdata )
host - > quirks = pdata - > quirks ;
2009-06-04 15:57:29 +04:00
host - > irq = platform_get_irq ( pdev , 0 ) ;
if ( ! request_mem_region ( iomem - > start , resource_size ( iomem ) ,
mmc_hostname ( host - > mmc ) ) ) {
dev_err ( & pdev - > dev , " cannot request region \n " ) ;
ret = - EBUSY ;
goto err_request ;
}
host - > ioaddr = ioremap ( iomem - > start , resource_size ( iomem ) ) ;
if ( ! host - > ioaddr ) {
dev_err ( & pdev - > dev , " failed to remap registers \n " ) ;
ret = - ENOMEM ;
goto err_remap ;
}
2012-10-25 09:47:19 +04:00
/*
* Some platforms need to probe the controller to be able to
* determine which caps should be used .
*/
if ( host - > ops & & host - > ops - > platform_init )
host - > ops - > platform_init ( host ) ;
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_remap :
release_mem_region ( iomem - > start , resource_size ( iomem ) ) ;
err_request :
sdhci_free_host ( host ) ;
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 ) ;
struct resource * iomem = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
iounmap ( host - > ioaddr ) ;
release_mem_region ( iomem - > start , resource_size ( iomem ) ) ;
sdhci_free_host ( host ) ;
platform_set_drvdata ( pdev , NULL ) ;
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 ,
struct sdhci_pltfm_data * pdata )
{
struct sdhci_host * host ;
int ret = 0 ;
host = sdhci_pltfm_init ( pdev , pdata ) ;
if ( IS_ERR ( host ) )
return PTR_ERR ( host ) ;
2011-05-27 19:48:14 +04:00
sdhci_get_of_property ( pdev ) ;
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 ) ;
int dead = ( readl ( host - > ioaddr + SDHCI_INT_STATUS ) = = 0xffffffff ) ;
sdhci_remove_host ( host , dead ) ;
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
2010-09-28 12:41:27 +04:00
# ifdef CONFIG_PM
2011-11-03 14:09:45 +04:00
static 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 ) ;
2010-09-28 12:41:27 +04:00
2011-11-03 14:09:45 +04:00
return sdhci_suspend_host ( host ) ;
2010-09-28 12:41:27 +04:00
}
2011-11-03 14:09:45 +04:00
static 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 ) ;
2010-09-28 12:41:27 +04:00
return sdhci_resume_host ( host ) ;
}
2011-11-03 14:09:45 +04:00
const struct dev_pm_ops sdhci_pltfm_pmops = {
. suspend = sdhci_pltfm_suspend ,
. resume = sdhci_pltfm_resume ,
} ;
EXPORT_SYMBOL_GPL ( sdhci_pltfm_pmops ) ;
2010-09-28 12:41:27 +04:00
# endif /* CONFIG_PM */
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 " ) ;