2009-03-17 00:14:05 +03:00
/*
* OpenFirmware bindings for Secure Digital Host Controller Interface .
*
* Copyright ( c ) 2007 Freescale Semiconductor , Inc .
* Copyright ( c ) 2009 MontaVista Software , Inc .
*
* Authors : Xiaobo Xie < X . Xie @ freescale . com >
* Anton Vorontsov < avorontsov @ ru . mvista . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or ( at
* your option ) any later version .
*/
2010-11-16 23:33:52 +03:00
# include <linux/err.h>
2009-03-17 00:14:05 +03:00
# include <linux/module.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/interrupt.h>
# include <linux/delay.h>
# include <linux/of.h>
# include <linux/of_platform.h>
2010-11-16 23:33:52 +03:00
# include <linux/of_address.h>
# include <linux/of_irq.h>
2009-03-17 00:14:05 +03:00
# include <linux/mmc/host.h>
2010-11-16 23:33:52 +03:00
# ifdef CONFIG_PPC
2009-09-23 03:45:15 +04:00
# include <asm/machdep.h>
2010-11-16 23:33:52 +03:00
# endif
2009-12-18 02:27:20 +03:00
# include "sdhci-of.h"
2009-03-17 00:14:05 +03:00
# include "sdhci.h"
2009-12-18 02:27:20 +03:00
# ifdef CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER
2009-03-17 00:14:05 +03:00
/*
2009-12-18 02:27:20 +03:00
* These accessors are designed for big endian hosts doing I / O to
* little endian controllers incorporating a 32 - bit hardware byte swapper .
2009-03-17 00:14:05 +03:00
*/
2009-12-18 02:27:20 +03:00
u32 sdhci_be32bs_readl ( struct sdhci_host * host , int reg )
2009-03-17 00:14:05 +03:00
{
return in_be32 ( host - > ioaddr + reg ) ;
}
2009-12-18 02:27:20 +03:00
u16 sdhci_be32bs_readw ( struct sdhci_host * host , int reg )
2009-03-17 00:14:05 +03:00
{
2009-12-18 02:27:20 +03:00
return in_be16 ( host - > ioaddr + ( reg ^ 0x2 ) ) ;
2009-03-17 00:14:05 +03:00
}
2009-12-18 02:27:20 +03:00
u8 sdhci_be32bs_readb ( struct sdhci_host * host , int reg )
2009-03-17 00:14:05 +03:00
{
return in_8 ( host - > ioaddr + ( reg ^ 0x3 ) ) ;
}
2009-12-18 02:27:20 +03:00
void sdhci_be32bs_writel ( struct sdhci_host * host , u32 val , int reg )
2009-03-17 00:14:05 +03:00
{
out_be32 ( host - > ioaddr + reg , val ) ;
}
2009-12-18 02:27:20 +03:00
void sdhci_be32bs_writew ( struct sdhci_host * host , u16 val , int reg )
2009-03-17 00:14:05 +03:00
{
struct sdhci_of_host * of_host = sdhci_priv ( host ) ;
int base = reg & ~ 0x3 ;
int shift = ( reg & 0x2 ) * 8 ;
switch ( reg ) {
case SDHCI_TRANSFER_MODE :
/*
* Postpone this write , we must do it together with a
* command write that is down below .
*/
of_host - > xfer_mode_shadow = val ;
return ;
case SDHCI_COMMAND :
2009-12-18 02:27:20 +03:00
sdhci_be32bs_writel ( host , val < < 16 | of_host - > xfer_mode_shadow ,
SDHCI_TRANSFER_MODE ) ;
2009-03-17 00:14:05 +03:00
return ;
}
clrsetbits_be32 ( host - > ioaddr + base , 0xffff < < shift , val < < shift ) ;
}
2009-12-18 02:27:20 +03:00
void sdhci_be32bs_writeb ( struct sdhci_host * host , u8 val , int reg )
2009-03-17 00:14:05 +03:00
{
int base = reg & ~ 0x3 ;
int shift = ( reg & 0x3 ) * 8 ;
clrsetbits_be32 ( host - > ioaddr + base , 0xff < < shift , val < < shift ) ;
}
2009-12-18 02:27:20 +03:00
# endif /* CONFIG_MMC_SDHCI_BIG_ENDIAN_32BIT_BYTE_SWAPPER */
2009-03-17 00:14:05 +03:00
# ifdef CONFIG_PM
2010-08-06 19:25:50 +04:00
static int sdhci_of_suspend ( struct platform_device * ofdev , pm_message_t state )
2009-03-17 00:14:05 +03:00
{
struct sdhci_host * host = dev_get_drvdata ( & ofdev - > dev ) ;
2010-05-27 01:42:08 +04:00
return mmc_suspend_host ( host - > mmc ) ;
2009-03-17 00:14:05 +03:00
}
2010-08-06 19:25:50 +04:00
static int sdhci_of_resume ( struct platform_device * ofdev )
2009-03-17 00:14:05 +03:00
{
struct sdhci_host * host = dev_get_drvdata ( & ofdev - > dev ) ;
return mmc_resume_host ( host - > mmc ) ;
}
# else
# define sdhci_of_suspend NULL
# define sdhci_of_resume NULL
# endif
2009-09-23 03:45:15 +04:00
static bool __devinit sdhci_of_wp_inverted ( struct device_node * np )
{
if ( of_get_property ( np , " sdhci,wp-inverted " , NULL ) )
return true ;
/* Old device trees don't have the wp-inverted property. */
2010-11-16 23:33:52 +03:00
# ifdef CONFIG_PPC
2009-09-23 03:45:15 +04:00
return machine_is ( mpc837x_rdb ) | | machine_is ( mpc837x_mds ) ;
2010-11-16 23:33:52 +03:00
# else
return false ;
# endif
2009-09-23 03:45:15 +04:00
}
2011-05-18 21:19:24 +04:00
static const struct of_device_id sdhci_of_match [ ] ;
2011-02-17 12:43:24 +03:00
static int __devinit sdhci_of_probe ( struct platform_device * ofdev )
2009-03-17 00:14:05 +03:00
{
2011-05-18 21:19:24 +04:00
const struct of_device_id * match ;
2010-04-14 03:12:29 +04:00
struct device_node * np = ofdev - > dev . of_node ;
2011-02-17 12:43:24 +03:00
struct sdhci_of_data * sdhci_of_data ;
2009-03-17 00:14:05 +03:00
struct sdhci_host * host ;
struct sdhci_of_host * of_host ;
2010-11-16 23:33:50 +03:00
const __be32 * clk ;
2009-03-17 00:14:05 +03:00
int size ;
int ret ;
2011-05-18 21:19:24 +04:00
match = of_match_device ( sdhci_of_match , & ofdev - > dev ) ;
if ( ! match )
2011-02-17 12:43:24 +03:00
return - EINVAL ;
2011-05-18 21:19:24 +04:00
sdhci_of_data = match - > data ;
2011-02-17 12:43:24 +03:00
2009-03-17 00:14:05 +03:00
if ( ! of_device_is_available ( np ) )
return - ENODEV ;
host = sdhci_alloc_host ( & ofdev - > dev , sizeof ( * of_host ) ) ;
2009-08-07 02:07:41 +04:00
if ( IS_ERR ( host ) )
2009-03-17 00:14:05 +03:00
return - ENOMEM ;
of_host = sdhci_priv ( host ) ;
dev_set_drvdata ( & ofdev - > dev , host ) ;
host - > ioaddr = of_iomap ( np , 0 ) ;
if ( ! host - > ioaddr ) {
ret = - ENOMEM ;
goto err_addr_map ;
}
host - > irq = irq_of_parse_and_map ( np , 0 ) ;
if ( ! host - > irq ) {
ret = - EINVAL ;
goto err_no_irq ;
}
host - > hw_name = dev_name ( & ofdev - > dev ) ;
if ( sdhci_of_data ) {
host - > quirks = sdhci_of_data - > quirks ;
host - > ops = & sdhci_of_data - > ops ;
}
2010-08-11 05:01:59 +04:00
if ( of_get_property ( np , " sdhci,auto-cmd12 " , NULL ) )
host - > quirks | = SDHCI_QUIRK_MULTIBLOCK_READ_ACMD12 ;
2009-06-18 00:14:08 +04:00
if ( of_get_property ( np , " sdhci,1-bit-only " , NULL ) )
host - > quirks | = SDHCI_QUIRK_FORCE_1_BIT_DATA ;
2009-09-23 03:45:15 +04:00
if ( sdhci_of_wp_inverted ( np ) )
host - > quirks | = SDHCI_QUIRK_INVERTED_WRITE_PROTECT ;
2009-03-17 00:14:05 +03:00
clk = of_get_property ( np , " clock-frequency " , & size ) ;
if ( clk & & size = = sizeof ( * clk ) & & * clk )
2010-11-16 23:33:50 +03:00
of_host - > clock = be32_to_cpup ( clk ) ;
2009-03-17 00:14:05 +03:00
ret = sdhci_add_host ( host ) ;
if ( ret )
goto err_add_host ;
return 0 ;
err_add_host :
irq_dispose_mapping ( host - > irq ) ;
err_no_irq :
iounmap ( host - > ioaddr ) ;
err_addr_map :
sdhci_free_host ( host ) ;
return ret ;
}
2010-08-06 19:25:50 +04:00
static int __devexit sdhci_of_remove ( struct platform_device * ofdev )
2009-03-17 00:14:05 +03:00
{
struct sdhci_host * host = dev_get_drvdata ( & ofdev - > dev ) ;
sdhci_remove_host ( host , 0 ) ;
sdhci_free_host ( host ) ;
irq_dispose_mapping ( host - > irq ) ;
iounmap ( host - > ioaddr ) ;
return 0 ;
}
static const struct of_device_id sdhci_of_match [ ] = {
2009-12-18 02:27:20 +03:00
# ifdef CONFIG_MMC_SDHCI_OF_ESDHC
2009-03-17 00:14:05 +03:00
{ . compatible = " fsl,mpc8379-esdhc " , . data = & sdhci_esdhc , } ,
{ . compatible = " fsl,mpc8536-esdhc " , . data = & sdhci_esdhc , } ,
2009-05-08 17:52:49 +04:00
{ . compatible = " fsl,esdhc " , . data = & sdhci_esdhc , } ,
2009-12-18 02:27:20 +03:00
# endif
# ifdef CONFIG_MMC_SDHCI_OF_HLWD
{ . compatible = " nintendo,hollywood-sdhci " , . data = & sdhci_hlwd , } ,
2009-12-18 02:27:20 +03:00
# endif
2009-03-17 00:14:05 +03:00
{ . compatible = " generic-sdhci " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , sdhci_of_match ) ;
2011-02-17 12:43:24 +03:00
static struct platform_driver sdhci_of_driver = {
2010-04-14 03:13:02 +04:00
. driver = {
. name = " sdhci-of " ,
. owner = THIS_MODULE ,
. of_match_table = sdhci_of_match ,
} ,
2009-03-17 00:14:05 +03:00
. probe = sdhci_of_probe ,
. remove = __devexit_p ( sdhci_of_remove ) ,
. suspend = sdhci_of_suspend ,
. resume = sdhci_of_resume ,
} ;
static int __init sdhci_of_init ( void )
{
2011-02-17 12:43:24 +03:00
return platform_driver_register ( & sdhci_of_driver ) ;
2009-03-17 00:14:05 +03:00
}
module_init ( sdhci_of_init ) ;
static void __exit sdhci_of_exit ( void )
{
2011-02-17 12:43:24 +03:00
platform_driver_unregister ( & sdhci_of_driver ) ;
2009-03-17 00:14:05 +03:00
}
module_exit ( sdhci_of_exit ) ;
MODULE_DESCRIPTION ( " Secure Digital Host Controller Interface OF driver " ) ;
MODULE_AUTHOR ( " Xiaobo Xie <X.Xie@freescale.com>, "
" Anton Vorontsov <avorontsov@ru.mvista.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;