2010-10-15 14:21:04 +04:00
/*
* Freescale eSDHC i . MX controller driver for the platform bus .
*
* derived from the OF - version .
*
* Copyright ( c ) 2010 Pengutronix e . K .
* Author : Wolfram Sang < w . sang @ pengutronix . de >
*
* 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 .
*/
# include <linux/io.h>
# include <linux/delay.h>
# include <linux/err.h>
# include <linux/clk.h>
2011-02-26 16:44:39 +03:00
# include <linux/gpio.h>
2011-08-15 06:28:18 +04:00
# include <linux/module.h>
2011-03-25 16:18:27 +03:00
# include <linux/slab.h>
2010-10-15 14:21:04 +04:00
# include <linux/mmc/host.h>
2011-03-21 08:22:16 +03:00
# include <linux/mmc/mmc.h>
# include <linux/mmc/sdio.h>
2012-12-11 18:32:20 +04:00
# include <linux/mmc/slot-gpio.h>
2011-06-30 11:44:44 +04:00
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/of_gpio.h>
2012-05-11 10:56:01 +04:00
# include <linux/pinctrl/consumer.h>
2012-08-24 17:14:29 +04:00
# include <linux/platform_data/mmc-esdhc-imx.h>
2013-11-04 12:38:29 +04:00
# include <linux/pm_runtime.h>
2010-10-15 14:21:04 +04:00
# include "sdhci-pltfm.h"
# include "sdhci-esdhc.h"
2013-01-15 19:36:53 +04:00
# define ESDHC_CTRL_D3CD 0x08
2011-03-21 08:22:16 +03:00
/* VENDOR SPEC register */
2013-01-15 19:36:53 +04:00
# define ESDHC_VENDOR_SPEC 0xc0
# define ESDHC_VENDOR_SPEC_SDIO_QUIRK (1 << 1)
2013-09-13 15:11:34 +04:00
# define ESDHC_VENDOR_SPEC_VSELECT (1 << 1)
2013-09-13 15:11:33 +04:00
# define ESDHC_VENDOR_SPEC_FRC_SDCLK_ON (1 << 8)
2013-01-15 19:36:53 +04:00
# define ESDHC_WTMK_LVL 0x44
# define ESDHC_MIX_CTRL 0x48
2013-10-18 15:48:46 +04:00
# define ESDHC_MIX_CTRL_DDREN (1 << 3)
2013-01-21 15:02:26 +04:00
# define ESDHC_MIX_CTRL_AC23EN (1 << 7)
2013-09-13 15:11:34 +04:00
# define ESDHC_MIX_CTRL_EXE_TUNE (1 << 22)
# define ESDHC_MIX_CTRL_SMPCLK_SEL (1 << 23)
# define ESDHC_MIX_CTRL_FBCLK_SEL (1 << 25)
2013-01-21 15:02:26 +04:00
/* Bits 3 and 6 are not SDHCI standard definitions */
# define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7
2013-11-04 12:38:26 +04:00
/* Tuning bits */
# define ESDHC_MIX_CTRL_TUNING_MASK 0x03c00000
2011-03-21 08:22:16 +03:00
2013-10-18 15:48:47 +04:00
/* dll control register */
# define ESDHC_DLL_CTRL 0x60
# define ESDHC_DLL_OVERRIDE_VAL_SHIFT 9
# define ESDHC_DLL_OVERRIDE_EN_SHIFT 8
2013-09-13 15:11:34 +04:00
/* tune control register */
# define ESDHC_TUNE_CTRL_STATUS 0x68
# define ESDHC_TUNE_CTRL_STEP 1
# define ESDHC_TUNE_CTRL_MIN 0
# define ESDHC_TUNE_CTRL_MAX ((1 << 7) - 1)
2013-10-18 15:48:43 +04:00
# define ESDHC_TUNING_CTRL 0xcc
# define ESDHC_STD_TUNING_EN (1 << 24)
/* NOTE: the minimum valid tuning start tap for mx6sl is 1 */
# define ESDHC_TUNING_START_TAP 0x1
2013-09-13 15:11:34 +04:00
# define ESDHC_TUNING_BLOCK_PATTERN_LEN 64
2013-09-13 15:11:35 +04:00
/* pinctrl state */
# define ESDHC_PINCTRL_STATE_100MHZ "state_100mhz"
# define ESDHC_PINCTRL_STATE_200MHZ "state_200mhz"
2013-01-21 15:02:28 +04:00
/*
* Our interpretation of the SDHCI_HOST_CONTROL register
*/
# define ESDHC_CTRL_4BITBUS (0x1 << 1)
# define ESDHC_CTRL_8BITBUS (0x2 << 1)
# define ESDHC_CTRL_BUSWIDTH_MASK (0x3 << 1)
2011-08-12 00:51:46 +04:00
/*
* There is an INT DMA ERR mis - match between eSDHC and STD SDHC SPEC :
* Bit25 is used in STD SPEC , and is reserved in fsl eSDHC design ,
* but bit28 is used as the INT DMA ERR in fsl eSDHC design .
* Define this macro DMA error INT for fsl eSDHC
*/
2013-01-15 19:36:53 +04:00
# define ESDHC_INT_VENDOR_SPEC_DMA_ERR (1 << 28)
2011-08-12 00:51:46 +04:00
2011-03-21 08:22:16 +03:00
/*
* The CMDTYPE of the CMD register ( offset 0xE ) should be set to
* " 11 " when the STOP CMD12 is issued on imx53 to abort one
* open ended multi - blk IO . Otherwise the TC INT wouldn ' t
* be generated .
* In exact block transfer , the controller doesn ' t complete the
* operations automatically as required at the end of the
* transfer and remains on hold if the abort command is not sent .
* As a result , the TC flag is not asserted and SW received timeout
* exeception . Bit1 of Vendor Spec registor is used to fix it .
*/
2013-10-17 11:19:44 +04:00
# define ESDHC_FLAG_MULTIBLK_NO_INT BIT(1)
/*
* The flag enables the workaround for ESDHC errata ENGcm07207 which
* affects i . MX25 and i . MX35 .
*/
# define ESDHC_FLAG_ENGCM07207 BIT(2)
2013-10-17 11:19:45 +04:00
/*
* The flag tells that the ESDHC controller is an USDHC block that is
* integrated on the i . MX6 series .
*/
# define ESDHC_FLAG_USDHC BIT(3)
2013-10-18 15:48:43 +04:00
/* The IP supports manual tuning process */
# define ESDHC_FLAG_MAN_TUNING BIT(4)
/* The IP supports standard tuning process */
# define ESDHC_FLAG_STD_TUNING BIT(5)
/* The IP has SDHCI_CAPABILITIES_1 register */
# define ESDHC_FLAG_HAVE_CAP1 BIT(6)
2011-03-25 16:18:27 +03:00
2013-10-17 11:19:47 +04:00
struct esdhc_soc_data {
u32 flags ;
} ;
static struct esdhc_soc_data esdhc_imx25_data = {
. flags = ESDHC_FLAG_ENGCM07207 ,
} ;
static struct esdhc_soc_data esdhc_imx35_data = {
. flags = ESDHC_FLAG_ENGCM07207 ,
} ;
static struct esdhc_soc_data esdhc_imx51_data = {
. flags = 0 ,
} ;
static struct esdhc_soc_data esdhc_imx53_data = {
. flags = ESDHC_FLAG_MULTIBLK_NO_INT ,
} ;
static struct esdhc_soc_data usdhc_imx6q_data = {
2013-10-18 15:48:43 +04:00
. flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_MAN_TUNING ,
} ;
static struct esdhc_soc_data usdhc_imx6sl_data = {
. flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
| ESDHC_FLAG_HAVE_CAP1 ,
2011-06-30 05:24:26 +04:00
} ;
2011-03-25 16:18:27 +03:00
struct pltfm_imx_data {
u32 scratchpad ;
2012-05-11 10:56:01 +04:00
struct pinctrl * pinctrl ;
2013-09-13 15:11:35 +04:00
struct pinctrl_state * pins_default ;
struct pinctrl_state * pins_100mhz ;
struct pinctrl_state * pins_200mhz ;
2013-10-17 11:19:47 +04:00
const struct esdhc_soc_data * socdata ;
2011-07-06 18:57:48 +04:00
struct esdhc_platform_data boarddata ;
2012-03-07 12:31:34 +04:00
struct clk * clk_ipg ;
struct clk * clk_ahb ;
struct clk * clk_per ;
2013-03-15 12:49:26 +04:00
enum {
NO_CMD_PENDING , /* no multiblock command pending*/
MULTIBLK_IN_PROCESS , /* exact multiblock cmd in process */
WAIT_FOR_INT , /* sent CMD12, waiting for response INT */
} multiblock_status ;
2013-09-13 15:11:34 +04:00
u32 uhs_mode ;
2013-10-18 15:48:46 +04:00
u32 is_ddr ;
2011-03-25 16:18:27 +03:00
} ;
2011-06-30 05:24:26 +04:00
static struct platform_device_id imx_esdhc_devtype [ ] = {
{
. name = " sdhci-esdhc-imx25 " ,
2013-10-17 11:19:47 +04:00
. driver_data = ( kernel_ulong_t ) & esdhc_imx25_data ,
2011-06-30 05:24:26 +04:00
} , {
. name = " sdhci-esdhc-imx35 " ,
2013-10-17 11:19:47 +04:00
. driver_data = ( kernel_ulong_t ) & esdhc_imx35_data ,
2011-06-30 05:24:26 +04:00
} , {
. name = " sdhci-esdhc-imx51 " ,
2013-10-17 11:19:47 +04:00
. driver_data = ( kernel_ulong_t ) & esdhc_imx51_data ,
2011-06-30 05:24:26 +04:00
} , {
/* sentinel */
}
} ;
MODULE_DEVICE_TABLE ( platform , imx_esdhc_devtype ) ;
2011-06-30 11:44:44 +04:00
static const struct of_device_id imx_esdhc_dt_ids [ ] = {
2013-10-17 11:19:47 +04:00
{ . compatible = " fsl,imx25-esdhc " , . data = & esdhc_imx25_data , } ,
{ . compatible = " fsl,imx35-esdhc " , . data = & esdhc_imx35_data , } ,
{ . compatible = " fsl,imx51-esdhc " , . data = & esdhc_imx51_data , } ,
{ . compatible = " fsl,imx53-esdhc " , . data = & esdhc_imx53_data , } ,
2013-10-18 15:48:43 +04:00
{ . compatible = " fsl,imx6sl-usdhc " , . data = & usdhc_imx6sl_data , } ,
2013-10-17 11:19:47 +04:00
{ . compatible = " fsl,imx6q-usdhc " , . data = & usdhc_imx6q_data , } ,
2011-06-30 11:44:44 +04:00
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , imx_esdhc_dt_ids ) ;
2011-06-30 05:24:26 +04:00
static inline int is_imx25_esdhc ( struct pltfm_imx_data * data )
{
2013-10-17 11:19:47 +04:00
return data - > socdata = = & esdhc_imx25_data ;
2011-06-30 05:24:26 +04:00
}
static inline int is_imx53_esdhc ( struct pltfm_imx_data * data )
{
2013-10-17 11:19:47 +04:00
return data - > socdata = = & esdhc_imx53_data ;
2011-06-30 05:24:26 +04:00
}
2011-09-19 13:32:21 +04:00
static inline int is_imx6q_usdhc ( struct pltfm_imx_data * data )
{
2013-10-17 11:19:47 +04:00
return data - > socdata = = & usdhc_imx6q_data ;
2011-09-19 13:32:21 +04:00
}
2013-10-17 11:19:45 +04:00
static inline int esdhc_is_usdhc ( struct pltfm_imx_data * data )
{
2013-10-17 11:19:47 +04:00
return ! ! ( data - > socdata - > flags & ESDHC_FLAG_USDHC ) ;
2013-10-17 11:19:45 +04:00
}
2010-10-15 14:21:04 +04:00
static inline void esdhc_clrset_le ( struct sdhci_host * host , u32 mask , u32 val , int reg )
{
void __iomem * base = host - > ioaddr + ( reg & ~ 0x3 ) ;
u32 shift = ( reg & 0x3 ) * 8 ;
writel ( ( ( readl ( base ) & ~ ( mask < < shift ) ) | ( val < < shift ) ) , base ) ;
}
2011-02-26 16:44:41 +03:00
static u32 esdhc_readl_le ( struct sdhci_host * host , int reg )
{
2013-03-15 12:49:26 +04:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct pltfm_imx_data * imx_data = pltfm_host - > priv ;
2011-02-26 16:44:41 +03:00
u32 val = readl ( host - > ioaddr + reg ) ;
2013-09-13 15:11:34 +04:00
if ( unlikely ( reg = = SDHCI_PRESENT_STATE ) ) {
u32 fsl_prss = val ;
/* save the least 20 bits */
val = fsl_prss & 0x000FFFFF ;
/* move dat[0-3] bits */
val | = ( fsl_prss & 0x0F000000 ) > > 4 ;
/* move cmd line bit */
val | = ( fsl_prss & 0x00800000 ) < < 1 ;
}
2011-08-12 00:51:46 +04:00
if ( unlikely ( reg = = SDHCI_CAPABILITIES ) ) {
2013-10-18 15:48:44 +04:00
/* ignore bit[0-15] as it stores cap_1 register val for mx6sl */
if ( imx_data - > socdata - > flags & ESDHC_FLAG_HAVE_CAP1 )
val & = 0xffff0000 ;
2011-08-12 00:51:46 +04:00
/* In FSL esdhc IC module, only bit20 is used to indicate the
* ADMA2 capability of esdhc , but this bit is messed up on
* some SOCs ( e . g . on MX25 , MX35 this bit is set , but they
* don ' t actually support ADMA2 ) . So set the BROKEN_ADMA
* uirk on MX25 / 35 platforms .
*/
if ( val & SDHCI_CAN_DO_ADMA1 ) {
val & = ~ SDHCI_CAN_DO_ADMA1 ;
val | = SDHCI_CAN_DO_ADMA2 ;
}
}
2013-10-18 15:48:43 +04:00
if ( unlikely ( reg = = SDHCI_CAPABILITIES_1 ) ) {
if ( esdhc_is_usdhc ( imx_data ) ) {
if ( imx_data - > socdata - > flags & ESDHC_FLAG_HAVE_CAP1 )
val = readl ( host - > ioaddr + SDHCI_CAPABILITIES ) & 0xFFFF ;
else
/* imx6q/dl does not have cap_1 register, fake one */
val = SDHCI_SUPPORT_DDR50 | SDHCI_SUPPORT_SDR104
2013-10-18 15:48:48 +04:00
| SDHCI_SUPPORT_SDR50
| SDHCI_USE_SDR50_TUNING ;
2013-10-18 15:48:43 +04:00
}
}
2013-09-13 15:11:34 +04:00
2013-10-17 11:19:45 +04:00
if ( unlikely ( reg = = SDHCI_MAX_CURRENT ) & & esdhc_is_usdhc ( imx_data ) ) {
2013-09-13 15:11:34 +04:00
val = 0 ;
val | = 0xFF < < SDHCI_MAX_CURRENT_330_SHIFT ;
val | = 0xFF < < SDHCI_MAX_CURRENT_300_SHIFT ;
val | = 0xFF < < SDHCI_MAX_CURRENT_180_SHIFT ;
}
2011-08-12 00:51:46 +04:00
if ( unlikely ( reg = = SDHCI_INT_STATUS ) ) {
2013-01-15 19:36:53 +04:00
if ( val & ESDHC_INT_VENDOR_SPEC_DMA_ERR ) {
val & = ~ ESDHC_INT_VENDOR_SPEC_DMA_ERR ;
2011-08-12 00:51:46 +04:00
val | = SDHCI_INT_ADMA_ERROR ;
}
2013-03-15 12:49:26 +04:00
/*
* mask off the interrupt we get in response to the manually
* sent CMD12
*/
if ( ( imx_data - > multiblock_status = = WAIT_FOR_INT ) & &
( ( val & SDHCI_INT_RESPONSE ) = = SDHCI_INT_RESPONSE ) ) {
val & = ~ SDHCI_INT_RESPONSE ;
writel ( SDHCI_INT_RESPONSE , host - > ioaddr +
SDHCI_INT_STATUS ) ;
imx_data - > multiblock_status = NO_CMD_PENDING ;
}
2011-08-12 00:51:46 +04:00
}
2011-02-26 16:44:41 +03:00
return val ;
}
static void esdhc_writel_le ( struct sdhci_host * host , u32 val , int reg )
{
2011-03-25 16:18:27 +03:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct pltfm_imx_data * imx_data = pltfm_host - > priv ;
2011-08-12 00:45:59 +04:00
u32 data ;
if ( unlikely ( reg = = SDHCI_INT_ENABLE | | reg = = SDHCI_SIGNAL_ENABLE ) ) {
if ( val & SDHCI_INT_CARD_INT ) {
/*
* Clear and then set D3CD bit to avoid missing the
* card interrupt . This is a eSDHC controller problem
* so we need to apply the following workaround : clear
* and set D3CD bit will make eSDHC re - sample the card
* interrupt . In case a card interrupt was lost ,
* re - sample it by the following steps .
*/
data = readl ( host - > ioaddr + SDHCI_HOST_CONTROL ) ;
2013-01-15 19:36:53 +04:00
data & = ~ ESDHC_CTRL_D3CD ;
2011-08-12 00:45:59 +04:00
writel ( data , host - > ioaddr + SDHCI_HOST_CONTROL ) ;
2013-01-15 19:36:53 +04:00
data | = ESDHC_CTRL_D3CD ;
2011-08-12 00:45:59 +04:00
writel ( data , host - > ioaddr + SDHCI_HOST_CONTROL ) ;
}
}
2011-02-26 16:44:41 +03:00
2013-10-17 11:19:47 +04:00
if ( unlikely ( ( imx_data - > socdata - > flags & ESDHC_FLAG_MULTIBLK_NO_INT )
2011-03-21 08:22:16 +03:00
& & ( reg = = SDHCI_INT_STATUS )
& & ( val & SDHCI_INT_DATA_END ) ) ) {
u32 v ;
2013-01-15 19:36:53 +04:00
v = readl ( host - > ioaddr + ESDHC_VENDOR_SPEC ) ;
v & = ~ ESDHC_VENDOR_SPEC_SDIO_QUIRK ;
writel ( v , host - > ioaddr + ESDHC_VENDOR_SPEC ) ;
2013-03-15 12:49:26 +04:00
if ( imx_data - > multiblock_status = = MULTIBLK_IN_PROCESS )
{
/* send a manual CMD12 with RESPTYP=none */
data = MMC_STOP_TRANSMISSION < < 24 |
SDHCI_CMD_ABORTCMD < < 16 ;
writel ( data , host - > ioaddr + SDHCI_TRANSFER_MODE ) ;
imx_data - > multiblock_status = WAIT_FOR_INT ;
}
2011-03-21 08:22:16 +03:00
}
2011-08-12 00:51:46 +04:00
if ( unlikely ( reg = = SDHCI_INT_ENABLE | | reg = = SDHCI_SIGNAL_ENABLE ) ) {
if ( val & SDHCI_INT_ADMA_ERROR ) {
val & = ~ SDHCI_INT_ADMA_ERROR ;
2013-01-15 19:36:53 +04:00
val | = ESDHC_INT_VENDOR_SPEC_DMA_ERR ;
2011-08-12 00:51:46 +04:00
}
}
2011-02-26 16:44:41 +03:00
writel ( val , host - > ioaddr + reg ) ;
}
2010-10-15 14:21:04 +04:00
static u16 esdhc_readw_le ( struct sdhci_host * host , int reg )
{
2013-01-15 19:30:27 +04:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct pltfm_imx_data * imx_data = pltfm_host - > priv ;
2013-09-13 15:11:34 +04:00
u16 ret = 0 ;
u32 val ;
2013-01-15 19:30:27 +04:00
2011-09-19 13:32:21 +04:00
if ( unlikely ( reg = = SDHCI_HOST_VERSION ) ) {
2013-01-15 19:30:27 +04:00
reg ^ = 2 ;
2013-10-17 11:19:45 +04:00
if ( esdhc_is_usdhc ( imx_data ) ) {
2013-01-15 19:30:27 +04:00
/*
* The usdhc register returns a wrong host version .
* Correct it here .
*/
return SDHCI_SPEC_300 ;
}
2011-09-19 13:32:21 +04:00
}
2010-10-15 14:21:04 +04:00
2013-09-13 15:11:34 +04:00
if ( unlikely ( reg = = SDHCI_HOST_CONTROL2 ) ) {
val = readl ( host - > ioaddr + ESDHC_VENDOR_SPEC ) ;
if ( val & ESDHC_VENDOR_SPEC_VSELECT )
ret | = SDHCI_CTRL_VDD_180 ;
2013-10-17 11:19:45 +04:00
if ( esdhc_is_usdhc ( imx_data ) ) {
2013-10-18 15:48:43 +04:00
if ( imx_data - > socdata - > flags & ESDHC_FLAG_MAN_TUNING )
val = readl ( host - > ioaddr + ESDHC_MIX_CTRL ) ;
else if ( imx_data - > socdata - > flags & ESDHC_FLAG_STD_TUNING )
/* the std tuning bits is in ACMD12_ERR for imx6sl */
val = readl ( host - > ioaddr + SDHCI_ACMD12_ERR ) ;
2013-09-13 15:11:34 +04:00
}
2013-10-18 15:48:43 +04:00
if ( val & ESDHC_MIX_CTRL_EXE_TUNE )
ret | = SDHCI_CTRL_EXEC_TUNING ;
if ( val & ESDHC_MIX_CTRL_SMPCLK_SEL )
ret | = SDHCI_CTRL_TUNED_CLK ;
2013-09-13 15:11:34 +04:00
ret | = ( imx_data - > uhs_mode & SDHCI_CTRL_UHS_MASK ) ;
ret & = ~ SDHCI_CTRL_PRESET_VAL_ENABLE ;
return ret ;
}
2013-10-30 18:09:49 +04:00
if ( unlikely ( reg = = SDHCI_TRANSFER_MODE ) ) {
if ( esdhc_is_usdhc ( imx_data ) ) {
u32 m = readl ( host - > ioaddr + ESDHC_MIX_CTRL ) ;
ret = m & ESDHC_MIX_CTRL_SDHCI_MASK ;
/* Swap AC23 bit */
if ( m & ESDHC_MIX_CTRL_AC23EN ) {
ret & = ~ ESDHC_MIX_CTRL_AC23EN ;
ret | = SDHCI_TRNS_AUTO_CMD23 ;
}
} else {
ret = readw ( host - > ioaddr + SDHCI_TRANSFER_MODE ) ;
}
return ret ;
}
2010-10-15 14:21:04 +04:00
return readw ( host - > ioaddr + reg ) ;
}
static void esdhc_writew_le ( struct sdhci_host * host , u16 val , int reg )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
2011-03-25 16:18:27 +03:00
struct pltfm_imx_data * imx_data = pltfm_host - > priv ;
2013-09-13 15:11:34 +04:00
u32 new_val = 0 ;
2010-10-15 14:21:04 +04:00
switch ( reg ) {
2013-09-13 15:11:34 +04:00
case SDHCI_CLOCK_CONTROL :
new_val = readl ( host - > ioaddr + ESDHC_VENDOR_SPEC ) ;
if ( val & SDHCI_CLOCK_CARD_EN )
new_val | = ESDHC_VENDOR_SPEC_FRC_SDCLK_ON ;
else
new_val & = ~ ESDHC_VENDOR_SPEC_FRC_SDCLK_ON ;
writel ( new_val , host - > ioaddr + ESDHC_VENDOR_SPEC ) ;
return ;
case SDHCI_HOST_CONTROL2 :
new_val = readl ( host - > ioaddr + ESDHC_VENDOR_SPEC ) ;
if ( val & SDHCI_CTRL_VDD_180 )
new_val | = ESDHC_VENDOR_SPEC_VSELECT ;
else
new_val & = ~ ESDHC_VENDOR_SPEC_VSELECT ;
writel ( new_val , host - > ioaddr + ESDHC_VENDOR_SPEC ) ;
imx_data - > uhs_mode = val & SDHCI_CTRL_UHS_MASK ;
2013-10-18 15:48:43 +04:00
if ( imx_data - > socdata - > flags & ESDHC_FLAG_MAN_TUNING ) {
new_val = readl ( host - > ioaddr + ESDHC_MIX_CTRL ) ;
if ( val & SDHCI_CTRL_TUNED_CLK )
new_val | = ESDHC_MIX_CTRL_SMPCLK_SEL ;
else
new_val & = ~ ESDHC_MIX_CTRL_SMPCLK_SEL ;
writel ( new_val , host - > ioaddr + ESDHC_MIX_CTRL ) ;
} else if ( imx_data - > socdata - > flags & ESDHC_FLAG_STD_TUNING ) {
u32 v = readl ( host - > ioaddr + SDHCI_ACMD12_ERR ) ;
u32 m = readl ( host - > ioaddr + ESDHC_MIX_CTRL ) ;
2013-11-04 12:38:27 +04:00
if ( val & SDHCI_CTRL_TUNED_CLK ) {
v | = ESDHC_MIX_CTRL_SMPCLK_SEL ;
} else {
v & = ~ ESDHC_MIX_CTRL_SMPCLK_SEL ;
m & = ~ ESDHC_MIX_CTRL_FBCLK_SEL ;
}
2013-10-18 15:48:43 +04:00
if ( val & SDHCI_CTRL_EXEC_TUNING ) {
v | = ESDHC_MIX_CTRL_EXE_TUNE ;
m | = ESDHC_MIX_CTRL_FBCLK_SEL ;
} else {
v & = ~ ESDHC_MIX_CTRL_EXE_TUNE ;
}
writel ( v , host - > ioaddr + SDHCI_ACMD12_ERR ) ;
writel ( m , host - > ioaddr + ESDHC_MIX_CTRL ) ;
}
2013-09-13 15:11:34 +04:00
return ;
2010-10-15 14:21:04 +04:00
case SDHCI_TRANSFER_MODE :
2013-10-17 11:19:47 +04:00
if ( ( imx_data - > socdata - > flags & ESDHC_FLAG_MULTIBLK_NO_INT )
2011-03-21 08:22:16 +03:00
& & ( host - > cmd - > opcode = = SD_IO_RW_EXTENDED )
& & ( host - > cmd - > data - > blocks > 1 )
& & ( host - > cmd - > data - > flags & MMC_DATA_READ ) ) {
u32 v ;
2013-01-15 19:36:53 +04:00
v = readl ( host - > ioaddr + ESDHC_VENDOR_SPEC ) ;
v | = ESDHC_VENDOR_SPEC_SDIO_QUIRK ;
writel ( v , host - > ioaddr + ESDHC_VENDOR_SPEC ) ;
2011-03-21 08:22:16 +03:00
}
2013-01-21 15:02:24 +04:00
2013-10-17 11:19:45 +04:00
if ( esdhc_is_usdhc ( imx_data ) ) {
2013-01-21 15:02:24 +04:00
u32 m = readl ( host - > ioaddr + ESDHC_MIX_CTRL ) ;
2013-01-21 15:02:26 +04:00
/* Swap AC23 bit */
if ( val & SDHCI_TRNS_AUTO_CMD23 ) {
val & = ~ SDHCI_TRNS_AUTO_CMD23 ;
val | = ESDHC_MIX_CTRL_AC23EN ;
}
m = val | ( m & ~ ESDHC_MIX_CTRL_SDHCI_MASK ) ;
2013-01-21 15:02:24 +04:00
writel ( m , host - > ioaddr + ESDHC_MIX_CTRL ) ;
} else {
/*
* Postpone this write , we must do it together with a
* command write that is down below .
*/
imx_data - > scratchpad = val ;
}
2010-10-15 14:21:04 +04:00
return ;
case SDHCI_COMMAND :
2013-03-15 12:49:26 +04:00
if ( host - > cmd - > opcode = = MMC_STOP_TRANSMISSION )
2011-03-21 08:22:16 +03:00
val | = SDHCI_CMD_ABORTCMD ;
2011-09-19 13:32:21 +04:00
2013-03-15 12:49:26 +04:00
if ( ( host - > cmd - > opcode = = MMC_SET_BLOCK_COUNT ) & &
2013-10-17 11:19:47 +04:00
( imx_data - > socdata - > flags & ESDHC_FLAG_MULTIBLK_NO_INT ) )
2013-03-15 12:49:26 +04:00
imx_data - > multiblock_status = MULTIBLK_IN_PROCESS ;
2013-10-17 11:19:45 +04:00
if ( esdhc_is_usdhc ( imx_data ) )
2011-09-19 13:32:21 +04:00
writel ( val < < 16 ,
host - > ioaddr + SDHCI_TRANSFER_MODE ) ;
2013-01-21 15:02:24 +04:00
else
2011-09-19 13:32:21 +04:00
writel ( val < < 16 | imx_data - > scratchpad ,
host - > ioaddr + SDHCI_TRANSFER_MODE ) ;
2010-10-15 14:21:04 +04:00
return ;
case SDHCI_BLOCK_SIZE :
val & = ~ SDHCI_MAKE_BLKSZ ( 0x7 , 0 ) ;
break ;
}
esdhc_clrset_le ( host , 0xffff , val , reg ) ;
}
static void esdhc_writeb_le ( struct sdhci_host * host , u8 val , int reg )
{
2012-07-19 10:49:16 +04:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct pltfm_imx_data * imx_data = pltfm_host - > priv ;
2010-10-15 14:21:04 +04:00
u32 new_val ;
2013-01-21 15:02:28 +04:00
u32 mask ;
2010-10-15 14:21:04 +04:00
switch ( reg ) {
case SDHCI_POWER_CONTROL :
/*
* FSL put some DMA bits here
* If your board has a regulator , code should be here
*/
return ;
case SDHCI_HOST_CONTROL :
2013-01-15 19:36:52 +04:00
/* FSL messed up here, so we need to manually compose it. */
2013-01-21 15:02:28 +04:00
new_val = val & SDHCI_CTRL_LED ;
2012-08-05 18:25:40 +04:00
/* ensure the endianness */
2010-10-15 14:21:04 +04:00
new_val | = ESDHC_HOST_CONTROL_LE ;
2012-07-19 10:49:16 +04:00
/* bits 8&9 are reserved on mx25 */
if ( ! is_imx25_esdhc ( imx_data ) ) {
/* DMA mode bits are shifted */
new_val | = ( val & SDHCI_CTRL_DMA_MASK ) < < 5 ;
}
2010-10-15 14:21:04 +04:00
2013-01-21 15:02:28 +04:00
/*
* Do not touch buswidth bits here . This is done in
* esdhc_pltfm_bus_width .
2013-04-15 19:08:35 +04:00
* Do not touch the D3CD bit either which is used for the
* SDIO interrupt errata workaround .
2013-01-21 15:02:28 +04:00
*/
2013-04-15 19:08:35 +04:00
mask = 0xffff & ~ ( ESDHC_CTRL_BUSWIDTH_MASK | ESDHC_CTRL_D3CD ) ;
2013-01-21 15:02:28 +04:00
esdhc_clrset_le ( host , mask , new_val , reg ) ;
2010-10-15 14:21:04 +04:00
return ;
}
esdhc_clrset_le ( host , 0xff , val , reg ) ;
2011-06-21 18:41:51 +04:00
/*
* The esdhc has a design violation to SDHC spec which tells
* that software reset should not affect card detection circuit .
* But esdhc clears its SYSCTL register bits [ 0. .2 ] during the
* software reset . This will stop those clocks that card detection
* circuit relies on . To work around it , we turn the clocks on back
* to keep card detection circuit functional .
*/
2013-01-21 15:02:25 +04:00
if ( ( reg = = SDHCI_SOFTWARE_RESET ) & & ( val & 1 ) ) {
2011-06-21 18:41:51 +04:00
esdhc_clrset_le ( host , 0x7 , 0x7 , ESDHC_SYSTEM_CONTROL ) ;
2013-01-21 15:02:25 +04:00
/*
* The reset on usdhc fails to clear MIX_CTRL register .
* Do it manually here .
*/
2013-10-18 15:48:46 +04:00
if ( esdhc_is_usdhc ( imx_data ) ) {
2013-11-04 12:38:26 +04:00
/* the tuning bits should be kept during reset */
new_val = readl ( host - > ioaddr + ESDHC_MIX_CTRL ) ;
writel ( new_val & ESDHC_MIX_CTRL_TUNING_MASK ,
host - > ioaddr + ESDHC_MIX_CTRL ) ;
2013-10-18 15:48:46 +04:00
imx_data - > is_ddr = 0 ;
}
2013-01-21 15:02:25 +04:00
}
2010-10-15 14:21:04 +04:00
}
2013-06-05 17:13:26 +04:00
static unsigned int esdhc_pltfm_get_max_clock ( struct sdhci_host * host )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct pltfm_imx_data * imx_data = pltfm_host - > priv ;
struct esdhc_platform_data * boarddata = & imx_data - > boarddata ;
2013-12-26 11:23:53 +04:00
if ( boarddata - > f_max & & ( boarddata - > f_max < pltfm_host - > clock ) )
2013-06-05 17:13:26 +04:00
return boarddata - > f_max ;
else
2013-12-26 11:23:53 +04:00
return pltfm_host - > clock ;
2013-06-05 17:13:26 +04:00
}
2010-10-15 14:21:04 +04:00
static unsigned int esdhc_pltfm_get_min_clock ( struct sdhci_host * host )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
2013-12-26 11:23:53 +04:00
return pltfm_host - > clock / 256 / 16 ;
2010-10-15 14:21:04 +04:00
}
2013-06-05 17:13:25 +04:00
static inline void esdhc_pltfm_set_clock ( struct sdhci_host * host ,
unsigned int clock )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
2013-09-13 15:11:33 +04:00
struct pltfm_imx_data * imx_data = pltfm_host - > priv ;
2013-12-26 11:23:53 +04:00
unsigned int host_clock = pltfm_host - > clock ;
2013-09-13 15:11:32 +04:00
int pre_div = 2 ;
int div = 1 ;
2013-09-13 15:11:33 +04:00
u32 temp , val ;
2013-09-13 15:11:32 +04:00
2013-09-13 15:11:33 +04:00
if ( clock = = 0 ) {
2013-10-17 11:19:45 +04:00
if ( esdhc_is_usdhc ( imx_data ) ) {
2013-09-13 15:11:33 +04:00
val = readl ( host - > ioaddr + ESDHC_VENDOR_SPEC ) ;
writel ( val & ~ ESDHC_VENDOR_SPEC_FRC_SDCLK_ON ,
host - > ioaddr + ESDHC_VENDOR_SPEC ) ;
}
2014-04-25 15:58:45 +04:00
return ;
2013-09-13 15:11:33 +04:00
}
2013-09-13 15:11:32 +04:00
2013-10-18 15:48:46 +04:00
if ( esdhc_is_usdhc ( imx_data ) & & ! imx_data - > is_ddr )
2013-09-13 15:11:36 +04:00
pre_div = 1 ;
2013-09-13 15:11:32 +04:00
temp = sdhci_readl ( host , ESDHC_SYSTEM_CONTROL ) ;
temp & = ~ ( ESDHC_CLOCK_IPGEN | ESDHC_CLOCK_HCKEN | ESDHC_CLOCK_PEREN
| ESDHC_CLOCK_MASK ) ;
sdhci_writel ( host , temp , ESDHC_SYSTEM_CONTROL ) ;
while ( host_clock / pre_div / 16 > clock & & pre_div < 256 )
pre_div * = 2 ;
while ( host_clock / pre_div / div > clock & & div < 16 )
div + + ;
2013-09-13 15:11:37 +04:00
host - > mmc - > actual_clock = host_clock / pre_div / div ;
2013-09-13 15:11:32 +04:00
dev_dbg ( mmc_dev ( host - > mmc ) , " desired SD clock: %d, actual: %d \n " ,
2013-09-13 15:11:37 +04:00
clock , host - > mmc - > actual_clock ) ;
2013-09-13 15:11:32 +04:00
2013-10-18 15:48:46 +04:00
if ( imx_data - > is_ddr )
pre_div > > = 2 ;
else
pre_div > > = 1 ;
2013-09-13 15:11:32 +04:00
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 ) ;
2013-09-13 15:11:33 +04:00
2013-10-17 11:19:45 +04:00
if ( esdhc_is_usdhc ( imx_data ) ) {
2013-09-13 15:11:33 +04:00
val = readl ( host - > ioaddr + ESDHC_VENDOR_SPEC ) ;
writel ( val | ESDHC_VENDOR_SPEC_FRC_SDCLK_ON ,
host - > ioaddr + ESDHC_VENDOR_SPEC ) ;
}
2013-09-13 15:11:32 +04:00
mdelay ( 1 ) ;
2013-06-05 17:13:25 +04:00
}
2011-06-21 18:41:51 +04:00
static unsigned int esdhc_pltfm_get_ro ( struct sdhci_host * host )
{
2011-07-06 18:57:48 +04:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct pltfm_imx_data * imx_data = pltfm_host - > priv ;
struct esdhc_platform_data * boarddata = & imx_data - > boarddata ;
2011-06-21 18:41:51 +04:00
switch ( boarddata - > wp_type ) {
case ESDHC_WP_GPIO :
2012-12-11 18:32:20 +04:00
return mmc_gpio_get_ro ( host - > mmc ) ;
2011-06-21 18:41:51 +04:00
case ESDHC_WP_CONTROLLER :
return ! ( readl ( host - > ioaddr + SDHCI_PRESENT_STATE ) &
SDHCI_WRITE_PROTECT ) ;
case ESDHC_WP_NONE :
break ;
}
return - ENOSYS ;
}
2014-04-25 15:57:07 +04:00
static void esdhc_pltfm_set_bus_width ( struct sdhci_host * host , int width )
2013-01-21 15:02:28 +04:00
{
u32 ctrl ;
switch ( width ) {
case MMC_BUS_WIDTH_8 :
ctrl = ESDHC_CTRL_8BITBUS ;
break ;
case MMC_BUS_WIDTH_4 :
ctrl = ESDHC_CTRL_4BITBUS ;
break ;
default :
ctrl = 0 ;
break ;
}
esdhc_clrset_le ( host , ESDHC_CTRL_BUSWIDTH_MASK , ctrl ,
SDHCI_HOST_CONTROL ) ;
}
2013-09-13 15:11:34 +04:00
static void esdhc_prepare_tuning ( struct sdhci_host * host , u32 val )
{
u32 reg ;
/* FIXME: delay a bit for card to be ready for next tuning due to errors */
mdelay ( 1 ) ;
2013-11-04 12:38:28 +04:00
pm_runtime_get_sync ( host - > mmc - > parent ) ;
2013-09-13 15:11:34 +04:00
reg = readl ( host - > ioaddr + ESDHC_MIX_CTRL ) ;
reg | = ESDHC_MIX_CTRL_EXE_TUNE | ESDHC_MIX_CTRL_SMPCLK_SEL |
ESDHC_MIX_CTRL_FBCLK_SEL ;
writel ( reg , host - > ioaddr + ESDHC_MIX_CTRL ) ;
writel ( val < < 8 , host - > ioaddr + ESDHC_TUNE_CTRL_STATUS ) ;
dev_dbg ( mmc_dev ( host - > mmc ) ,
" tunning with delay 0x%x ESDHC_TUNE_CTRL_STATUS 0x%x \n " ,
val , readl ( host - > ioaddr + ESDHC_TUNE_CTRL_STATUS ) ) ;
}
static void esdhc_request_done ( struct mmc_request * mrq )
{
complete ( & mrq - > completion ) ;
}
static int esdhc_send_tuning_cmd ( struct sdhci_host * host , u32 opcode )
{
struct mmc_command cmd = { 0 } ;
2013-10-05 05:59:23 +04:00
struct mmc_request mrq = { NULL } ;
2013-09-13 15:11:34 +04:00
struct mmc_data data = { 0 } ;
struct scatterlist sg ;
char tuning_pattern [ ESDHC_TUNING_BLOCK_PATTERN_LEN ] ;
cmd . opcode = opcode ;
cmd . arg = 0 ;
cmd . flags = MMC_RSP_R1 | MMC_CMD_ADTC ;
data . blksz = ESDHC_TUNING_BLOCK_PATTERN_LEN ;
data . blocks = 1 ;
data . flags = MMC_DATA_READ ;
data . sg = & sg ;
data . sg_len = 1 ;
sg_init_one ( & sg , tuning_pattern , sizeof ( tuning_pattern ) ) ;
mrq . cmd = & cmd ;
mrq . cmd - > mrq = & mrq ;
mrq . data = & data ;
mrq . data - > mrq = & mrq ;
mrq . cmd - > data = mrq . data ;
mrq . done = esdhc_request_done ;
init_completion ( & ( mrq . completion ) ) ;
disable_irq ( host - > irq ) ;
spin_lock ( & host - > lock ) ;
host - > mrq = & mrq ;
sdhci_send_command ( host , mrq . cmd ) ;
spin_unlock ( & host - > lock ) ;
enable_irq ( host - > irq ) ;
wait_for_completion ( & mrq . completion ) ;
if ( cmd . error )
return cmd . error ;
if ( data . error )
return data . error ;
return 0 ;
}
static void esdhc_post_tuning ( struct sdhci_host * host )
{
u32 reg ;
reg = readl ( host - > ioaddr + ESDHC_MIX_CTRL ) ;
reg & = ~ ESDHC_MIX_CTRL_EXE_TUNE ;
writel ( reg , host - > ioaddr + ESDHC_MIX_CTRL ) ;
}
static int esdhc_executing_tuning ( struct sdhci_host * host , u32 opcode )
{
int min , max , avg , ret ;
/* find the mininum delay first which can pass tuning */
min = ESDHC_TUNE_CTRL_MIN ;
while ( min < ESDHC_TUNE_CTRL_MAX ) {
esdhc_prepare_tuning ( host , min ) ;
if ( ! esdhc_send_tuning_cmd ( host , opcode ) )
break ;
min + = ESDHC_TUNE_CTRL_STEP ;
}
/* find the maxinum delay which can not pass tuning */
max = min + ESDHC_TUNE_CTRL_STEP ;
while ( max < ESDHC_TUNE_CTRL_MAX ) {
esdhc_prepare_tuning ( host , max ) ;
if ( esdhc_send_tuning_cmd ( host , opcode ) ) {
max - = ESDHC_TUNE_CTRL_STEP ;
break ;
}
max + = ESDHC_TUNE_CTRL_STEP ;
}
/* use average delay to get the best timing */
avg = ( min + max ) / 2 ;
esdhc_prepare_tuning ( host , avg ) ;
ret = esdhc_send_tuning_cmd ( host , opcode ) ;
esdhc_post_tuning ( host ) ;
dev_dbg ( mmc_dev ( host - > mmc ) , " tunning %s at 0x%x ret %d \n " ,
ret ? " failed " : " passed " , avg , ret ) ;
return ret ;
}
2013-09-13 15:11:35 +04:00
static int esdhc_change_pinstate ( struct sdhci_host * host ,
unsigned int uhs )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct pltfm_imx_data * imx_data = pltfm_host - > priv ;
struct pinctrl_state * pinctrl ;
dev_dbg ( mmc_dev ( host - > mmc ) , " change pinctrl state for uhs %d \n " , uhs ) ;
if ( IS_ERR ( imx_data - > pinctrl ) | |
IS_ERR ( imx_data - > pins_default ) | |
IS_ERR ( imx_data - > pins_100mhz ) | |
IS_ERR ( imx_data - > pins_200mhz ) )
return - EINVAL ;
switch ( uhs ) {
case MMC_TIMING_UHS_SDR50 :
pinctrl = imx_data - > pins_100mhz ;
break ;
case MMC_TIMING_UHS_SDR104 :
2013-10-30 18:10:42 +04:00
case MMC_TIMING_MMC_HS200 :
2013-09-13 15:11:35 +04:00
pinctrl = imx_data - > pins_200mhz ;
break ;
default :
/* back to default state for other legacy timing */
pinctrl = imx_data - > pins_default ;
}
return pinctrl_select_state ( imx_data - > pinctrl , pinctrl ) ;
}
static int esdhc_set_uhs_signaling ( struct sdhci_host * host , unsigned int uhs )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct pltfm_imx_data * imx_data = pltfm_host - > priv ;
2013-10-18 15:48:47 +04:00
struct esdhc_platform_data * boarddata = & imx_data - > boarddata ;
2013-09-13 15:11:35 +04:00
switch ( uhs ) {
case MMC_TIMING_UHS_SDR12 :
imx_data - > uhs_mode = SDHCI_CTRL_UHS_SDR12 ;
break ;
case MMC_TIMING_UHS_SDR25 :
imx_data - > uhs_mode = SDHCI_CTRL_UHS_SDR25 ;
break ;
case MMC_TIMING_UHS_SDR50 :
imx_data - > uhs_mode = SDHCI_CTRL_UHS_SDR50 ;
break ;
case MMC_TIMING_UHS_SDR104 :
2013-10-30 18:10:42 +04:00
case MMC_TIMING_MMC_HS200 :
2013-09-13 15:11:35 +04:00
imx_data - > uhs_mode = SDHCI_CTRL_UHS_SDR104 ;
break ;
case MMC_TIMING_UHS_DDR50 :
imx_data - > uhs_mode = SDHCI_CTRL_UHS_DDR50 ;
2013-10-18 15:48:46 +04:00
writel ( readl ( host - > ioaddr + ESDHC_MIX_CTRL ) |
ESDHC_MIX_CTRL_DDREN ,
host - > ioaddr + ESDHC_MIX_CTRL ) ;
imx_data - > is_ddr = 1 ;
2013-10-18 15:48:47 +04:00
if ( boarddata - > delay_line ) {
u32 v ;
v = boarddata - > delay_line < <
ESDHC_DLL_OVERRIDE_VAL_SHIFT |
( 1 < < ESDHC_DLL_OVERRIDE_EN_SHIFT ) ;
if ( is_imx53_esdhc ( imx_data ) )
v < < = 1 ;
writel ( v , host - > ioaddr + ESDHC_DLL_CTRL ) ;
}
2013-09-13 15:11:35 +04:00
break ;
}
return esdhc_change_pinstate ( host , uhs ) ;
}
2014-04-25 15:57:18 +04: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 ) ;
}
2013-10-18 15:48:43 +04:00
static struct sdhci_ops sdhci_esdhc_ops = {
2011-03-25 16:18:27 +03:00
. read_l = esdhc_readl_le ,
2011-02-26 16:44:39 +03:00
. read_w = esdhc_readw_le ,
2011-03-25 16:18:27 +03:00
. write_l = esdhc_writel_le ,
2011-02-26 16:44:39 +03:00
. write_w = esdhc_writew_le ,
. write_b = esdhc_writeb_le ,
2013-06-05 17:13:25 +04:00
. set_clock = esdhc_pltfm_set_clock ,
2013-06-05 17:13:26 +04:00
. get_max_clock = esdhc_pltfm_get_max_clock ,
2011-02-26 16:44:39 +03:00
. get_min_clock = esdhc_pltfm_get_min_clock ,
2011-06-21 18:41:51 +04:00
. get_ro = esdhc_pltfm_get_ro ,
2014-04-25 15:57:07 +04:00
. set_bus_width = esdhc_pltfm_set_bus_width ,
2013-09-13 15:11:35 +04:00
. set_uhs_signaling = esdhc_set_uhs_signaling ,
2014-04-25 15:57:18 +04:00
. reset = esdhc_reset ,
2011-02-26 16:44:39 +03:00
} ;
2013-03-13 22:26:03 +04:00
static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
2011-08-12 00:51:46 +04:00
. quirks = ESDHC_DEFAULT_QUIRKS | SDHCI_QUIRK_NO_HISPD_BIT
| SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC
| SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC
2011-05-27 19:48:12 +04:00
| SDHCI_QUIRK_BROKEN_CARD_DETECTION ,
. ops = & sdhci_esdhc_ops ,
} ;
2011-06-30 11:44:44 +04:00
# ifdef CONFIG_OF
2012-11-19 22:23:06 +04:00
static int
2011-06-30 11:44:44 +04:00
sdhci_esdhc_imx_probe_dt ( struct platform_device * pdev ,
struct esdhc_platform_data * boarddata )
{
struct device_node * np = pdev - > dev . of_node ;
if ( ! np )
return - ENODEV ;
2012-05-13 08:14:24 +04:00
if ( of_get_property ( np , " non-removable " , NULL ) )
2011-06-30 11:44:44 +04:00
boarddata - > cd_type = ESDHC_CD_PERMANENT ;
if ( of_get_property ( np , " fsl,cd-controller " , NULL ) )
boarddata - > cd_type = ESDHC_CD_CONTROLLER ;
if ( of_get_property ( np , " fsl,wp-controller " , NULL ) )
boarddata - > wp_type = ESDHC_WP_CONTROLLER ;
boarddata - > cd_gpio = of_get_named_gpio ( np , " cd-gpios " , 0 ) ;
if ( gpio_is_valid ( boarddata - > cd_gpio ) )
boarddata - > cd_type = ESDHC_CD_GPIO ;
boarddata - > wp_gpio = of_get_named_gpio ( np , " wp-gpios " , 0 ) ;
if ( gpio_is_valid ( boarddata - > wp_gpio ) )
boarddata - > wp_type = ESDHC_WP_GPIO ;
2013-01-21 15:02:28 +04:00
of_property_read_u32 ( np , " bus-width " , & boarddata - > max_bus_width ) ;
2013-06-05 17:13:26 +04:00
of_property_read_u32 ( np , " max-frequency " , & boarddata - > f_max ) ;
2013-09-13 15:11:35 +04:00
if ( of_find_property ( np , " no-1-8-v " , NULL ) )
boarddata - > support_vsel = false ;
else
boarddata - > support_vsel = true ;
2013-10-18 15:48:47 +04:00
if ( of_property_read_u32 ( np , " fsl,delay-line " , & boarddata - > delay_line ) )
boarddata - > delay_line = 0 ;
2011-06-30 11:44:44 +04:00
return 0 ;
}
# else
static inline int
sdhci_esdhc_imx_probe_dt ( struct platform_device * pdev ,
struct esdhc_platform_data * boarddata )
{
return - ENODEV ;
}
# endif
2012-11-19 22:23:06 +04:00
static int sdhci_esdhc_imx_probe ( struct platform_device * pdev )
2010-10-15 14:21:04 +04:00
{
2011-06-30 11:44:44 +04:00
const struct of_device_id * of_id =
of_match_device ( imx_esdhc_dt_ids , & pdev - > dev ) ;
2011-05-27 19:48:12 +04:00
struct sdhci_pltfm_host * pltfm_host ;
struct sdhci_host * host ;
struct esdhc_platform_data * boarddata ;
2011-02-26 16:44:39 +03:00
int err ;
2011-03-25 16:18:27 +03:00
struct pltfm_imx_data * imx_data ;
2010-10-15 14:21:04 +04:00
2013-05-30 00:50:05 +04:00
host = sdhci_pltfm_init ( pdev , & sdhci_esdhc_imx_pdata , 0 ) ;
2011-05-27 19:48:12 +04:00
if ( IS_ERR ( host ) )
return PTR_ERR ( host ) ;
pltfm_host = sdhci_priv ( host ) ;
2012-11-26 10:39:43 +04:00
imx_data = devm_kzalloc ( & pdev - > dev , sizeof ( * imx_data ) , GFP_KERNEL ) ;
2011-06-30 11:44:44 +04:00
if ( ! imx_data ) {
err = - ENOMEM ;
2012-11-26 10:39:43 +04:00
goto free_sdhci ;
2011-06-30 11:44:44 +04:00
}
2011-06-30 05:24:26 +04:00
2013-10-17 11:19:47 +04:00
imx_data - > socdata = of_id ? of_id - > data : ( struct esdhc_soc_data * )
pdev - > id_entry - > driver_data ;
2011-05-27 19:48:12 +04:00
pltfm_host - > priv = imx_data ;
2012-03-07 12:31:34 +04:00
imx_data - > clk_ipg = devm_clk_get ( & pdev - > dev , " ipg " ) ;
if ( IS_ERR ( imx_data - > clk_ipg ) ) {
err = PTR_ERR ( imx_data - > clk_ipg ) ;
2012-11-26 10:39:43 +04:00
goto free_sdhci ;
2010-10-15 14:21:04 +04:00
}
2012-03-07 12:31:34 +04:00
imx_data - > clk_ahb = devm_clk_get ( & pdev - > dev , " ahb " ) ;
if ( IS_ERR ( imx_data - > clk_ahb ) ) {
err = PTR_ERR ( imx_data - > clk_ahb ) ;
2012-11-26 10:39:43 +04:00
goto free_sdhci ;
2012-03-07 12:31:34 +04:00
}
imx_data - > clk_per = devm_clk_get ( & pdev - > dev , " per " ) ;
if ( IS_ERR ( imx_data - > clk_per ) ) {
err = PTR_ERR ( imx_data - > clk_per ) ;
2012-11-26 10:39:43 +04:00
goto free_sdhci ;
2012-03-07 12:31:34 +04:00
}
pltfm_host - > clk = imx_data - > clk_per ;
2013-12-26 11:23:53 +04:00
pltfm_host - > clock = clk_get_rate ( pltfm_host - > clk ) ;
2012-03-07 12:31:34 +04:00
clk_prepare_enable ( imx_data - > clk_per ) ;
clk_prepare_enable ( imx_data - > clk_ipg ) ;
clk_prepare_enable ( imx_data - > clk_ahb ) ;
2010-10-15 14:21:04 +04:00
2013-09-13 15:11:35 +04:00
imx_data - > pinctrl = devm_pinctrl_get ( & pdev - > dev ) ;
2012-05-11 10:56:01 +04:00
if ( IS_ERR ( imx_data - > pinctrl ) ) {
err = PTR_ERR ( imx_data - > pinctrl ) ;
2012-11-26 10:39:43 +04:00
goto disable_clk ;
2012-05-11 10:56:01 +04:00
}
2013-09-13 15:11:35 +04:00
imx_data - > pins_default = pinctrl_lookup_state ( imx_data - > pinctrl ,
PINCTRL_STATE_DEFAULT ) ;
if ( IS_ERR ( imx_data - > pins_default ) ) {
err = PTR_ERR ( imx_data - > pins_default ) ;
dev_err ( mmc_dev ( host - > mmc ) , " could not get default state \n " ) ;
goto disable_clk ;
}
2012-04-18 04:30:20 +04:00
host - > quirks | = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL ;
2010-10-23 03:57:21 +04:00
2013-10-17 11:19:47 +04:00
if ( imx_data - > socdata - > flags & ESDHC_FLAG_ENGCM07207 )
2011-02-26 16:44:39 +03:00
/* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */
2011-08-12 00:51:46 +04:00
host - > quirks | = SDHCI_QUIRK_NO_MULTIBLOCK
| SDHCI_QUIRK_BROKEN_ADMA ;
2011-02-26 16:44:39 +03:00
2011-11-10 12:39:32 +04:00
/*
* The imx6q ROM code will change the default watermark level setting
* to something insane . Change it back here .
*/
2013-10-18 15:48:49 +04:00
if ( esdhc_is_usdhc ( imx_data ) ) {
2013-01-15 19:36:53 +04:00
writel ( 0x08100810 , host - > ioaddr + ESDHC_WTMK_LVL ) ;
2013-10-18 15:48:49 +04:00
host - > quirks2 | = SDHCI_QUIRK2_PRESET_VALUE_BROKEN ;
2013-10-30 18:09:52 +04:00
host - > mmc - > caps | = MMC_CAP_1_8V_DDR ;
2013-10-18 15:48:49 +04:00
}
2011-11-10 12:39:32 +04:00
2013-10-18 15:48:43 +04:00
if ( imx_data - > socdata - > flags & ESDHC_FLAG_MAN_TUNING )
sdhci_esdhc_ops . platform_execute_tuning =
esdhc_executing_tuning ;
2013-11-04 12:38:27 +04:00
if ( imx_data - > socdata - > flags & ESDHC_FLAG_STD_TUNING )
writel ( readl ( host - > ioaddr + ESDHC_TUNING_CTRL ) |
ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP ,
host - > ioaddr + ESDHC_TUNING_CTRL ) ;
2011-07-06 18:57:48 +04:00
boarddata = & imx_data - > boarddata ;
2011-06-30 11:44:44 +04:00
if ( sdhci_esdhc_imx_probe_dt ( pdev , boarddata ) < 0 ) {
if ( ! host - > mmc - > parent - > platform_data ) {
dev_err ( mmc_dev ( host - > mmc ) , " no board data! \n " ) ;
err = - EINVAL ;
2012-11-26 10:39:43 +04:00
goto disable_clk ;
2011-06-30 11:44:44 +04:00
}
imx_data - > boarddata = * ( ( struct esdhc_platform_data * )
host - > mmc - > parent - > platform_data ) ;
}
2011-06-21 18:41:51 +04:00
/* write_protect */
if ( boarddata - > wp_type = = ESDHC_WP_GPIO ) {
2012-12-11 18:32:20 +04:00
err = mmc_gpio_request_ro ( host - > mmc , boarddata - > wp_gpio ) ;
2011-02-26 16:44:39 +03:00
if ( err ) {
2012-12-11 18:32:20 +04:00
dev_err ( mmc_dev ( host - > mmc ) ,
" failed to request write-protect gpio! \n " ) ;
goto disable_clk ;
2011-02-26 16:44:39 +03:00
}
2012-12-11 18:32:20 +04:00
host - > mmc - > caps2 | = MMC_CAP2_RO_ACTIVE_HIGH ;
2011-06-21 18:41:51 +04:00
}
/* card_detect */
switch ( boarddata - > cd_type ) {
case ESDHC_CD_GPIO :
2013-08-08 14:38:31 +04:00
err = mmc_gpio_request_cd ( host - > mmc , boarddata - > cd_gpio , 0 ) ;
2011-02-26 16:44:41 +03:00
if ( err ) {
2011-06-21 18:41:51 +04:00
dev_err ( mmc_dev ( host - > mmc ) ,
2012-12-11 18:32:20 +04:00
" failed to request card-detect gpio! \n " ) ;
2012-11-26 10:39:43 +04:00
goto disable_clk ;
2011-02-26 16:44:41 +03:00
}
2011-06-21 18:41:51 +04:00
/* fall through */
2011-02-26 16:44:41 +03:00
2011-06-21 18:41:51 +04:00
case ESDHC_CD_CONTROLLER :
/* we have a working card_detect back */
2011-02-26 16:44:41 +03:00
host - > quirks & = ~ SDHCI_QUIRK_BROKEN_CARD_DETECTION ;
2011-06-21 18:41:51 +04:00
break ;
case ESDHC_CD_PERMANENT :
2013-10-30 18:09:51 +04:00
host - > mmc - > caps | = MMC_CAP_NONREMOVABLE ;
2011-06-21 18:41:51 +04:00
break ;
case ESDHC_CD_NONE :
break ;
2011-02-26 16:44:39 +03:00
}
2010-10-23 03:57:22 +04:00
2013-01-21 15:02:28 +04:00
switch ( boarddata - > max_bus_width ) {
case 8 :
host - > mmc - > caps | = MMC_CAP_8_BIT_DATA | MMC_CAP_4_BIT_DATA ;
break ;
case 4 :
host - > mmc - > caps | = MMC_CAP_4_BIT_DATA ;
break ;
case 1 :
default :
host - > quirks | = SDHCI_QUIRK_FORCE_1_BIT_DATA ;
break ;
}
2013-09-13 15:11:35 +04:00
/* sdr50 and sdr104 needs work on 1.8v signal voltage */
2013-10-17 11:19:45 +04:00
if ( ( boarddata - > support_vsel ) & & esdhc_is_usdhc ( imx_data ) ) {
2013-09-13 15:11:35 +04:00
imx_data - > pins_100mhz = pinctrl_lookup_state ( imx_data - > pinctrl ,
ESDHC_PINCTRL_STATE_100MHZ ) ;
imx_data - > pins_200mhz = pinctrl_lookup_state ( imx_data - > pinctrl ,
ESDHC_PINCTRL_STATE_200MHZ ) ;
if ( IS_ERR ( imx_data - > pins_100mhz ) | |
IS_ERR ( imx_data - > pins_200mhz ) ) {
dev_warn ( mmc_dev ( host - > mmc ) ,
" could not get ultra high speed state, work on normal mode \n " ) ;
/* fall back to not support uhs by specify no 1.8v quirk */
host - > quirks2 | = SDHCI_QUIRK2_NO_1_8_V ;
}
} else {
host - > quirks2 | = SDHCI_QUIRK2_NO_1_8_V ;
}
2011-05-27 19:48:12 +04:00
err = sdhci_add_host ( host ) ;
if ( err )
2012-11-26 10:39:43 +04:00
goto disable_clk ;
2011-05-27 19:48:12 +04:00
2013-11-04 12:38:29 +04:00
pm_runtime_set_active ( & pdev - > dev ) ;
pm_runtime_enable ( & pdev - > dev ) ;
pm_runtime_set_autosuspend_delay ( & pdev - > dev , 50 ) ;
pm_runtime_use_autosuspend ( & pdev - > dev ) ;
pm_suspend_ignore_children ( & pdev - > dev , 1 ) ;
2010-10-15 14:21:04 +04:00
return 0 ;
2011-02-26 16:44:41 +03:00
2012-11-26 10:39:43 +04:00
disable_clk :
2012-03-07 12:31:34 +04:00
clk_disable_unprepare ( imx_data - > clk_per ) ;
clk_disable_unprepare ( imx_data - > clk_ipg ) ;
clk_disable_unprepare ( imx_data - > clk_ahb ) ;
2012-11-26 10:39:43 +04:00
free_sdhci :
2011-05-27 19:48:12 +04:00
sdhci_pltfm_free ( pdev ) ;
return err ;
2010-10-15 14:21:04 +04:00
}
2012-11-19 22:26:03 +04:00
static int sdhci_esdhc_imx_remove ( struct platform_device * pdev )
2010-10-15 14:21:04 +04:00
{
2011-05-27 19:48:12 +04:00
struct sdhci_host * host = platform_get_drvdata ( pdev ) ;
2010-10-15 14:21:04 +04:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
2011-03-25 16:18:27 +03:00
struct pltfm_imx_data * imx_data = pltfm_host - > priv ;
2011-05-27 19:48:12 +04:00
int dead = ( readl ( host - > ioaddr + SDHCI_INT_STATUS ) = = 0xffffffff ) ;
sdhci_remove_host ( host , dead ) ;
2011-02-26 16:44:39 +03:00
2013-11-04 12:38:29 +04:00
pm_runtime_dont_use_autosuspend ( & pdev - > dev ) ;
pm_runtime_disable ( & pdev - > dev ) ;
2013-12-26 11:23:54 +04:00
if ( ! IS_ENABLED ( CONFIG_PM_RUNTIME ) ) {
clk_disable_unprepare ( imx_data - > clk_per ) ;
clk_disable_unprepare ( imx_data - > clk_ipg ) ;
clk_disable_unprepare ( imx_data - > clk_ahb ) ;
}
2012-03-07 12:31:34 +04:00
2011-05-27 19:48:12 +04:00
sdhci_pltfm_free ( pdev ) ;
return 0 ;
2010-10-15 14:21:04 +04:00
}
2013-11-04 12:38:29 +04:00
# ifdef CONFIG_PM_RUNTIME
static int sdhci_esdhc_runtime_suspend ( struct device * dev )
{
struct sdhci_host * host = dev_get_drvdata ( dev ) ;
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct pltfm_imx_data * imx_data = pltfm_host - > priv ;
int ret ;
ret = sdhci_runtime_suspend_host ( host ) ;
2014-04-25 15:55:56 +04:00
if ( ! sdhci_sdio_irq_enabled ( host ) ) {
clk_disable_unprepare ( imx_data - > clk_per ) ;
clk_disable_unprepare ( imx_data - > clk_ipg ) ;
}
2013-11-04 12:38:29 +04:00
clk_disable_unprepare ( imx_data - > clk_ahb ) ;
return ret ;
}
static int sdhci_esdhc_runtime_resume ( struct device * dev )
{
struct sdhci_host * host = dev_get_drvdata ( dev ) ;
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct pltfm_imx_data * imx_data = pltfm_host - > priv ;
2014-04-25 15:55:56 +04:00
if ( ! sdhci_sdio_irq_enabled ( host ) ) {
clk_prepare_enable ( imx_data - > clk_per ) ;
clk_prepare_enable ( imx_data - > clk_ipg ) ;
}
2013-11-04 12:38:29 +04:00
clk_prepare_enable ( imx_data - > clk_ahb ) ;
return sdhci_runtime_resume_host ( host ) ;
}
# endif
static const struct dev_pm_ops sdhci_esdhc_pmops = {
SET_SYSTEM_SLEEP_PM_OPS ( sdhci_pltfm_suspend , sdhci_pltfm_resume )
SET_RUNTIME_PM_OPS ( sdhci_esdhc_runtime_suspend ,
sdhci_esdhc_runtime_resume , NULL )
} ;
2011-05-27 19:48:12 +04:00
static struct platform_driver sdhci_esdhc_imx_driver = {
. driver = {
. name = " sdhci-esdhc-imx " ,
. owner = THIS_MODULE ,
2011-06-30 11:44:44 +04:00
. of_match_table = imx_esdhc_dt_ids ,
2013-11-04 12:38:29 +04:00
. pm = & sdhci_esdhc_pmops ,
2011-05-27 19:48:12 +04:00
} ,
2011-06-30 05:24:26 +04:00
. id_table = imx_esdhc_devtype ,
2011-05-27 19:48:12 +04:00
. probe = sdhci_esdhc_imx_probe ,
2012-11-19 22:20:26 +04:00
. remove = sdhci_esdhc_imx_remove ,
2010-10-15 14:21:04 +04:00
} ;
2011-05-27 19:48:12 +04:00
2011-11-26 08:55:43 +04:00
module_platform_driver ( sdhci_esdhc_imx_driver ) ;
2011-05-27 19:48:12 +04:00
MODULE_DESCRIPTION ( " SDHCI driver for Freescale i.MX eSDHC " ) ;
MODULE_AUTHOR ( " Wolfram Sang <w.sang@pengutronix.de> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;