2009-12-18 02:27:20 +03:00
/*
* Freescale eSDHC controller driver .
*
2012-02-14 10:05:37 +04:00
* Copyright ( c ) 2007 , 2010 , 2012 Freescale Semiconductor , Inc .
2009-12-18 02:27:20 +03:00
* 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 .
*/
# include <linux/io.h>
2012-02-14 10:05:37 +04:00
# include <linux/of.h>
2009-12-18 02:27:20 +03:00
# include <linux/delay.h>
2011-07-03 23:15:51 +04:00
# include <linux/module.h>
2009-12-18 02:27:20 +03:00
# include <linux/mmc/host.h>
2011-05-27 19:48:14 +04:00
# include "sdhci-pltfm.h"
2010-10-15 14:21:03 +04:00
# include "sdhci-esdhc.h"
2009-12-18 02:27:20 +03:00
static u16 esdhc_readw ( struct sdhci_host * host , int reg )
{
u16 ret ;
2011-09-09 16:05:46 +04:00
int base = reg & ~ 0x3 ;
int shift = ( reg & 0x2 ) * 8 ;
2009-12-18 02:27:20 +03:00
if ( unlikely ( reg = = SDHCI_HOST_VERSION ) )
2011-09-09 16:05:46 +04:00
ret = in_be32 ( host - > ioaddr + base ) & 0xffff ;
2009-12-18 02:27:20 +03:00
else
2011-09-09 16:05:46 +04:00
ret = ( in_be32 ( host - > ioaddr + base ) > > shift ) & 0xffff ;
return ret ;
}
static u8 esdhc_readb ( struct sdhci_host * host , int reg )
{
int base = reg & ~ 0x3 ;
int shift = ( reg & 0x3 ) * 8 ;
u8 ret = ( in_be32 ( host - > ioaddr + base ) > > shift ) & 0xff ;
2012-01-13 11:02:01 +04:00
/*
* " DMA select " locates at offset 0x28 in SD specification , but on
* P5020 or P3041 , it locates at 0x29 .
*/
if ( reg = = SDHCI_HOST_CONTROL ) {
u32 dma_bits ;
dma_bits = in_be32 ( host - > ioaddr + reg ) ;
/* DMA select is 22,23 bits in Protocol Control Register */
dma_bits = ( dma_bits > > 5 ) & SDHCI_CTRL_DMA_MASK ;
/* fixup the result */
ret & = ~ SDHCI_CTRL_DMA_MASK ;
ret | = dma_bits ;
}
2009-12-18 02:27:20 +03:00
return ret ;
}
static void esdhc_writew ( struct sdhci_host * host , u16 val , int reg )
{
if ( reg = = SDHCI_BLOCK_SIZE ) {
/*
* Two last DMA bits are reserved , and first one is used for
* non - standard blksz of 4096 bytes that we don ' t support
* yet . So clear the DMA boundary bits .
*/
val & = ~ SDHCI_MAKE_BLKSZ ( 0x7 , 0 ) ;
}
sdhci_be32bs_writew ( host , val , reg ) ;
}
static void esdhc_writeb ( struct sdhci_host * host , u8 val , int reg )
{
2012-01-13 11:02:01 +04:00
/*
* " DMA select " location is offset 0x28 in SD specification , but on
* P5020 or P3041 , it ' s located at 0x29 .
*/
if ( reg = = SDHCI_HOST_CONTROL ) {
u32 dma_bits ;
/* DMA select is 22,23 bits in Protocol Control Register */
dma_bits = ( val & SDHCI_CTRL_DMA_MASK ) < < 5 ;
clrsetbits_be32 ( host - > ioaddr + reg , SDHCI_CTRL_DMA_MASK < < 5 ,
dma_bits ) ;
val & = ~ SDHCI_CTRL_DMA_MASK ;
val | = in_be32 ( host - > ioaddr + reg ) & SDHCI_CTRL_DMA_MASK ;
}
2009-12-18 02:27:20 +03:00
/* Prevent SDHCI core from writing reserved bits (e.g. HISPD). */
if ( reg = = SDHCI_HOST_CONTROL )
val & = ~ ESDHC_HOST_CONTROL_RES ;
sdhci_be32bs_writeb ( host , val , reg ) ;
}
2010-10-15 14:21:03 +04:00
static int esdhc_of_enable_dma ( struct sdhci_host * host )
2009-12-18 02:27:20 +03:00
{
setbits32 ( host - > ioaddr + ESDHC_DMA_SYSCTL , ESDHC_DMA_SNOOP ) ;
return 0 ;
}
2010-10-15 14:21:03 +04:00
static unsigned int esdhc_of_get_max_clock ( struct sdhci_host * host )
2009-12-18 02:27:20 +03:00
{
2011-07-21 01:13:36 +04:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
2009-12-18 02:27:20 +03:00
2011-07-21 01:13:36 +04:00
return pltfm_host - > clock ;
2009-12-18 02:27:20 +03:00
}
2010-10-15 14:21:03 +04:00
static unsigned int esdhc_of_get_min_clock ( struct sdhci_host * host )
2009-12-18 02:27:20 +03:00
{
2011-07-21 01:13:36 +04:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
2009-12-18 02:27:20 +03:00
2011-07-21 01:13:36 +04:00
return pltfm_host - > clock / 256 / 16 ;
2009-12-18 02:27:20 +03:00
}
2012-02-14 10:05:37 +04:00
static void esdhc_of_set_clock ( struct sdhci_host * host , unsigned int clock )
{
/* Workaround to reduce the clock frequency for p1010 esdhc */
if ( of_find_compatible_node ( NULL , NULL , " fsl,p1010-esdhc " ) ) {
if ( clock > 20000000 )
clock - = 5000000 ;
if ( clock > 40000000 )
clock - = 5000000 ;
}
/* Set the clock */
esdhc_set_clock ( host , clock ) ;
}
2012-02-05 02:13:13 +04:00
# ifdef CONFIG_PM
static u32 esdhc_proctl ;
static void esdhc_of_suspend ( struct sdhci_host * host )
{
esdhc_proctl = sdhci_be32bs_readl ( host , SDHCI_HOST_CONTROL ) ;
}
static void esdhc_of_resume ( struct sdhci_host * host )
{
esdhc_of_enable_dma ( host ) ;
sdhci_be32bs_writel ( host , esdhc_proctl , SDHCI_HOST_CONTROL ) ;
}
# endif
2011-07-21 01:13:36 +04:00
static struct sdhci_ops sdhci_esdhc_ops = {
. read_l = sdhci_be32bs_readl ,
. read_w = esdhc_readw ,
2011-09-09 16:05:46 +04:00
. read_b = esdhc_readb ,
2011-07-21 01:13:36 +04:00
. write_l = sdhci_be32bs_writel ,
. write_w = esdhc_writew ,
. write_b = esdhc_writeb ,
2012-02-14 10:05:37 +04:00
. set_clock = esdhc_of_set_clock ,
2011-07-21 01:13:36 +04:00
. enable_dma = esdhc_of_enable_dma ,
. get_max_clock = esdhc_of_get_max_clock ,
. get_min_clock = esdhc_of_get_min_clock ,
2012-02-05 02:13:13 +04:00
# ifdef CONFIG_PM
. platform_suspend = esdhc_of_suspend ,
. platform_resume = esdhc_of_resume ,
# endif
2011-07-21 01:13:36 +04:00
} ;
2011-05-27 19:48:14 +04:00
static struct sdhci_pltfm_data sdhci_esdhc_pdata = {
2011-02-26 16:44:40 +03:00
/* card detection could be handled via GPIO */
2011-03-21 08:22:13 +03:00
. quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_BROKEN_CARD_DETECTION
| SDHCI_QUIRK_NO_CARD_NO_RESET ,
2011-07-21 01:13:36 +04:00
. ops = & sdhci_esdhc_ops ,
2009-12-18 02:27:20 +03:00
} ;
2011-05-27 19:48:14 +04:00
static int __devinit sdhci_esdhc_probe ( struct platform_device * pdev )
{
return sdhci_pltfm_register ( pdev , & sdhci_esdhc_pdata ) ;
}
static int __devexit sdhci_esdhc_remove ( struct platform_device * pdev )
{
return sdhci_pltfm_unregister ( pdev ) ;
}
static const struct of_device_id sdhci_esdhc_of_match [ ] = {
{ . compatible = " fsl,mpc8379-esdhc " } ,
{ . compatible = " fsl,mpc8536-esdhc " } ,
{ . compatible = " fsl,esdhc " } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , sdhci_esdhc_of_match ) ;
static struct platform_driver sdhci_esdhc_driver = {
. driver = {
. name = " sdhci-esdhc " ,
. owner = THIS_MODULE ,
. of_match_table = sdhci_esdhc_of_match ,
2011-11-03 14:09:45 +04:00
. pm = SDHCI_PLTFM_PMOPS ,
2011-05-27 19:48:14 +04:00
} ,
. probe = sdhci_esdhc_probe ,
. remove = __devexit_p ( sdhci_esdhc_remove ) ,
} ;
2011-11-26 08:55:43 +04:00
module_platform_driver ( sdhci_esdhc_driver ) ;
2011-05-27 19:48:14 +04:00
MODULE_DESCRIPTION ( " SDHCI OF driver for Freescale MPC eSDHC " ) ;
MODULE_AUTHOR ( " Xiaobo Xie <X.Xie@freescale.com>, "
" Anton Vorontsov <avorontsov@ru.mvista.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;