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>
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)
# define ESDHC_WTMK_LVL 0x44
# define ESDHC_MIX_CTRL 0x48
2011-03-21 08:22:16 +03:00
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 .
*/
# define ESDHC_FLAG_MULTIBLK_NO_INT (1 << 1)
2011-03-25 16:18:27 +03:00
2011-06-30 05:24:26 +04:00
enum imx_esdhc_type {
IMX25_ESDHC ,
IMX35_ESDHC ,
IMX51_ESDHC ,
IMX53_ESDHC ,
2011-09-19 13:32:21 +04:00
IMX6Q_USDHC ,
2011-06-30 05:24:26 +04:00
} ;
2011-03-25 16:18:27 +03:00
struct pltfm_imx_data {
int flags ;
u32 scratchpad ;
2011-06-30 05:24:26 +04:00
enum imx_esdhc_type devtype ;
2012-05-11 10:56:01 +04:00
struct pinctrl * pinctrl ;
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 ;
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 " ,
. driver_data = IMX25_ESDHC ,
} , {
. name = " sdhci-esdhc-imx35 " ,
. driver_data = IMX35_ESDHC ,
} , {
. name = " sdhci-esdhc-imx51 " ,
. driver_data = IMX51_ESDHC ,
} , {
. name = " sdhci-esdhc-imx53 " ,
. driver_data = IMX53_ESDHC ,
2011-09-19 13:32:21 +04:00
} , {
. name = " sdhci-usdhc-imx6q " ,
. driver_data = IMX6Q_USDHC ,
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 [ ] = {
{ . compatible = " fsl,imx25-esdhc " , . data = & imx_esdhc_devtype [ IMX25_ESDHC ] , } ,
{ . compatible = " fsl,imx35-esdhc " , . data = & imx_esdhc_devtype [ IMX35_ESDHC ] , } ,
{ . compatible = " fsl,imx51-esdhc " , . data = & imx_esdhc_devtype [ IMX51_ESDHC ] , } ,
{ . compatible = " fsl,imx53-esdhc " , . data = & imx_esdhc_devtype [ IMX53_ESDHC ] , } ,
2011-09-19 13:32:21 +04:00
{ . compatible = " fsl,imx6q-usdhc " , . data = & imx_esdhc_devtype [ IMX6Q_USDHC ] , } ,
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 )
{
return data - > devtype = = IMX25_ESDHC ;
}
static inline int is_imx35_esdhc ( struct pltfm_imx_data * data )
{
return data - > devtype = = IMX35_ESDHC ;
}
static inline int is_imx51_esdhc ( struct pltfm_imx_data * data )
{
return data - > devtype = = IMX51_ESDHC ;
}
static inline int is_imx53_esdhc ( struct pltfm_imx_data * data )
{
return data - > devtype = = IMX53_ESDHC ;
}
2011-09-19 13:32:21 +04:00
static inline int is_imx6q_usdhc ( struct pltfm_imx_data * data )
{
return data - > devtype = = IMX6Q_USDHC ;
}
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 )
{
u32 val = readl ( host - > ioaddr + reg ) ;
2011-08-12 00:51:46 +04:00
if ( unlikely ( reg = = SDHCI_CAPABILITIES ) ) {
/* 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 ;
}
}
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 ;
}
}
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
2011-03-21 08:22:16 +03:00
if ( unlikely ( ( imx_data - > flags & ESDHC_FLAG_MULTIBLK_NO_INT )
& & ( 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 ) ;
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 ;
2011-09-19 13:32:21 +04:00
if ( unlikely ( reg = = SDHCI_HOST_VERSION ) ) {
2013-01-15 19:30:27 +04:00
reg ^ = 2 ;
if ( is_imx6q_usdhc ( imx_data ) ) {
/*
* 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
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 ;
2010-10-15 14:21:04 +04:00
switch ( reg ) {
case SDHCI_TRANSFER_MODE :
2011-03-21 08:22:16 +03:00
if ( ( imx_data - > flags & ESDHC_FLAG_MULTIBLK_NO_INT )
& & ( 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
if ( is_imx6q_usdhc ( imx_data ) ) {
u32 m = readl ( host - > ioaddr + ESDHC_MIX_CTRL ) ;
m = val | ( m & 0xffff0000 ) ;
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 :
2012-02-17 14:51:49 +04:00
if ( ( host - > cmd - > opcode = = MMC_STOP_TRANSMISSION | |
host - > cmd - > opcode = = MMC_SET_BLOCK_COUNT ) & &
( imx_data - > flags & ESDHC_FLAG_MULTIBLK_NO_INT ) )
2011-03-21 08:22:16 +03:00
val | = SDHCI_CMD_ABORTCMD ;
2011-09-19 13:32:21 +04:00
2013-01-21 15:02:24 +04:00
if ( is_imx6q_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 ;
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. */
new_val = val & ( SDHCI_CTRL_LED | SDHCI_CTRL_4BITBUS ) ;
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
esdhc_clrset_le ( host , 0xffff , new_val , reg ) ;
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 .
*/
if ( is_imx6q_usdhc ( imx_data ) )
writel ( 0 , host - > ioaddr + ESDHC_MIX_CTRL ) ;
}
2010-10-15 14:21:04 +04:00
}
static unsigned int esdhc_pltfm_get_max_clock ( struct sdhci_host * host )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
return clk_get_rate ( pltfm_host - > clk ) ;
}
static unsigned int esdhc_pltfm_get_min_clock ( struct sdhci_host * host )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
return clk_get_rate ( pltfm_host - > clk ) / 256 / 16 ;
}
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 ;
}
2011-02-26 16:44:39 +03: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 ,
. set_clock = esdhc_set_clock ,
. get_max_clock = esdhc_pltfm_get_max_clock ,
. get_min_clock = esdhc_pltfm_get_min_clock ,
2011-06-21 18:41:51 +04:00
. get_ro = esdhc_pltfm_get_ro ,
2011-02-26 16:44:39 +03:00
} ;
2011-05-27 19:48:12 +04:00
static 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 ;
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
2011-05-27 19:48:12 +04:00
host = sdhci_pltfm_init ( pdev , & sdhci_esdhc_imx_pdata ) ;
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
2011-06-30 11:44:44 +04:00
if ( of_id )
pdev - > id_entry = of_id - > data ;
2011-06-30 05:24:26 +04:00
imx_data - > devtype = 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 ;
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
2012-05-11 10:56:01 +04:00
imx_data - > pinctrl = devm_pinctrl_get_select_default ( & pdev - > dev ) ;
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
}
2012-04-18 04:30:20 +04:00
host - > quirks | = SDHCI_QUIRK_BROKEN_TIMEOUT_VAL ;
2010-10-23 03:57:21 +04:00
2011-06-30 05:24:26 +04:00
if ( is_imx25_esdhc ( imx_data ) | | is_imx35_esdhc ( imx_data ) )
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-06-30 05:24:26 +04:00
if ( is_imx53_esdhc ( imx_data ) )
2011-03-21 08:22:16 +03:00
imx_data - > flags | = ESDHC_FLAG_MULTIBLK_NO_INT ;
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 .
*/
if ( is_imx6q_usdhc ( imx_data ) )
2013-01-15 19:36:53 +04:00
writel ( 0x08100810 , host - > ioaddr + ESDHC_WTMK_LVL ) ;
2011-11-10 12:39:32 +04:00
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 :
2012-12-11 18:32:20 +04:00
err = mmc_gpio_request_cd ( host - > mmc , boarddata - > cd_gpio ) ;
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 :
host - > mmc - > caps = MMC_CAP_NONREMOVABLE ;
break ;
case ESDHC_CD_NONE :
break ;
2011-02-26 16:44:39 +03:00
}
2010-10-23 03:57:22 +04:00
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
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
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 ) ;
2011-05-27 19:48:12 +04:00
sdhci_pltfm_free ( pdev ) ;
return 0 ;
2010-10-15 14:21:04 +04:00
}
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 ,
2011-11-03 14:09:45 +04:00
. pm = SDHCI_PLTFM_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 " ) ;