2010-10-15 12:21:04 +02:00
/*
* Freescale eSDHC i . MX controller driver for the platform bus .
*
* derived from the OF - version .
*
* Copyright ( c ) 2010 Pengutronix e . K .
2015-04-20 15:51:42 +02:00
* Author : Wolfram Sang < kernel @ pengutronix . de >
2010-10-15 12:21:04 +02:00
*
* 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 14:44:39 +01:00
# include <linux/gpio.h>
2011-08-15 10:28:18 +08:00
# include <linux/module.h>
2011-03-25 09:18:27 -04:00
# include <linux/slab.h>
2010-10-15 12:21:04 +02:00
# include <linux/mmc/host.h>
2011-03-21 13:22:16 +08:00
# include <linux/mmc/mmc.h>
# include <linux/mmc/sdio.h>
2012-12-11 22:32:20 +08:00
# include <linux/mmc/slot-gpio.h>
2011-06-30 15:44:44 +08:00
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/of_gpio.h>
2012-05-11 14:56:01 +08:00
# include <linux/pinctrl/consumer.h>
2012-08-24 15:14:29 +02:00
# include <linux/platform_data/mmc-esdhc-imx.h>
2013-11-04 16:38:29 +08:00
# include <linux/pm_runtime.h>
2010-10-15 12:21:04 +02:00
# include "sdhci-pltfm.h"
# include "sdhci-esdhc.h"
2013-01-15 23:36:53 +08:00
# define ESDHC_CTRL_D3CD 0x08
2015-08-11 19:38:30 +08:00
# define ESDHC_BURST_LEN_EN_INCR (1 << 27)
2011-03-21 13:22:16 +08:00
/* VENDOR SPEC register */
2013-01-15 23:36:53 +08:00
# define ESDHC_VENDOR_SPEC 0xc0
# define ESDHC_VENDOR_SPEC_SDIO_QUIRK (1 << 1)
2013-09-13 19:11:34 +08:00
# define ESDHC_VENDOR_SPEC_VSELECT (1 << 1)
2013-09-13 19:11:33 +08:00
# define ESDHC_VENDOR_SPEC_FRC_SDCLK_ON (1 << 8)
2013-01-15 23:36:53 +08:00
# define ESDHC_WTMK_LVL 0x44
# define ESDHC_MIX_CTRL 0x48
2013-10-18 19:48:46 +08:00
# define ESDHC_MIX_CTRL_DDREN (1 << 3)
2013-01-21 19:02:26 +08:00
# define ESDHC_MIX_CTRL_AC23EN (1 << 7)
2013-09-13 19:11:34 +08: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)
2015-08-11 19:38:26 +08:00
# define ESDHC_MIX_CTRL_HS400_EN (1 << 26)
2013-01-21 19:02:26 +08:00
/* Bits 3 and 6 are not SDHCI standard definitions */
# define ESDHC_MIX_CTRL_SDHCI_MASK 0xb7
2013-11-04 16:38:26 +08:00
/* Tuning bits */
# define ESDHC_MIX_CTRL_TUNING_MASK 0x03c00000
2011-03-21 13:22:16 +08:00
2013-10-18 19:48:47 +08: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 19:11:34 +08: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)
2015-08-11 19:38:26 +08:00
/* strobe dll register */
# define ESDHC_STROBE_DLL_CTRL 0x70
# define ESDHC_STROBE_DLL_CTRL_ENABLE (1 << 0)
# define ESDHC_STROBE_DLL_CTRL_RESET (1 << 1)
# define ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT 3
# define ESDHC_STROBE_DLL_STATUS 0x74
# define ESDHC_STROBE_DLL_STS_REF_LOCK (1 << 1)
# define ESDHC_STROBE_DLL_STS_SLV_LOCK 0x1
2013-10-18 19:48:43 +08: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
2015-08-11 19:38:27 +08:00
# define ESDHC_TUNING_STEP_SHIFT 16
2013-10-18 19:48:43 +08:00
2013-09-13 19:11:35 +08:00
/* pinctrl state */
# define ESDHC_PINCTRL_STATE_100MHZ "state_100mhz"
# define ESDHC_PINCTRL_STATE_200MHZ "state_200mhz"
2013-01-21 19:02:28 +08: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-11 16: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 23:36:53 +08:00
# define ESDHC_INT_VENDOR_SPEC_DMA_ERR (1 << 28)
2011-08-11 16:51:46 -04:00
2011-03-21 13:22:16 +08: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 15:19:44 +08: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 15:19:45 +08: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 19:48:43 +08: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)
2015-05-27 18:13:28 +08:00
/*
* The IP has errata ERR004536
* uSDHC : ADMA Length Mismatch Error occurs if the AHB read access is slow ,
* when reading data from the card
*/
# define ESDHC_FLAG_ERR004536 BIT(7)
2015-05-27 18:13:31 +08:00
/* The IP supports HS200 mode */
# define ESDHC_FLAG_HS200 BIT(8)
2015-08-11 19:38:26 +08:00
/* The IP supports HS400 mode */
# define ESDHC_FLAG_HS400 BIT(9)
/* A higher clock ferquency than this rate requires strobell dll control */
# define ESDHC_STROBE_DLL_CLK_FREQ 100000000
2011-03-25 09:18:27 -04:00
2013-10-17 15:19:47 +08: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 19:48:43 +08: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
2015-05-27 18:13:31 +08:00
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_ERR004536
| ESDHC_FLAG_HS200 ,
2011-06-30 09:24:26 +08:00
} ;
2015-05-27 18:13:30 +08:00
static struct esdhc_soc_data usdhc_imx6sx_data = {
. flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
2015-05-27 18:13:31 +08:00
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200 ,
2015-05-27 18:13:30 +08:00
} ;
2015-08-11 19:38:26 +08:00
static struct esdhc_soc_data usdhc_imx7d_data = {
. flags = ESDHC_FLAG_USDHC | ESDHC_FLAG_STD_TUNING
| ESDHC_FLAG_HAVE_CAP1 | ESDHC_FLAG_HS200
| ESDHC_FLAG_HS400 ,
} ;
2011-03-25 09:18:27 -04:00
struct pltfm_imx_data {
u32 scratchpad ;
2012-05-11 14:56:01 +08:00
struct pinctrl * pinctrl ;
2013-09-13 19:11:35 +08:00
struct pinctrl_state * pins_default ;
struct pinctrl_state * pins_100mhz ;
struct pinctrl_state * pins_200mhz ;
2013-10-17 15:19:47 +08:00
const struct esdhc_soc_data * socdata ;
2011-07-06 22:57:48 +08:00
struct esdhc_platform_data boarddata ;
2012-03-07 09:31:34 +01:00
struct clk * clk_ipg ;
struct clk * clk_ahb ;
struct clk * clk_per ;
2013-03-15 09:49:26 +01: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-10-18 19:48:46 +08:00
u32 is_ddr ;
2011-03-25 09:18:27 -04:00
} ;
2015-05-02 00:49:21 +09:00
static const struct platform_device_id imx_esdhc_devtype [ ] = {
2011-06-30 09:24:26 +08:00
{
. name = " sdhci-esdhc-imx25 " ,
2013-10-17 15:19:47 +08:00
. driver_data = ( kernel_ulong_t ) & esdhc_imx25_data ,
2011-06-30 09:24:26 +08:00
} , {
. name = " sdhci-esdhc-imx35 " ,
2013-10-17 15:19:47 +08:00
. driver_data = ( kernel_ulong_t ) & esdhc_imx35_data ,
2011-06-30 09:24:26 +08:00
} , {
. name = " sdhci-esdhc-imx51 " ,
2013-10-17 15:19:47 +08:00
. driver_data = ( kernel_ulong_t ) & esdhc_imx51_data ,
2011-06-30 09:24:26 +08:00
} , {
/* sentinel */
}
} ;
MODULE_DEVICE_TABLE ( platform , imx_esdhc_devtype ) ;
2011-06-30 15:44:44 +08:00
static const struct of_device_id imx_esdhc_dt_ids [ ] = {
2013-10-17 15:19:47 +08: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 , } ,
2015-05-27 18:13:30 +08:00
{ . compatible = " fsl,imx6sx-usdhc " , . data = & usdhc_imx6sx_data , } ,
2013-10-18 19:48:43 +08:00
{ . compatible = " fsl,imx6sl-usdhc " , . data = & usdhc_imx6sl_data , } ,
2013-10-17 15:19:47 +08:00
{ . compatible = " fsl,imx6q-usdhc " , . data = & usdhc_imx6q_data , } ,
2015-08-11 19:38:26 +08:00
{ . compatible = " fsl,imx7d-usdhc " , . data = & usdhc_imx7d_data , } ,
2011-06-30 15:44:44 +08:00
{ /* sentinel */ }
} ;
MODULE_DEVICE_TABLE ( of , imx_esdhc_dt_ids ) ;
2011-06-30 09:24:26 +08:00
static inline int is_imx25_esdhc ( struct pltfm_imx_data * data )
{
2013-10-17 15:19:47 +08:00
return data - > socdata = = & esdhc_imx25_data ;
2011-06-30 09:24:26 +08:00
}
static inline int is_imx53_esdhc ( struct pltfm_imx_data * data )
{
2013-10-17 15:19:47 +08:00
return data - > socdata = = & esdhc_imx53_data ;
2011-06-30 09:24:26 +08:00
}
2011-09-19 17:32:21 +08:00
static inline int is_imx6q_usdhc ( struct pltfm_imx_data * data )
{
2013-10-17 15:19:47 +08:00
return data - > socdata = = & usdhc_imx6q_data ;
2011-09-19 17:32:21 +08:00
}
2013-10-17 15:19:45 +08:00
static inline int esdhc_is_usdhc ( struct pltfm_imx_data * data )
{
2013-10-17 15:19:47 +08:00
return ! ! ( data - > socdata - > flags & ESDHC_FLAG_USDHC ) ;
2013-10-17 15:19:45 +08:00
}
2010-10-15 12:21:04 +02: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 14:44:41 +01:00
static u32 esdhc_readl_le ( struct sdhci_host * host , int reg )
{
2013-03-15 09:49:26 +01:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct pltfm_imx_data * imx_data = pltfm_host - > priv ;
2011-02-26 14:44:41 +01:00
u32 val = readl ( host - > ioaddr + reg ) ;
2013-09-13 19:11:34 +08: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-11 16:51:46 -04:00
if ( unlikely ( reg = = SDHCI_CAPABILITIES ) ) {
2013-10-18 19:48:44 +08: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-11 16: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 19:48:43 +08: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 19:48:48 +08:00
| SDHCI_SUPPORT_SDR50
| SDHCI_USE_SDR50_TUNING ;
2015-08-11 19:38:26 +08:00
if ( imx_data - > socdata - > flags & ESDHC_FLAG_HS400 )
val | = SDHCI_SUPPORT_HS400 ;
2013-10-18 19:48:43 +08:00
}
}
2013-09-13 19:11:34 +08:00
2013-10-17 15:19:45 +08:00
if ( unlikely ( reg = = SDHCI_MAX_CURRENT ) & & esdhc_is_usdhc ( imx_data ) ) {
2013-09-13 19:11:34 +08: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-11 16:51:46 -04:00
if ( unlikely ( reg = = SDHCI_INT_STATUS ) ) {
2013-01-15 23:36:53 +08:00
if ( val & ESDHC_INT_VENDOR_SPEC_DMA_ERR ) {
val & = ~ ESDHC_INT_VENDOR_SPEC_DMA_ERR ;
2011-08-11 16:51:46 -04:00
val | = SDHCI_INT_ADMA_ERROR ;
}
2013-03-15 09:49:26 +01: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-11 16:51:46 -04:00
}
2011-02-26 14:44:41 +01:00
return val ;
}
static void esdhc_writel_le ( struct sdhci_host * host , u32 val , int reg )
{
2011-03-25 09:18:27 -04:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct pltfm_imx_data * imx_data = pltfm_host - > priv ;
2011-08-11 16:45:59 -04:00
u32 data ;
if ( unlikely ( reg = = SDHCI_INT_ENABLE | | reg = = SDHCI_SIGNAL_ENABLE ) ) {
2015-05-27 18:13:27 +08:00
if ( ( val & SDHCI_INT_CARD_INT ) & & ! esdhc_is_usdhc ( imx_data ) ) {
2011-08-11 16:45:59 -04:00
/*
* 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 23:36:53 +08:00
data & = ~ ESDHC_CTRL_D3CD ;
2011-08-11 16:45:59 -04:00
writel ( data , host - > ioaddr + SDHCI_HOST_CONTROL ) ;
2013-01-15 23:36:53 +08:00
data | = ESDHC_CTRL_D3CD ;
2011-08-11 16:45:59 -04:00
writel ( data , host - > ioaddr + SDHCI_HOST_CONTROL ) ;
}
2015-05-27 18:13:26 +08:00
if ( val & SDHCI_INT_ADMA_ERROR ) {
val & = ~ SDHCI_INT_ADMA_ERROR ;
val | = ESDHC_INT_VENDOR_SPEC_DMA_ERR ;
}
2011-08-11 16:45:59 -04:00
}
2011-02-26 14:44:41 +01:00
2013-10-17 15:19:47 +08:00
if ( unlikely ( ( imx_data - > socdata - > flags & ESDHC_FLAG_MULTIBLK_NO_INT )
2011-03-21 13:22:16 +08:00
& & ( reg = = SDHCI_INT_STATUS )
& & ( val & SDHCI_INT_DATA_END ) ) ) {
u32 v ;
2013-01-15 23:36:53 +08:00
v = readl ( host - > ioaddr + ESDHC_VENDOR_SPEC ) ;
v & = ~ ESDHC_VENDOR_SPEC_SDIO_QUIRK ;
writel ( v , host - > ioaddr + ESDHC_VENDOR_SPEC ) ;
2013-03-15 09:49:26 +01: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 13:22:16 +08:00
}
2011-02-26 14:44:41 +01:00
writel ( val , host - > ioaddr + reg ) ;
}
2010-10-15 12:21:04 +02:00
static u16 esdhc_readw_le ( struct sdhci_host * host , int reg )
{
2013-01-15 23:30:27 +08:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct pltfm_imx_data * imx_data = pltfm_host - > priv ;
2013-09-13 19:11:34 +08:00
u16 ret = 0 ;
u32 val ;
2013-01-15 23:30:27 +08:00
2011-09-19 17:32:21 +08:00
if ( unlikely ( reg = = SDHCI_HOST_VERSION ) ) {
2013-01-15 23:30:27 +08:00
reg ^ = 2 ;
2013-10-17 15:19:45 +08:00
if ( esdhc_is_usdhc ( imx_data ) ) {
2013-01-15 23:30:27 +08:00
/*
* The usdhc register returns a wrong host version .
* Correct it here .
*/
return SDHCI_SPEC_300 ;
}
2011-09-19 17:32:21 +08:00
}
2010-10-15 12:21:04 +02:00
2013-09-13 19:11:34 +08: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 15:19:45 +08:00
if ( esdhc_is_usdhc ( imx_data ) ) {
2013-10-18 19:48:43 +08: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 19:11:34 +08:00
}
2013-10-18 19:48:43 +08: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 19:11:34 +08:00
ret & = ~ SDHCI_CTRL_PRESET_VAL_ENABLE ;
return ret ;
}
2013-10-30 22:09:49 +08: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 12:21:04 +02: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 09:18:27 -04:00
struct pltfm_imx_data * imx_data = pltfm_host - > priv ;
2013-09-13 19:11:34 +08:00
u32 new_val = 0 ;
2010-10-15 12:21:04 +02:00
switch ( reg ) {
2013-09-13 19:11:34 +08: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 ;
2015-02-26 23:37:55 +03:00
writel ( new_val , host - > ioaddr + ESDHC_VENDOR_SPEC ) ;
2013-09-13 19:11:34 +08:00
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 ) ;
2013-10-18 19:48:43 +08: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 ) ;
2015-08-11 19:38:27 +08:00
u32 tuning_ctrl ;
2013-11-04 16:38:27 +08: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 19:48:43 +08:00
if ( val & SDHCI_CTRL_EXEC_TUNING ) {
v | = ESDHC_MIX_CTRL_EXE_TUNE ;
m | = ESDHC_MIX_CTRL_FBCLK_SEL ;
2015-08-11 19:38:27 +08:00
tuning_ctrl = readl ( host - > ioaddr + ESDHC_TUNING_CTRL ) ;
tuning_ctrl | = ESDHC_STD_TUNING_EN | ESDHC_TUNING_START_TAP ;
if ( imx_data - > boarddata . tuning_step )
tuning_ctrl | = imx_data - > boarddata . tuning_step < < ESDHC_TUNING_STEP_SHIFT ;
writel ( tuning_ctrl , host - > ioaddr + ESDHC_TUNING_CTRL ) ;
2013-10-18 19:48:43 +08:00
} else {
v & = ~ ESDHC_MIX_CTRL_EXE_TUNE ;
}
writel ( v , host - > ioaddr + SDHCI_ACMD12_ERR ) ;
writel ( m , host - > ioaddr + ESDHC_MIX_CTRL ) ;
}
2013-09-13 19:11:34 +08:00
return ;
2010-10-15 12:21:04 +02:00
case SDHCI_TRANSFER_MODE :
2013-10-17 15:19:47 +08:00
if ( ( imx_data - > socdata - > flags & ESDHC_FLAG_MULTIBLK_NO_INT )
2011-03-21 13:22:16 +08: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 23:36:53 +08:00
v = readl ( host - > ioaddr + ESDHC_VENDOR_SPEC ) ;
v | = ESDHC_VENDOR_SPEC_SDIO_QUIRK ;
writel ( v , host - > ioaddr + ESDHC_VENDOR_SPEC ) ;
2011-03-21 13:22:16 +08:00
}
2013-01-21 19:02:24 +08:00
2013-10-17 15:19:45 +08:00
if ( esdhc_is_usdhc ( imx_data ) ) {
2013-01-21 19:02:24 +08:00
u32 m = readl ( host - > ioaddr + ESDHC_MIX_CTRL ) ;
2013-01-21 19:02:26 +08: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 19:02:24 +08: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 12:21:04 +02:00
return ;
case SDHCI_COMMAND :
2013-03-15 09:49:26 +01:00
if ( host - > cmd - > opcode = = MMC_STOP_TRANSMISSION )
2011-03-21 13:22:16 +08:00
val | = SDHCI_CMD_ABORTCMD ;
2011-09-19 17:32:21 +08:00
2013-03-15 09:49:26 +01:00
if ( ( host - > cmd - > opcode = = MMC_SET_BLOCK_COUNT ) & &
2013-10-17 15:19:47 +08:00
( imx_data - > socdata - > flags & ESDHC_FLAG_MULTIBLK_NO_INT ) )
2013-03-15 09:49:26 +01:00
imx_data - > multiblock_status = MULTIBLK_IN_PROCESS ;
2013-10-17 15:19:45 +08:00
if ( esdhc_is_usdhc ( imx_data ) )
2011-09-19 17:32:21 +08:00
writel ( val < < 16 ,
host - > ioaddr + SDHCI_TRANSFER_MODE ) ;
2013-01-21 19:02:24 +08:00
else
2011-09-19 17:32:21 +08:00
writel ( val < < 16 | imx_data - > scratchpad ,
host - > ioaddr + SDHCI_TRANSFER_MODE ) ;
2010-10-15 12:21:04 +02: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 02: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 12:21:04 +02:00
u32 new_val ;
2013-01-21 19:02:28 +08:00
u32 mask ;
2010-10-15 12:21:04 +02: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 23:36:52 +08:00
/* FSL messed up here, so we need to manually compose it. */
2013-01-21 19:02:28 +08:00
new_val = val & SDHCI_CTRL_LED ;
2012-08-05 23:25:40 +09:00
/* ensure the endianness */
2010-10-15 12:21:04 +02:00
new_val | = ESDHC_HOST_CONTROL_LE ;
2012-07-19 02: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 12:21:04 +02:00
2013-01-21 19:02:28 +08:00
/*
* Do not touch buswidth bits here . This is done in
* esdhc_pltfm_bus_width .
2013-04-15 17:08:35 +02:00
* Do not touch the D3CD bit either which is used for the
* SDIO interrupt errata workaround .
2013-01-21 19:02:28 +08:00
*/
2013-04-15 17:08:35 +02:00
mask = 0xffff & ~ ( ESDHC_CTRL_BUSWIDTH_MASK | ESDHC_CTRL_D3CD ) ;
2013-01-21 19:02:28 +08:00
esdhc_clrset_le ( host , mask , new_val , reg ) ;
2010-10-15 12:21:04 +02:00
return ;
}
esdhc_clrset_le ( host , 0xff , val , reg ) ;
2011-06-21 22:41:51 +08: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 19:02:25 +08:00
if ( ( reg = = SDHCI_SOFTWARE_RESET ) & & ( val & 1 ) ) {
2011-06-21 22:41:51 +08:00
esdhc_clrset_le ( host , 0x7 , 0x7 , ESDHC_SYSTEM_CONTROL ) ;
2013-01-21 19:02:25 +08:00
/*
* The reset on usdhc fails to clear MIX_CTRL register .
* Do it manually here .
*/
2013-10-18 19:48:46 +08:00
if ( esdhc_is_usdhc ( imx_data ) ) {
2013-11-04 16:38:26 +08: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 19:48:46 +08:00
imx_data - > is_ddr = 0 ;
}
2013-01-21 19:02:25 +08:00
}
2010-10-15 12:21:04 +02:00
}
2013-06-05 15:13:26 +02:00
static unsigned int esdhc_pltfm_get_max_clock ( struct sdhci_host * host )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
2015-07-22 20:53:09 +08:00
return pltfm_host - > clock ;
2013-06-05 15:13:26 +02:00
}
2010-10-15 12:21:04 +02: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 15:23:53 +08:00
return pltfm_host - > clock / 256 / 16 ;
2010-10-15 12:21:04 +02:00
}
2013-06-05 15:13:25 +02: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 19:11:33 +08:00
struct pltfm_imx_data * imx_data = pltfm_host - > priv ;
2013-12-26 15:23:53 +08:00
unsigned int host_clock = pltfm_host - > clock ;
2013-09-13 19:11:32 +08:00
int pre_div = 2 ;
int div = 1 ;
2013-09-13 19:11:33 +08:00
u32 temp , val ;
2013-09-13 19:11:32 +08:00
2013-09-13 19:11:33 +08:00
if ( clock = = 0 ) {
2014-04-25 12:58:50 +01:00
host - > mmc - > actual_clock = 0 ;
2013-10-17 15:19:45 +08:00
if ( esdhc_is_usdhc ( imx_data ) ) {
2013-09-13 19:11:33 +08:00
val = readl ( host - > ioaddr + ESDHC_VENDOR_SPEC ) ;
writel ( val & ~ ESDHC_VENDOR_SPEC_FRC_SDCLK_ON ,
host - > ioaddr + ESDHC_VENDOR_SPEC ) ;
}
2014-04-25 12:58:45 +01:00
return ;
2013-09-13 19:11:33 +08:00
}
2013-09-13 19:11:32 +08:00
2013-10-18 19:48:46 +08:00
if ( esdhc_is_usdhc ( imx_data ) & & ! imx_data - > is_ddr )
2013-09-13 19:11:36 +08:00
pre_div = 1 ;
2013-09-13 19:11:32 +08: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 19:11:37 +08:00
host - > mmc - > actual_clock = host_clock / pre_div / div ;
2013-09-13 19:11:32 +08:00
dev_dbg ( mmc_dev ( host - > mmc ) , " desired SD clock: %d, actual: %d \n " ,
2013-09-13 19:11:37 +08:00
clock , host - > mmc - > actual_clock ) ;
2013-09-13 19:11:32 +08:00
2013-10-18 19:48:46 +08:00
if ( imx_data - > is_ddr )
pre_div > > = 2 ;
else
pre_div > > = 1 ;
2013-09-13 19:11:32 +08: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 19:11:33 +08:00
2013-10-17 15:19:45 +08:00
if ( esdhc_is_usdhc ( imx_data ) ) {
2013-09-13 19:11:33 +08:00
val = readl ( host - > ioaddr + ESDHC_VENDOR_SPEC ) ;
writel ( val | ESDHC_VENDOR_SPEC_FRC_SDCLK_ON ,
host - > ioaddr + ESDHC_VENDOR_SPEC ) ;
}
2013-09-13 19:11:32 +08:00
mdelay ( 1 ) ;
2013-06-05 15:13:25 +02:00
}
2011-06-21 22:41:51 +08:00
static unsigned int esdhc_pltfm_get_ro ( struct sdhci_host * host )
{
2011-07-06 22:57:48 +08: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 22:41:51 +08:00
switch ( boarddata - > wp_type ) {
case ESDHC_WP_GPIO :
2012-12-11 22:32:20 +08:00
return mmc_gpio_get_ro ( host - > mmc ) ;
2011-06-21 22:41:51 +08: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 12:57:07 +01:00
static void esdhc_pltfm_set_bus_width ( struct sdhci_host * host , int width )
2013-01-21 19:02:28 +08: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 19:11:34 +08: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 ) ;
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_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 ) ;
2014-12-05 12:59:40 +01:00
if ( ! mmc_send_tuning ( host - > mmc ) )
2013-09-13 19:11:34 +08:00
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 ) ;
2014-12-05 12:59:40 +01:00
if ( mmc_send_tuning ( host - > mmc ) ) {
2013-09-13 19:11:34 +08:00
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 ) ;
2014-12-05 12:59:40 +01:00
ret = mmc_send_tuning ( host - > mmc ) ;
2013-09-13 19:11:34 +08:00
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 19:11:35 +08: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 22:10:42 +08:00
case MMC_TIMING_MMC_HS200 :
2015-08-11 19:38:26 +08:00
case MMC_TIMING_MMC_HS400 :
2013-09-13 19:11:35 +08: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 ) ;
}
2015-08-11 19:38:26 +08:00
/*
* For HS400 eMMC , there is a data_strobe line , this signal is generated
* by the device and used for data output and CRC status response output
* in HS400 mode . The frequency of this signal follows the frequency of
* CLK generated by host . Host receive the data which is aligned to the
* edge of data_strobe line . Due to the time delay between CLK line and
* data_strobe line , if the delay time is larger than one clock cycle ,
* then CLK and data_strobe line will misaligned , read error shows up .
* So when the CLK is higher than 100 MHz , each clock cycle is short enough ,
* host should config the delay target .
*/
static void esdhc_set_strobe_dll ( struct sdhci_host * host )
{
u32 v ;
if ( host - > mmc - > actual_clock > ESDHC_STROBE_DLL_CLK_FREQ ) {
/* force a reset on strobe dll */
writel ( ESDHC_STROBE_DLL_CTRL_RESET ,
host - > ioaddr + ESDHC_STROBE_DLL_CTRL ) ;
/*
* enable strobe dll ctrl and adjust the delay target
* for the uSDHC loopback read clock
*/
v = ESDHC_STROBE_DLL_CTRL_ENABLE |
( 7 < < ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT ) ;
writel ( v , host - > ioaddr + ESDHC_STROBE_DLL_CTRL ) ;
/* wait 1us to make sure strobe dll status register stable */
udelay ( 1 ) ;
v = readl ( host - > ioaddr + ESDHC_STROBE_DLL_STATUS ) ;
if ( ! ( v & ESDHC_STROBE_DLL_STS_REF_LOCK ) )
dev_warn ( mmc_dev ( host - > mmc ) ,
" warning! HS400 strobe DLL status REF not lock! \n " ) ;
if ( ! ( v & ESDHC_STROBE_DLL_STS_SLV_LOCK ) )
dev_warn ( mmc_dev ( host - > mmc ) ,
" warning! HS400 strobe DLL status SLV not lock! \n " ) ;
}
}
2014-04-25 12:59:41 +01:00
static void esdhc_set_uhs_signaling ( struct sdhci_host * host , unsigned timing )
2013-09-13 19:11:35 +08:00
{
2015-08-11 19:38:26 +08:00
u32 m ;
2013-09-13 19:11:35 +08:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct pltfm_imx_data * imx_data = pltfm_host - > priv ;
2013-10-18 19:48:47 +08:00
struct esdhc_platform_data * boarddata = & imx_data - > boarddata ;
2013-09-13 19:11:35 +08:00
2015-08-11 19:38:26 +08:00
/* disable ddr mode and disable HS400 mode */
m = readl ( host - > ioaddr + ESDHC_MIX_CTRL ) ;
m & = ~ ( ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN ) ;
imx_data - > is_ddr = 0 ;
2014-04-25 12:59:41 +01:00
switch ( timing ) {
2013-09-13 19:11:35 +08:00
case MMC_TIMING_UHS_SDR12 :
case MMC_TIMING_UHS_SDR25 :
case MMC_TIMING_UHS_SDR50 :
case MMC_TIMING_UHS_SDR104 :
2013-10-30 22:10:42 +08:00
case MMC_TIMING_MMC_HS200 :
2015-08-11 19:38:26 +08:00
writel ( m , host - > ioaddr + ESDHC_MIX_CTRL ) ;
2013-09-13 19:11:35 +08:00
break ;
case MMC_TIMING_UHS_DDR50 :
2014-05-09 14:53:15 +08:00
case MMC_TIMING_MMC_DDR52 :
2015-08-11 19:38:26 +08:00
m | = ESDHC_MIX_CTRL_DDREN ;
writel ( m , host - > ioaddr + ESDHC_MIX_CTRL ) ;
2013-10-18 19:48:46 +08:00
imx_data - > is_ddr = 1 ;
2013-10-18 19:48:47 +08: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 19:11:35 +08:00
break ;
2015-08-11 19:38:26 +08:00
case MMC_TIMING_MMC_HS400 :
m | = ESDHC_MIX_CTRL_DDREN | ESDHC_MIX_CTRL_HS400_EN ;
writel ( m , host - > ioaddr + ESDHC_MIX_CTRL ) ;
imx_data - > is_ddr = 1 ;
esdhc_set_strobe_dll ( host ) ;
break ;
2013-09-13 19:11:35 +08:00
}
2014-04-25 12:59:41 +01:00
esdhc_change_pinstate ( host , timing ) ;
2013-09-13 19:11:35 +08:00
}
2014-04-25 12:57:18 +01: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 ) ;
}
2014-08-27 15:26:28 +08:00
static unsigned int esdhc_get_max_timeout_count ( struct sdhci_host * host )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct pltfm_imx_data * imx_data = pltfm_host - > priv ;
return esdhc_is_usdhc ( imx_data ) ? 1 < < 28 : 1 < < 27 ;
}
2014-08-27 15:26:30 +08:00
static void esdhc_set_timeout ( struct sdhci_host * host , struct mmc_command * cmd )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct pltfm_imx_data * imx_data = pltfm_host - > priv ;
/* use maximum timeout counter */
sdhci_writeb ( host , esdhc_is_usdhc ( imx_data ) ? 0xF : 0xE ,
SDHCI_TIMEOUT_CONTROL ) ;
}
2013-10-18 19:48:43 +08:00
static struct sdhci_ops sdhci_esdhc_ops = {
2011-03-25 09:18:27 -04:00
. read_l = esdhc_readl_le ,
2011-02-26 14:44:39 +01:00
. read_w = esdhc_readw_le ,
2011-03-25 09:18:27 -04:00
. write_l = esdhc_writel_le ,
2011-02-26 14:44:39 +01:00
. write_w = esdhc_writew_le ,
. write_b = esdhc_writeb_le ,
2013-06-05 15:13:25 +02:00
. set_clock = esdhc_pltfm_set_clock ,
2013-06-05 15:13:26 +02:00
. get_max_clock = esdhc_pltfm_get_max_clock ,
2011-02-26 14:44:39 +01:00
. get_min_clock = esdhc_pltfm_get_min_clock ,
2014-08-27 15:26:28 +08:00
. get_max_timeout_count = esdhc_get_max_timeout_count ,
2011-06-21 22:41:51 +08:00
. get_ro = esdhc_pltfm_get_ro ,
2014-08-27 15:26:30 +08:00
. set_timeout = esdhc_set_timeout ,
2014-04-25 12:57:07 +01:00
. set_bus_width = esdhc_pltfm_set_bus_width ,
2013-09-13 19:11:35 +08:00
. set_uhs_signaling = esdhc_set_uhs_signaling ,
2014-04-25 12:57:18 +01:00
. reset = esdhc_reset ,
2011-02-26 14:44:39 +01:00
} ;
2013-03-13 19:26:03 +01:00
static const struct sdhci_pltfm_data sdhci_esdhc_imx_pdata = {
2011-08-11 16: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 23:48:12 +08:00
| SDHCI_QUIRK_BROKEN_CARD_DETECTION ,
. ops = & sdhci_esdhc_ops ,
} ;
2011-06-30 15:44:44 +08:00
# ifdef CONFIG_OF
2012-11-19 13:23:06 -05:00
static int
2011-06-30 15:44:44 +08:00
sdhci_esdhc_imx_probe_dt ( struct platform_device * pdev ,
2015-03-24 14:45:04 +01:00
struct sdhci_host * host ,
2015-07-22 20:53:06 +08:00
struct pltfm_imx_data * imx_data )
2011-06-30 15:44:44 +08:00
{
struct device_node * np = pdev - > dev . of_node ;
2015-07-22 20:53:06 +08:00
struct esdhc_platform_data * boarddata = & imx_data - > boarddata ;
2015-07-22 20:53:05 +08:00
int ret ;
2011-06-30 15:44:44 +08:00
if ( of_get_property ( np , " fsl,wp-controller " , NULL ) )
boarddata - > wp_type = ESDHC_WP_CONTROLLER ;
boarddata - > wp_gpio = of_get_named_gpio ( np , " wp-gpios " , 0 ) ;
if ( gpio_is_valid ( boarddata - > wp_gpio ) )
boarddata - > wp_type = ESDHC_WP_GPIO ;
2015-08-11 19:38:27 +08:00
of_property_read_u32 ( np , " fsl,tuning-step " , & boarddata - > tuning_step ) ;
2013-09-13 19:11:35 +08:00
if ( of_find_property ( np , " no-1-8-v " , NULL ) )
boarddata - > support_vsel = false ;
else
boarddata - > support_vsel = true ;
2013-10-18 19:48:47 +08:00
if ( of_property_read_u32 ( np , " fsl,delay-line " , & boarddata - > delay_line ) )
boarddata - > delay_line = 0 ;
2015-03-24 14:45:04 +01:00
mmc_of_parse_voltage ( np , & host - > ocr_mask ) ;
2015-07-22 20:53:06 +08:00
/* sdr50 and sdr104 needs work on 1.8v signal voltage */
if ( ( boarddata - > support_vsel ) & & esdhc_is_usdhc ( imx_data ) & &
! IS_ERR ( imx_data - > pins_default ) ) {
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.8 v quirk
*/
host - > quirks2 | = SDHCI_QUIRK2_NO_1_8_V ;
}
} else {
host - > quirks2 | = SDHCI_QUIRK2_NO_1_8_V ;
}
2015-05-09 09:57:08 -03:00
/* call to generic mmc_of_parse to support additional capabilities */
2015-07-22 20:53:05 +08:00
ret = mmc_of_parse ( host - > mmc ) ;
if ( ret )
return ret ;
if ( ! IS_ERR_VALUE ( mmc_gpio_get_cd ( host - > mmc ) ) )
host - > quirks & = ~ SDHCI_QUIRK_BROKEN_CARD_DETECTION ;
return 0 ;
2011-06-30 15:44:44 +08:00
}
# else
static inline int
sdhci_esdhc_imx_probe_dt ( struct platform_device * pdev ,
2015-03-24 14:45:04 +01:00
struct sdhci_host * host ,
2015-07-22 20:53:06 +08:00
struct pltfm_imx_data * imx_data )
2011-06-30 15:44:44 +08:00
{
return - ENODEV ;
}
# endif
2015-07-22 20:53:06 +08:00
static int sdhci_esdhc_imx_probe_nondt ( struct platform_device * pdev ,
struct sdhci_host * host ,
struct pltfm_imx_data * imx_data )
{
struct esdhc_platform_data * boarddata = & imx_data - > boarddata ;
int err ;
if ( ! host - > mmc - > parent - > platform_data ) {
dev_err ( mmc_dev ( host - > mmc ) , " no board data! \n " ) ;
return - EINVAL ;
}
imx_data - > boarddata = * ( ( struct esdhc_platform_data * )
host - > mmc - > parent - > platform_data ) ;
/* write_protect */
if ( boarddata - > wp_type = = ESDHC_WP_GPIO ) {
err = mmc_gpio_request_ro ( host - > mmc , boarddata - > wp_gpio ) ;
if ( err ) {
dev_err ( mmc_dev ( host - > mmc ) ,
" failed to request write-protect gpio! \n " ) ;
return err ;
}
host - > mmc - > caps2 | = MMC_CAP2_RO_ACTIVE_HIGH ;
}
/* card_detect */
switch ( boarddata - > cd_type ) {
case ESDHC_CD_GPIO :
err = mmc_gpio_request_cd ( host - > mmc , boarddata - > cd_gpio , 0 ) ;
if ( err ) {
dev_err ( mmc_dev ( host - > mmc ) ,
" failed to request card-detect gpio! \n " ) ;
return err ;
}
/* fall through */
case ESDHC_CD_CONTROLLER :
/* we have a working card_detect back */
host - > quirks & = ~ SDHCI_QUIRK_BROKEN_CARD_DETECTION ;
break ;
case ESDHC_CD_PERMANENT :
host - > mmc - > caps | = MMC_CAP_NONREMOVABLE ;
break ;
case ESDHC_CD_NONE :
break ;
}
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 ;
}
return 0 ;
}
2012-11-19 13:23:06 -05:00
static int sdhci_esdhc_imx_probe ( struct platform_device * pdev )
2010-10-15 12:21:04 +02:00
{
2011-06-30 15:44:44 +08:00
const struct of_device_id * of_id =
of_match_device ( imx_esdhc_dt_ids , & pdev - > dev ) ;
2011-05-27 23:48:12 +08:00
struct sdhci_pltfm_host * pltfm_host ;
struct sdhci_host * host ;
2011-02-26 14:44:39 +01:00
int err ;
2011-03-25 09:18:27 -04:00
struct pltfm_imx_data * imx_data ;
2010-10-15 12:21:04 +02:00
2013-05-29 13:50:05 -07:00
host = sdhci_pltfm_init ( pdev , & sdhci_esdhc_imx_pdata , 0 ) ;
2011-05-27 23:48:12 +08:00
if ( IS_ERR ( host ) )
return PTR_ERR ( host ) ;
pltfm_host = sdhci_priv ( host ) ;
2012-11-26 14:39:43 +08:00
imx_data = devm_kzalloc ( & pdev - > dev , sizeof ( * imx_data ) , GFP_KERNEL ) ;
2011-06-30 15:44:44 +08:00
if ( ! imx_data ) {
err = - ENOMEM ;
2012-11-26 14:39:43 +08:00
goto free_sdhci ;
2011-06-30 15:44:44 +08:00
}
2011-06-30 09:24:26 +08:00
2013-10-17 15:19:47 +08:00
imx_data - > socdata = of_id ? of_id - > data : ( struct esdhc_soc_data * )
pdev - > id_entry - > driver_data ;
2011-05-27 23:48:12 +08:00
pltfm_host - > priv = imx_data ;
2012-03-07 09:31:34 +01: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 14:39:43 +08:00
goto free_sdhci ;
2010-10-15 12:21:04 +02:00
}
2012-03-07 09:31:34 +01: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 14:39:43 +08:00
goto free_sdhci ;
2012-03-07 09:31:34 +01: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 14:39:43 +08:00
goto free_sdhci ;
2012-03-07 09:31:34 +01:00
}
pltfm_host - > clk = imx_data - > clk_per ;
2013-12-26 15:23:53 +08:00
pltfm_host - > clock = clk_get_rate ( pltfm_host - > clk ) ;
2012-03-07 09:31:34 +01: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 12:21:04 +02:00
2013-09-13 19:11:35 +08:00
imx_data - > pinctrl = devm_pinctrl_get ( & pdev - > dev ) ;
2012-05-11 14:56:01 +08:00
if ( IS_ERR ( imx_data - > pinctrl ) ) {
err = PTR_ERR ( imx_data - > pinctrl ) ;
2012-11-26 14:39:43 +08:00
goto disable_clk ;
2012-05-11 14:56:01 +08:00
}
2013-09-13 19:11:35 +08:00
imx_data - > pins_default = pinctrl_lookup_state ( imx_data - > pinctrl ,
PINCTRL_STATE_DEFAULT ) ;
2014-10-01 04:25:32 -05:00
if ( IS_ERR ( imx_data - > pins_default ) )
dev_warn ( mmc_dev ( host - > mmc ) , " could not get default state \n " ) ;
2013-09-13 19:11:35 +08:00
2012-04-18 02:30:20 +02:00
host - > quirks | = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL ;
2010-10-23 01:57:21 +02:00
2013-10-17 15:19:47 +08:00
if ( imx_data - > socdata - > flags & ESDHC_FLAG_ENGCM07207 )
2011-02-26 14:44:39 +01:00
/* Fix errata ENGcm07207 present on i.MX25 and i.MX35 */
2011-08-11 16:51:46 -04:00
host - > quirks | = SDHCI_QUIRK_NO_MULTIBLOCK
| SDHCI_QUIRK_BROKEN_ADMA ;
2011-02-26 14:44:39 +01:00
2011-11-10 16:39:32 +08:00
/*
* The imx6q ROM code will change the default watermark level setting
* to something insane . Change it back here .
*/
2013-10-18 19:48:49 +08:00
if ( esdhc_is_usdhc ( imx_data ) ) {
2013-01-15 23:36:53 +08:00
writel ( 0x08100810 , host - > ioaddr + ESDHC_WTMK_LVL ) ;
2013-10-18 19:48:49 +08:00
host - > quirks2 | = SDHCI_QUIRK2_PRESET_VALUE_BROKEN ;
2013-10-30 22:09:52 +08:00
host - > mmc - > caps | = MMC_CAP_1_8V_DDR ;
2015-05-27 18:13:28 +08:00
2015-08-11 19:38:30 +08:00
/*
* ROM code will change the bit burst_length_enable setting
* to zero if this usdhc is choosed to boot system . Change
* it back here , otherwise it will impact the performance a
* lot . This bit is used to enable / disable the burst length
* for the external AHB2AXI bridge , it ' s usefully especially
* for INCR transfer because without burst length indicator ,
* the AHB2AXI bridge does not know the burst length in
* advance . And without burst length indicator , AHB INCR
* transfer can only be converted to singles on the AXI side .
*/
writel ( readl ( host - > ioaddr + SDHCI_HOST_CONTROL )
| ESDHC_BURST_LEN_EN_INCR ,
host - > ioaddr + SDHCI_HOST_CONTROL ) ;
2015-05-27 18:13:31 +08:00
if ( ! ( imx_data - > socdata - > flags & ESDHC_FLAG_HS200 ) )
host - > quirks2 | = SDHCI_QUIRK2_BROKEN_HS200 ;
2015-05-27 18:13:28 +08:00
/*
* errata ESDHC_FLAG_ERR004536 fix for MX6Q TO1 .2 and MX6DL
* TO1 .1 , it ' s harmless for MX6SL
*/
writel ( readl ( host - > ioaddr + 0x6c ) | BIT ( 7 ) ,
host - > ioaddr + 0x6c ) ;
2013-10-18 19:48:49 +08:00
}
2011-11-10 16:39:32 +08:00
2013-10-18 19:48:43 +08:00
if ( imx_data - > socdata - > flags & ESDHC_FLAG_MAN_TUNING )
sdhci_esdhc_ops . platform_execute_tuning =
esdhc_executing_tuning ;
2013-11-04 16:38:27 +08: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 ) ;
2015-05-27 18:13:28 +08:00
if ( imx_data - > socdata - > flags & ESDHC_FLAG_ERR004536 )
host - > quirks | = SDHCI_QUIRK_BROKEN_ADMA ;
2015-08-11 19:38:26 +08:00
if ( imx_data - > socdata - > flags & ESDHC_FLAG_HS400 )
host - > quirks2 | = SDHCI_QUIRK2_CAPS_BIT63_FOR_HS400 ;
2015-07-22 20:53:06 +08:00
if ( of_id )
err = sdhci_esdhc_imx_probe_dt ( pdev , host , imx_data ) ;
else
err = sdhci_esdhc_imx_probe_nondt ( pdev , host , imx_data ) ;
if ( err )
goto disable_clk ;
2013-09-13 19:11:35 +08:00
2011-05-27 23:48:12 +08:00
err = sdhci_add_host ( host ) ;
if ( err )
2012-11-26 14:39:43 +08:00
goto disable_clk ;
2011-05-27 23:48:12 +08:00
2013-11-04 16:38:29 +08:00
pm_runtime_set_active ( & pdev - > dev ) ;
pm_runtime_set_autosuspend_delay ( & pdev - > dev , 50 ) ;
pm_runtime_use_autosuspend ( & pdev - > dev ) ;
pm_suspend_ignore_children ( & pdev - > dev , 1 ) ;
2014-12-11 15:12:25 +01:00
pm_runtime_enable ( & pdev - > dev ) ;
2013-11-04 16:38:29 +08:00
2010-10-15 12:21:04 +02:00
return 0 ;
2011-02-26 14:44:41 +01:00
2012-11-26 14:39:43 +08:00
disable_clk :
2012-03-07 09:31:34 +01: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 14:39:43 +08:00
free_sdhci :
2011-05-27 23:48:12 +08:00
sdhci_pltfm_free ( pdev ) ;
return err ;
2010-10-15 12:21:04 +02:00
}
2012-11-19 13:26:03 -05:00
static int sdhci_esdhc_imx_remove ( struct platform_device * pdev )
2010-10-15 12:21:04 +02:00
{
2011-05-27 23:48:12 +08:00
struct sdhci_host * host = platform_get_drvdata ( pdev ) ;
2010-10-15 12:21:04 +02:00
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
2011-03-25 09:18:27 -04:00
struct pltfm_imx_data * imx_data = pltfm_host - > priv ;
2011-05-27 23:48:12 +08:00
int dead = ( readl ( host - > ioaddr + SDHCI_INT_STATUS ) = = 0xffffffff ) ;
2014-12-11 14:56:15 +01:00
pm_runtime_get_sync ( & pdev - > dev ) ;
2013-11-04 16:38:29 +08:00
pm_runtime_disable ( & pdev - > dev ) ;
2014-12-11 14:56:15 +01:00
pm_runtime_put_noidle ( & pdev - > dev ) ;
2013-11-04 16:38:29 +08:00
2014-12-11 14:56:15 +01:00
sdhci_remove_host ( host , dead ) ;
clk_disable_unprepare ( imx_data - > clk_per ) ;
clk_disable_unprepare ( imx_data - > clk_ipg ) ;
clk_disable_unprepare ( imx_data - > clk_ahb ) ;
2012-03-07 09:31:34 +01:00
2011-05-27 23:48:12 +08:00
sdhci_pltfm_free ( pdev ) ;
return 0 ;
2010-10-15 12:21:04 +02:00
}
2014-12-05 03:05:33 +01:00
# ifdef CONFIG_PM
2013-11-04 16:38:29 +08:00
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 12:55:56 +01:00
if ( ! sdhci_sdio_irq_enabled ( host ) ) {
clk_disable_unprepare ( imx_data - > clk_per ) ;
clk_disable_unprepare ( imx_data - > clk_ipg ) ;
}
2013-11-04 16:38:29 +08: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 12:55:56 +01:00
if ( ! sdhci_sdio_irq_enabled ( host ) ) {
clk_prepare_enable ( imx_data - > clk_per ) ;
clk_prepare_enable ( imx_data - > clk_ipg ) ;
}
2013-11-04 16:38:29 +08: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 23:48:12 +08:00
static struct platform_driver sdhci_esdhc_imx_driver = {
. driver = {
. name = " sdhci-esdhc-imx " ,
2011-06-30 15:44:44 +08:00
. of_match_table = imx_esdhc_dt_ids ,
2013-11-04 16:38:29 +08:00
. pm = & sdhci_esdhc_pmops ,
2011-05-27 23:48:12 +08:00
} ,
2011-06-30 09:24:26 +08:00
. id_table = imx_esdhc_devtype ,
2011-05-27 23:48:12 +08:00
. probe = sdhci_esdhc_imx_probe ,
2012-11-19 13:20:26 -05:00
. remove = sdhci_esdhc_imx_remove ,
2010-10-15 12:21:04 +02:00
} ;
2011-05-27 23:48:12 +08:00
2011-11-26 12:55:43 +08:00
module_platform_driver ( sdhci_esdhc_imx_driver ) ;
2011-05-27 23:48:12 +08:00
MODULE_DESCRIPTION ( " SDHCI driver for Freescale i.MX eSDHC " ) ;
2015-04-20 15:51:42 +02:00
MODULE_AUTHOR ( " Wolfram Sang <kernel@pengutronix.de> " ) ;
2011-05-27 23:48:12 +08:00
MODULE_LICENSE ( " GPL v2 " ) ;