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 .
*/
2013-06-27 20:00:05 +04:00
# include <linux/err.h>
2009-12-18 02:27:20 +03:00
# include <linux/io.h>
2012-02-14 10:05:37 +04:00
# include <linux/of.h>
2017-04-20 11:14:41 +03:00
# include <linux/of_address.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>
2016-11-09 06:14:12 +03:00
# include <linux/sys_soc.h>
2017-04-20 11:14:40 +03:00
# include <linux/clk.h>
# include <linux/ktime.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
2012-03-08 07:25:02 +04:00
# define VENDOR_V_22 0x12
2012-12-04 06:41:28 +04:00
# define VENDOR_V_23 0x13
2015-10-08 13:36:36 +03:00
struct sdhci_esdhc {
u8 vendor_ver ;
u8 spec_ver ;
2016-11-09 06:14:12 +03:00
bool quirk_incorrect_hostver ;
2017-04-20 11:14:40 +03:00
unsigned int peripheral_clock ;
2015-10-08 13:36:36 +03:00
} ;
/**
* esdhc_read * _fixup - Fixup the value read from incompatible eSDHC register
* to make it compatible with SD spec .
*
* @ host : pointer to sdhci_host
* @ spec_reg : SD spec register address
* @ value : 32 bit eSDHC register value on spec_reg address
*
* In SD spec , there are 8 / 16 / 32 / 64 bits registers , while all of eSDHC
* registers are 32 bits . There are differences in register size , register
* address , register function , bit position and function between eSDHC spec
* and SD spec .
*
* Return a fixed up register value
*/
static u32 esdhc_readl_fixup ( struct sdhci_host * host ,
int spec_reg , u32 value )
2012-03-08 07:25:02 +04:00
{
2015-10-08 13:36:36 +03:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
2016-02-16 16:08:26 +03:00
struct sdhci_esdhc * esdhc = sdhci_pltfm_priv ( pltfm_host ) ;
2012-03-08 07:25:02 +04:00
u32 ret ;
/*
* The bit of ADMA flag in eSDHC is not compatible with standard
* SDHC register , so set fake flag SDHCI_CAN_DO_ADMA2 when ADMA is
* supported by eSDHC .
* And for many FSL eSDHC controller , the reset value of field
2015-10-08 13:36:36 +03:00
* SDHCI_CAN_DO_ADMA1 is 1 , but some of them can ' t support ADMA ,
2012-03-08 07:25:02 +04:00
* only these vendor version is greater than 2.2 / 0x12 support ADMA .
*/
2015-10-08 13:36:36 +03:00
if ( ( spec_reg = = SDHCI_CAPABILITIES ) & & ( value & SDHCI_CAN_DO_ADMA1 ) ) {
if ( esdhc - > vendor_ver > VENDOR_V_22 ) {
ret = value | SDHCI_CAN_DO_ADMA2 ;
return ret ;
}
2012-03-08 07:25:02 +04:00
}
2016-11-15 13:13:16 +03:00
/*
* The DAT [ 3 : 0 ] line signal levels and the CMD line signal level are
* not compatible with standard SDHC register . The line signal levels
* DAT [ 7 : 0 ] are at bits 31 : 24 and the command line signal level is at
* bit 23. All other bits are the same as in the standard SDHC
* register .
*/
if ( spec_reg = = SDHCI_PRESENT_STATE ) {
ret = value & 0x000fffff ;
ret | = ( value > > 4 ) & SDHCI_DATA_LVL_MASK ;
ret | = ( value < < 1 ) & SDHCI_CMD_LVL ;
return ret ;
}
2015-10-08 13:36:36 +03:00
ret = value ;
2012-03-08 07:25:02 +04:00
return ret ;
}
2015-10-08 13:36:36 +03:00
static u16 esdhc_readw_fixup ( struct sdhci_host * host ,
int spec_reg , u32 value )
2009-12-18 02:27:20 +03:00
{
2016-11-09 06:14:12 +03:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_esdhc * esdhc = sdhci_pltfm_priv ( pltfm_host ) ;
2009-12-18 02:27:20 +03:00
u16 ret ;
2015-10-08 13:36:36 +03:00
int shift = ( spec_reg & 0x2 ) * 8 ;
2009-12-18 02:27:20 +03:00
2015-10-08 13:36:36 +03:00
if ( spec_reg = = SDHCI_HOST_VERSION )
ret = value & 0xffff ;
2009-12-18 02:27:20 +03:00
else
2015-10-08 13:36:36 +03:00
ret = ( value > > shift ) & 0xffff ;
2016-11-09 06:14:12 +03:00
/* Workaround for T4240-R1.0-R2.0 eSDHC which has incorrect
* vendor version and spec version information .
*/
if ( ( spec_reg = = SDHCI_HOST_VERSION ) & &
( esdhc - > quirk_incorrect_hostver ) )
ret = ( VENDOR_V_23 < < SDHCI_VENDOR_VER_SHIFT ) | SDHCI_SPEC_200 ;
2011-09-09 16:05:46 +04:00
return ret ;
}
2015-10-08 13:36:36 +03:00
static u8 esdhc_readb_fixup ( struct sdhci_host * host ,
int spec_reg , u32 value )
2011-09-09 16:05:46 +04:00
{
2015-10-08 13:36:36 +03:00
u8 ret ;
u8 dma_bits ;
int shift = ( spec_reg & 0x3 ) * 8 ;
ret = ( value > > 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 .
*/
2015-10-08 13:36:36 +03:00
if ( spec_reg = = SDHCI_HOST_CONTROL ) {
2012-01-13 11:02:01 +04:00
/* DMA select is 22,23 bits in Protocol Control Register */
2015-10-08 13:36:36 +03:00
dma_bits = ( value > > 5 ) & SDHCI_CTRL_DMA_MASK ;
2012-01-13 11:02:01 +04:00
/* fixup the result */
ret & = ~ SDHCI_CTRL_DMA_MASK ;
ret | = dma_bits ;
}
2009-12-18 02:27:20 +03:00
return ret ;
}
2015-10-08 13:36:36 +03:00
/**
* esdhc_write * _fixup - Fixup the SD spec register value so that it could be
* written into eSDHC register .
*
* @ host : pointer to sdhci_host
* @ spec_reg : SD spec register address
* @ value : 8 / 16 / 32 bit SD spec register value that would be written
* @ old_value : 32 bit eSDHC register value on spec_reg address
*
* In SD spec , there are 8 / 16 / 32 / 64 bits registers , while all of eSDHC
* registers are 32 bits . There are differences in register size , register
* address , register function , bit position and function between eSDHC spec
* and SD spec .
*
* Return a fixed up register value
*/
static u32 esdhc_writel_fixup ( struct sdhci_host * host ,
int spec_reg , u32 value , u32 old_value )
2012-12-04 06:41:28 +04:00
{
2015-10-08 13:36:36 +03:00
u32 ret ;
2012-12-04 06:41:28 +04:00
/*
2015-10-08 13:36:36 +03:00
* Enabling IRQSTATEN [ BGESEN ] is just to set IRQSTAT [ BGE ]
* when SYSCTL [ RSTD ] is set for some special operations .
* No any impact on other operation .
2012-12-04 06:41:28 +04:00
*/
2015-10-08 13:36:36 +03:00
if ( spec_reg = = SDHCI_INT_ENABLE )
ret = value | SDHCI_INT_BLK_GAP ;
else
ret = value ;
return ret ;
2012-12-04 06:41:28 +04:00
}
2015-10-08 13:36:36 +03:00
static u32 esdhc_writew_fixup ( struct sdhci_host * host ,
int spec_reg , u16 value , u32 old_value )
2009-12-18 02:27:20 +03:00
{
2015-10-08 13:36:36 +03:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
int shift = ( spec_reg & 0x2 ) * 8 ;
u32 ret ;
switch ( spec_reg ) {
case SDHCI_TRANSFER_MODE :
/*
* Postpone this write , we must do it together with a
* command write that is down below . Return old value .
*/
pltfm_host - > xfer_mode_shadow = value ;
return old_value ;
case SDHCI_COMMAND :
ret = ( value < < 16 ) | pltfm_host - > xfer_mode_shadow ;
return ret ;
}
ret = old_value & ( ~ ( 0xffff < < shift ) ) ;
ret | = ( value < < shift ) ;
if ( spec_reg = = SDHCI_BLOCK_SIZE ) {
2009-12-18 02:27:20 +03:00
/*
* 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 .
*/
2015-10-08 13:36:36 +03:00
ret & = ( ~ SDHCI_MAKE_BLKSZ ( 0x7 , 0 ) ) ;
2009-12-18 02:27:20 +03:00
}
2015-10-08 13:36:36 +03:00
return ret ;
2009-12-18 02:27:20 +03:00
}
2015-10-08 13:36:36 +03:00
static u32 esdhc_writeb_fixup ( struct sdhci_host * host ,
int spec_reg , u8 value , u32 old_value )
2009-12-18 02:27:20 +03:00
{
2015-10-08 13:36:36 +03:00
u32 ret ;
u32 dma_bits ;
u8 tmp ;
int shift = ( spec_reg & 0x3 ) * 8 ;
2015-10-16 10:44:03 +03:00
/*
* eSDHC doesn ' t have a standard power control register , so we do
* nothing here to avoid incorrect operation .
*/
if ( spec_reg = = SDHCI_POWER_CONTROL )
return old_value ;
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 .
*/
2015-10-08 13:36:36 +03:00
if ( spec_reg = = SDHCI_HOST_CONTROL ) {
2013-07-05 20:48:35 +04:00
/*
* If host control register is not standard , exit
* this function
*/
if ( host - > quirks2 & SDHCI_QUIRK2_BROKEN_HOST_CONTROL )
2015-10-08 13:36:36 +03:00
return old_value ;
2013-07-05 20:48:35 +04:00
2012-01-13 11:02:01 +04:00
/* DMA select is 22,23 bits in Protocol Control Register */
2015-10-08 13:36:36 +03:00
dma_bits = ( value & SDHCI_CTRL_DMA_MASK ) < < 5 ;
ret = ( old_value & ( ~ ( SDHCI_CTRL_DMA_MASK < < 5 ) ) ) | dma_bits ;
tmp = ( value & ( ~ SDHCI_CTRL_DMA_MASK ) ) |
( old_value & SDHCI_CTRL_DMA_MASK ) ;
ret = ( ret & ( ~ 0xff ) ) | tmp ;
/* Prevent SDHCI core from writing reserved bits (e.g. HISPD) */
ret & = ~ ESDHC_HOST_CONTROL_RES ;
return ret ;
2012-01-13 11:02:01 +04:00
}
2015-10-08 13:36:36 +03:00
ret = ( old_value & ( ~ ( 0xff < < shift ) ) ) | ( value < < shift ) ;
return ret ;
}
static u32 esdhc_be_readl ( struct sdhci_host * host , int reg )
{
u32 ret ;
u32 value ;
value = ioread32be ( host - > ioaddr + reg ) ;
ret = esdhc_readl_fixup ( host , reg , value ) ;
return ret ;
}
static u32 esdhc_le_readl ( struct sdhci_host * host , int reg )
{
u32 ret ;
u32 value ;
value = ioread32 ( host - > ioaddr + reg ) ;
ret = esdhc_readl_fixup ( host , reg , value ) ;
return ret ;
}
static u16 esdhc_be_readw ( struct sdhci_host * host , int reg )
{
u16 ret ;
u32 value ;
int base = reg & ~ 0x3 ;
value = ioread32be ( host - > ioaddr + base ) ;
ret = esdhc_readw_fixup ( host , reg , value ) ;
return ret ;
}
static u16 esdhc_le_readw ( struct sdhci_host * host , int reg )
{
u16 ret ;
u32 value ;
int base = reg & ~ 0x3 ;
value = ioread32 ( host - > ioaddr + base ) ;
ret = esdhc_readw_fixup ( host , reg , value ) ;
return ret ;
}
static u8 esdhc_be_readb ( struct sdhci_host * host , int reg )
{
u8 ret ;
u32 value ;
int base = reg & ~ 0x3 ;
value = ioread32be ( host - > ioaddr + base ) ;
ret = esdhc_readb_fixup ( host , reg , value ) ;
return ret ;
}
static u8 esdhc_le_readb ( struct sdhci_host * host , int reg )
{
u8 ret ;
u32 value ;
int base = reg & ~ 0x3 ;
value = ioread32 ( host - > ioaddr + base ) ;
ret = esdhc_readb_fixup ( host , reg , value ) ;
return ret ;
}
static void esdhc_be_writel ( struct sdhci_host * host , u32 val , int reg )
{
u32 value ;
value = esdhc_writel_fixup ( host , reg , val , 0 ) ;
iowrite32be ( value , host - > ioaddr + reg ) ;
}
static void esdhc_le_writel ( struct sdhci_host * host , u32 val , int reg )
{
u32 value ;
value = esdhc_writel_fixup ( host , reg , val , 0 ) ;
iowrite32 ( value , host - > ioaddr + reg ) ;
}
static void esdhc_be_writew ( struct sdhci_host * host , u16 val , int reg )
{
int base = reg & ~ 0x3 ;
u32 value ;
u32 ret ;
value = ioread32be ( host - > ioaddr + base ) ;
ret = esdhc_writew_fixup ( host , reg , val , value ) ;
if ( reg ! = SDHCI_TRANSFER_MODE )
iowrite32be ( ret , host - > ioaddr + base ) ;
}
static void esdhc_le_writew ( struct sdhci_host * host , u16 val , int reg )
{
int base = reg & ~ 0x3 ;
u32 value ;
u32 ret ;
value = ioread32 ( host - > ioaddr + base ) ;
ret = esdhc_writew_fixup ( host , reg , val , value ) ;
if ( reg ! = SDHCI_TRANSFER_MODE )
iowrite32 ( ret , host - > ioaddr + base ) ;
}
static void esdhc_be_writeb ( struct sdhci_host * host , u8 val , int reg )
{
int base = reg & ~ 0x3 ;
u32 value ;
u32 ret ;
value = ioread32be ( host - > ioaddr + base ) ;
ret = esdhc_writeb_fixup ( host , reg , val , value ) ;
iowrite32be ( ret , host - > ioaddr + base ) ;
}
static void esdhc_le_writeb ( struct sdhci_host * host , u8 val , int reg )
{
int base = reg & ~ 0x3 ;
u32 value ;
u32 ret ;
value = ioread32 ( host - > ioaddr + base ) ;
ret = esdhc_writeb_fixup ( host , reg , val , value ) ;
iowrite32 ( ret , host - > ioaddr + base ) ;
2009-12-18 02:27:20 +03:00
}
2012-12-04 06:41:28 +04:00
/*
* For Abort or Suspend after Stop at Block Gap , ignore the ADMA
* error ( IRQSTAT [ ADMAE ] ) if both Transfer Complete ( IRQSTAT [ TC ] )
* and Block Gap Event ( IRQSTAT [ BGE ] ) are also set .
* For Continue , apply soft reset for data ( SYSCTL [ RSTD ] ) ;
* and re - issue the entire read transaction from beginning .
*/
2015-10-08 13:36:36 +03:00
static void esdhc_of_adma_workaround ( struct sdhci_host * host , u32 intmask )
2012-12-04 06:41:28 +04:00
{
2015-10-08 13:36:36 +03:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
2016-02-16 16:08:26 +03:00
struct sdhci_esdhc * esdhc = sdhci_pltfm_priv ( pltfm_host ) ;
2012-12-04 06:41:28 +04:00
bool applicable ;
dma_addr_t dmastart ;
dma_addr_t dmanow ;
applicable = ( intmask & SDHCI_INT_DATA_END ) & &
2015-10-08 13:36:36 +03:00
( intmask & SDHCI_INT_BLK_GAP ) & &
( esdhc - > vendor_ver = = VENDOR_V_23 ) ;
2012-12-04 06:41:28 +04:00
if ( ! applicable )
return ;
host - > data - > error = 0 ;
dmastart = sg_dma_address ( host - > data - > sg ) ;
dmanow = dmastart + host - > data - > bytes_xfered ;
/*
* Force update to the next DMA block boundary .
*/
dmanow = ( dmanow & ~ ( SDHCI_DEFAULT_BOUNDARY_SIZE - 1 ) ) +
SDHCI_DEFAULT_BOUNDARY_SIZE ;
host - > data - > bytes_xfered = dmanow - dmastart ;
sdhci_writel ( host , dmanow , SDHCI_DMA_ADDRESS ) ;
}
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
{
2015-10-08 13:36:36 +03:00
u32 value ;
value = sdhci_readl ( host , ESDHC_DMA_SYSCTL ) ;
value | = ESDHC_DMA_SNOOP ;
sdhci_writel ( host , value , ESDHC_DMA_SYSCTL ) ;
2009-12-18 02:27:20 +03:00
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 ) ;
2017-04-20 11:14:40 +03:00
struct sdhci_esdhc * esdhc = sdhci_pltfm_priv ( pltfm_host ) ;
2009-12-18 02:27:20 +03:00
2017-04-20 11:14:40 +03:00
if ( esdhc - > peripheral_clock )
return esdhc - > peripheral_clock ;
else
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 ) ;
2017-04-20 11:14:40 +03:00
struct sdhci_esdhc * esdhc = sdhci_pltfm_priv ( pltfm_host ) ;
unsigned int clock ;
2009-12-18 02:27:20 +03:00
2017-04-20 11:14:40 +03:00
if ( esdhc - > peripheral_clock )
clock = esdhc - > peripheral_clock ;
else
clock = pltfm_host - > clock ;
return 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 )
{
2015-10-08 13:36:36 +03:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
2016-02-16 16:08:26 +03:00
struct sdhci_esdhc * esdhc = sdhci_pltfm_priv ( pltfm_host ) ;
2015-04-21 00:12:13 +03:00
int pre_div = 1 ;
2013-09-13 15:11:32 +04:00
int div = 1 ;
2016-12-26 12:46:30 +03:00
u32 timeout ;
2013-09-13 15:11:32 +04:00
u32 temp ;
2014-04-25 15:58:50 +04:00
host - > mmc - > actual_clock = 0 ;
2013-09-13 15:11:32 +04:00
if ( clock = = 0 )
2014-04-25 15:58:45 +04:00
return ;
2013-09-13 15:11:32 +04:00
2015-08-11 05:53:34 +03:00
/* Workaround to start pre_div at 2 for VNN < VENDOR_V_23 */
2015-10-08 13:36:36 +03:00
if ( esdhc - > vendor_ver < VENDOR_V_23 )
2015-08-11 05:53:34 +03:00
pre_div = 2 ;
2012-02-14 10:05:37 +04:00
/* 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 ;
}
2013-09-13 15:11:32 +04:00
temp = sdhci_readl ( host , ESDHC_SYSTEM_CONTROL ) ;
2016-12-26 12:46:30 +03:00
temp & = ~ ( ESDHC_CLOCK_SDCLKEN | ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN |
ESDHC_CLOCK_PEREN | ESDHC_CLOCK_MASK ) ;
2013-09-13 15:11:32 +04:00
sdhci_writel ( host , temp , ESDHC_SYSTEM_CONTROL ) ;
while ( host - > max_clk / pre_div / 16 > clock & & pre_div < 256 )
pre_div * = 2 ;
while ( host - > max_clk / pre_div / div > clock & & div < 16 )
div + + ;
dev_dbg ( mmc_dev ( host - > mmc ) , " desired SD clock: %d, actual: %d \n " ,
2013-09-13 15:11:37 +04:00
clock , host - > max_clk / pre_div / div ) ;
2015-04-21 00:12:13 +03:00
host - > mmc - > actual_clock = host - > max_clk / pre_div / div ;
2013-09-13 15:11:32 +04:00
pre_div > > = 1 ;
div - - ;
temp = sdhci_readl ( host , ESDHC_SYSTEM_CONTROL ) ;
temp | = ( ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
| ( div < < ESDHC_DIVIDER_SHIFT )
| ( pre_div < < ESDHC_PREDIV_SHIFT ) ) ;
sdhci_writel ( host , temp , ESDHC_SYSTEM_CONTROL ) ;
2016-12-26 12:46:30 +03:00
/* Wait max 20 ms */
timeout = 20 ;
while ( ! ( sdhci_readl ( host , ESDHC_PRSSTAT ) & ESDHC_CLOCK_STABLE ) ) {
if ( timeout = = 0 ) {
pr_err ( " %s: Internal clock never stabilised. \n " ,
mmc_hostname ( host - > mmc ) ) ;
return ;
}
timeout - - ;
mdelay ( 1 ) ;
}
temp | = ESDHC_CLOCK_SDCLKEN ;
sdhci_writel ( host , temp , ESDHC_SYSTEM_CONTROL ) ;
2012-02-14 10:05:37 +04:00
}
2014-04-25 15:57:07 +04:00
static void esdhc_pltfm_set_bus_width ( struct sdhci_host * host , int width )
2013-06-27 20:00:05 +04:00
{
u32 ctrl ;
2015-10-08 13:36:36 +03:00
ctrl = sdhci_readl ( host , ESDHC_PROCTL ) ;
ctrl & = ( ~ ESDHC_CTRL_BUSWIDTH_MASK ) ;
2013-06-27 20:00:05 +04:00
switch ( width ) {
case MMC_BUS_WIDTH_8 :
2015-10-08 13:36:36 +03:00
ctrl | = ESDHC_CTRL_8BITBUS ;
2013-06-27 20:00:05 +04:00
break ;
case MMC_BUS_WIDTH_4 :
2015-10-08 13:36:36 +03:00
ctrl | = ESDHC_CTRL_4BITBUS ;
2013-06-27 20:00:05 +04:00
break ;
default :
break ;
}
2015-10-08 13:36:36 +03:00
sdhci_writel ( host , ctrl , ESDHC_PROCTL ) ;
2013-06-27 20:00:05 +04:00
}
2017-04-20 11:14:40 +03:00
static void esdhc_clock_enable ( struct sdhci_host * host , bool enable )
{
u32 val ;
ktime_t timeout ;
val = sdhci_readl ( host , ESDHC_SYSTEM_CONTROL ) ;
if ( enable )
val | = ESDHC_CLOCK_SDCLKEN ;
else
val & = ~ ESDHC_CLOCK_SDCLKEN ;
sdhci_writel ( host , val , ESDHC_SYSTEM_CONTROL ) ;
/* Wait max 20 ms */
timeout = ktime_add_ms ( ktime_get ( ) , 20 ) ;
val = ESDHC_CLOCK_STABLE ;
while ( ! ( sdhci_readl ( host , ESDHC_PRSSTAT ) & val ) ) {
if ( ktime_after ( ktime_get ( ) , timeout ) ) {
pr_err ( " %s: Internal clock never stabilised. \n " ,
mmc_hostname ( host - > mmc ) ) ;
break ;
}
udelay ( 10 ) ;
}
}
2014-12-09 11:40:38 +03:00
static void esdhc_reset ( struct sdhci_host * host , u8 mask )
{
sdhci_reset ( host , mask ) ;
sdhci_writel ( host , host - > ier , SDHCI_INT_ENABLE ) ;
sdhci_writel ( host , host - > ier , SDHCI_SIGNAL_ENABLE ) ;
}
2017-04-20 11:14:41 +03:00
/* The SCFG, Supplemental Configuration Unit, provides SoC specific
* configuration and status registers for the device . There is a
* SDHC IO VSEL control register on SCFG for some platforms . It ' s
* used to support SDHC IO voltage switching .
*/
static const struct of_device_id scfg_device_ids [ ] = {
{ . compatible = " fsl,t1040-scfg " , } ,
{ . compatible = " fsl,ls1012a-scfg " , } ,
{ . compatible = " fsl,ls1046a-scfg " , } ,
{ }
} ;
/* SDHC IO VSEL control register definition */
# define SCFG_SDHCIOVSELCR 0x408
# define SDHCIOVSELCR_TGLEN 0x80000000
# define SDHCIOVSELCR_VSELVAL 0x60000000
# define SDHCIOVSELCR_SDHC_VS 0x00000001
static int esdhc_signal_voltage_switch ( struct mmc_host * mmc ,
struct mmc_ios * ios )
{
struct sdhci_host * host = mmc_priv ( mmc ) ;
struct device_node * scfg_node ;
void __iomem * scfg_base = NULL ;
u32 sdhciovselcr ;
u32 val ;
/*
* Signal Voltage Switching is only applicable for Host Controllers
* v3 .00 and above .
*/
if ( host - > version < SDHCI_SPEC_300 )
return 0 ;
val = sdhci_readl ( host , ESDHC_PROCTL ) ;
switch ( ios - > signal_voltage ) {
case MMC_SIGNAL_VOLTAGE_330 :
val & = ~ ESDHC_VOLT_SEL ;
sdhci_writel ( host , val , ESDHC_PROCTL ) ;
return 0 ;
case MMC_SIGNAL_VOLTAGE_180 :
scfg_node = of_find_matching_node ( NULL , scfg_device_ids ) ;
if ( scfg_node )
scfg_base = of_iomap ( scfg_node , 0 ) ;
if ( scfg_base ) {
sdhciovselcr = SDHCIOVSELCR_TGLEN |
SDHCIOVSELCR_VSELVAL ;
iowrite32be ( sdhciovselcr ,
scfg_base + SCFG_SDHCIOVSELCR ) ;
val | = ESDHC_VOLT_SEL ;
sdhci_writel ( host , val , ESDHC_PROCTL ) ;
mdelay ( 5 ) ;
sdhciovselcr = SDHCIOVSELCR_TGLEN |
SDHCIOVSELCR_SDHC_VS ;
iowrite32be ( sdhciovselcr ,
scfg_base + SCFG_SDHCIOVSELCR ) ;
iounmap ( scfg_base ) ;
} else {
val | = ESDHC_VOLT_SEL ;
sdhci_writel ( host , val , ESDHC_PROCTL ) ;
}
return 0 ;
default :
return 0 ;
}
}
2017-04-20 11:14:42 +03:00
static int esdhc_execute_tuning ( struct mmc_host * mmc , u32 opcode )
{
struct sdhci_host * host = mmc_priv ( mmc ) ;
u32 val ;
/* Use tuning block for tuning procedure */
esdhc_clock_enable ( host , false ) ;
val = sdhci_readl ( host , ESDHC_DMA_SYSCTL ) ;
val | = ESDHC_FLUSH_ASYNC_FIFO ;
sdhci_writel ( host , val , ESDHC_DMA_SYSCTL ) ;
val = sdhci_readl ( host , ESDHC_TBCTL ) ;
val | = ESDHC_TB_EN ;
sdhci_writel ( host , val , ESDHC_TBCTL ) ;
esdhc_clock_enable ( host , true ) ;
return sdhci_execute_tuning ( mmc , opcode ) ;
}
2016-07-27 12:01:48 +03:00
# ifdef CONFIG_PM_SLEEP
2014-04-25 15:59:46 +04:00
static u32 esdhc_proctl ;
static int esdhc_of_suspend ( struct device * dev )
{
struct sdhci_host * host = dev_get_drvdata ( dev ) ;
2015-10-08 13:36:36 +03:00
esdhc_proctl = sdhci_readl ( host , SDHCI_HOST_CONTROL ) ;
2014-04-25 15:59:46 +04:00
2017-03-20 20:50:32 +03:00
if ( host - > tuning_mode ! = SDHCI_TUNING_MODE_3 )
mmc_retune_needed ( host - > mmc ) ;
2014-04-25 15:59:46 +04:00
return sdhci_suspend_host ( host ) ;
}
2014-05-23 12:36:44 +04:00
static int esdhc_of_resume ( struct device * dev )
2014-04-25 15:59:46 +04:00
{
struct sdhci_host * host = dev_get_drvdata ( dev ) ;
int ret = sdhci_resume_host ( host ) ;
if ( ret = = 0 ) {
/* Isn't this already done by sdhci_resume_host() ? --rmk */
esdhc_of_enable_dma ( host ) ;
2015-10-08 13:36:36 +03:00
sdhci_writel ( host , esdhc_proctl , SDHCI_HOST_CONTROL ) ;
2014-04-25 15:59:46 +04:00
}
return ret ;
}
# endif
2016-07-27 12:01:48 +03:00
static SIMPLE_DEV_PM_OPS ( esdhc_of_dev_pm_ops ,
esdhc_of_suspend ,
esdhc_of_resume ) ;
2015-10-08 13:36:36 +03:00
static const struct sdhci_ops sdhci_esdhc_be_ops = {
. read_l = esdhc_be_readl ,
. read_w = esdhc_be_readw ,
. read_b = esdhc_be_readb ,
. write_l = esdhc_be_writel ,
. write_w = esdhc_be_writew ,
. write_b = esdhc_be_writeb ,
. set_clock = esdhc_of_set_clock ,
. enable_dma = esdhc_of_enable_dma ,
. get_max_clock = esdhc_of_get_max_clock ,
. get_min_clock = esdhc_of_get_min_clock ,
. adma_workaround = esdhc_of_adma_workaround ,
. set_bus_width = esdhc_pltfm_set_bus_width ,
. reset = esdhc_reset ,
. set_uhs_signaling = sdhci_set_uhs_signaling ,
} ;
static const struct sdhci_ops sdhci_esdhc_le_ops = {
. read_l = esdhc_le_readl ,
. read_w = esdhc_le_readw ,
. read_b = esdhc_le_readb ,
. write_l = esdhc_le_writel ,
. write_w = esdhc_le_writew ,
. write_b = esdhc_le_writeb ,
. set_clock = esdhc_of_set_clock ,
. enable_dma = esdhc_of_enable_dma ,
. get_max_clock = esdhc_of_get_max_clock ,
. get_min_clock = esdhc_of_get_min_clock ,
. adma_workaround = esdhc_of_adma_workaround ,
. set_bus_width = esdhc_pltfm_set_bus_width ,
. reset = esdhc_reset ,
. set_uhs_signaling = sdhci_set_uhs_signaling ,
} ;
static const struct sdhci_pltfm_data sdhci_esdhc_be_pdata = {
2016-12-26 12:40:44 +03:00
. quirks = ESDHC_DEFAULT_QUIRKS |
# ifdef CONFIG_PPC
SDHCI_QUIRK_BROKEN_CARD_DETECTION |
# endif
SDHCI_QUIRK_NO_CARD_NO_RESET |
SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC ,
2015-10-08 13:36:36 +03:00
. ops = & sdhci_esdhc_be_ops ,
} ;
static const struct sdhci_pltfm_data sdhci_esdhc_le_pdata = {
2016-12-26 12:40:44 +03:00
. quirks = ESDHC_DEFAULT_QUIRKS |
SDHCI_QUIRK_NO_CARD_NO_RESET |
SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC ,
2015-10-08 13:36:36 +03:00
. ops = & sdhci_esdhc_le_ops ,
2009-12-18 02:27:20 +03:00
} ;
2011-05-27 19:48:14 +04:00
2016-11-09 06:14:12 +03:00
static struct soc_device_attribute soc_incorrect_hostver [ ] = {
{ . family = " QorIQ T4240 " , . revision = " 1.0 " , } ,
{ . family = " QorIQ T4240 " , . revision = " 2.0 " , } ,
{ } ,
} ;
2015-10-08 13:36:36 +03:00
static void esdhc_init ( struct platform_device * pdev , struct sdhci_host * host )
{
struct sdhci_pltfm_host * pltfm_host ;
struct sdhci_esdhc * esdhc ;
2017-04-20 11:14:40 +03:00
struct device_node * np ;
struct clk * clk ;
u32 val ;
2015-10-08 13:36:36 +03:00
u16 host_ver ;
pltfm_host = sdhci_priv ( host ) ;
2016-02-16 16:08:26 +03:00
esdhc = sdhci_pltfm_priv ( pltfm_host ) ;
2015-10-08 13:36:36 +03:00
host_ver = sdhci_readw ( host , SDHCI_HOST_VERSION ) ;
esdhc - > vendor_ver = ( host_ver & SDHCI_VENDOR_VER_MASK ) > >
SDHCI_VENDOR_VER_SHIFT ;
esdhc - > spec_ver = host_ver & SDHCI_SPEC_VER_MASK ;
2016-11-09 06:14:12 +03:00
if ( soc_device_match ( soc_incorrect_hostver ) )
esdhc - > quirk_incorrect_hostver = true ;
else
esdhc - > quirk_incorrect_hostver = false ;
2017-04-20 11:14:40 +03:00
np = pdev - > dev . of_node ;
clk = of_clk_get ( np , 0 ) ;
if ( ! IS_ERR ( clk ) ) {
/*
* esdhc - > peripheral_clock would be assigned with a value
* which is eSDHC base clock when use periperal clock .
* For ls1046a , the clock value got by common clk API is
* peripheral clock while the eSDHC base clock is 1 / 2
* peripheral clock .
*/
if ( of_device_is_compatible ( np , " fsl,ls1046a-esdhc " ) )
esdhc - > peripheral_clock = clk_get_rate ( clk ) / 2 ;
else
esdhc - > peripheral_clock = clk_get_rate ( clk ) ;
clk_put ( clk ) ;
}
if ( esdhc - > peripheral_clock ) {
esdhc_clock_enable ( host , false ) ;
val = sdhci_readl ( host , ESDHC_DMA_SYSCTL ) ;
val | = ESDHC_PERIPHERAL_CLK_SEL ;
sdhci_writel ( host , val , ESDHC_DMA_SYSCTL ) ;
esdhc_clock_enable ( host , true ) ;
}
2015-10-08 13:36:36 +03:00
}
2012-11-19 22:23:06 +04:00
static int sdhci_esdhc_probe ( struct platform_device * pdev )
2011-05-27 19:48:14 +04:00
{
2013-06-27 20:00:05 +04:00
struct sdhci_host * host ;
2013-07-05 20:48:35 +04:00
struct device_node * np ;
2015-11-25 05:05:37 +03:00
struct sdhci_pltfm_host * pltfm_host ;
struct sdhci_esdhc * esdhc ;
2013-06-27 20:00:05 +04:00
int ret ;
2015-10-08 13:36:36 +03:00
np = pdev - > dev . of_node ;
2016-08-05 11:56:46 +03:00
if ( of_property_read_bool ( np , " little-endian " ) )
2016-02-16 16:08:26 +03:00
host = sdhci_pltfm_init ( pdev , & sdhci_esdhc_le_pdata ,
sizeof ( struct sdhci_esdhc ) ) ;
2015-10-08 13:36:36 +03:00
else
2016-02-16 16:08:26 +03:00
host = sdhci_pltfm_init ( pdev , & sdhci_esdhc_be_pdata ,
sizeof ( struct sdhci_esdhc ) ) ;
2015-10-08 13:36:36 +03:00
2013-06-27 20:00:05 +04:00
if ( IS_ERR ( host ) )
return PTR_ERR ( host ) ;
2017-04-20 11:14:41 +03:00
host - > mmc_host_ops . start_signal_voltage_switch =
esdhc_signal_voltage_switch ;
2017-04-20 11:14:42 +03:00
host - > mmc_host_ops . execute_tuning = esdhc_execute_tuning ;
2017-04-20 11:14:41 +03:00
2015-10-08 13:36:36 +03:00
esdhc_init ( pdev , host ) ;
2013-06-27 20:00:05 +04:00
sdhci_get_of_property ( pdev ) ;
2015-11-25 05:05:37 +03:00
pltfm_host = sdhci_priv ( host ) ;
2016-02-16 16:08:26 +03:00
esdhc = sdhci_pltfm_priv ( pltfm_host ) ;
2015-11-25 05:05:37 +03:00
if ( esdhc - > vendor_ver = = VENDOR_V_22 )
host - > quirks2 | = SDHCI_QUIRK2_HOST_NO_CMD23 ;
if ( esdhc - > vendor_ver > VENDOR_V_22 )
host - > quirks & = ~ SDHCI_QUIRK_NO_BUSY_IRQ ;
2015-06-01 08:47:12 +03:00
if ( of_device_is_compatible ( np , " fsl,p5040-esdhc " ) | |
of_device_is_compatible ( np , " fsl,p5020-esdhc " ) | |
of_device_is_compatible ( np , " fsl,p4080-esdhc " ) | |
of_device_is_compatible ( np , " fsl,p1020-esdhc " ) | |
2016-12-26 12:40:44 +03:00
of_device_is_compatible ( np , " fsl,t1040-esdhc " ) )
2015-06-01 08:47:12 +03:00
host - > quirks & = ~ SDHCI_QUIRK_BROKEN_CARD_DETECTION ;
2015-10-08 13:36:57 +03:00
if ( of_device_is_compatible ( np , " fsl,ls1021a-esdhc " ) )
host - > quirks | = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL ;
2013-07-05 20:48:35 +04:00
if ( of_device_is_compatible ( np , " fsl,p2020-esdhc " ) ) {
/*
* Freescale messed up with P2020 as it has a non - standard
* host control register
*/
host - > quirks2 | = SDHCI_QUIRK2_BROKEN_HOST_CONTROL ;
}
2013-06-27 20:00:05 +04:00
/* call to generic mmc_of_parse to support additional capabilities */
2014-12-18 12:41:41 +03:00
ret = mmc_of_parse ( host - > mmc ) ;
if ( ret )
goto err ;
2013-08-26 05:19:24 +04:00
mmc_of_parse_voltage ( np , & host - > ocr_mask ) ;
2013-06-27 20:00:05 +04:00
ret = sdhci_add_host ( host ) ;
if ( ret )
2014-12-18 12:41:41 +03:00
goto err ;
2013-06-27 20:00:05 +04:00
2014-12-18 12:41:41 +03:00
return 0 ;
err :
sdhci_pltfm_free ( pdev ) ;
2013-06-27 20:00:05 +04:00
return ret ;
2011-05-27 19:48:14 +04:00
}
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 " ,
. of_match_table = sdhci_esdhc_of_match ,
2016-07-27 12:01:48 +03:00
. pm = & esdhc_of_dev_pm_ops ,
2011-05-27 19:48:14 +04:00
} ,
. probe = sdhci_esdhc_probe ,
2015-02-27 10:47:31 +03:00
. remove = sdhci_pltfm_unregister ,
2011-05-27 19:48:14 +04:00
} ;
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 " ) ;