2008-12-11 04:37:17 +03:00
/*
2010-02-15 21:03:34 +03:00
* linux / arch / arm / mach - omap2 / hsmmc . c
2008-12-11 04:37:17 +03:00
*
* Copyright ( C ) 2007 - 2008 Texas Instruments
* Copyright ( C ) 2008 Nokia Corporation
* Author : Texas Instruments
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
2010-02-15 21:03:34 +03:00
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/string.h>
2008-12-11 04:37:17 +03:00
# include <linux/delay.h>
# include <mach/hardware.h>
2009-10-20 20:40:47 +04:00
# include <plat/mmc.h>
2010-02-15 21:03:34 +03:00
# include <plat/omap-pm.h>
2008-12-11 04:37:17 +03:00
2010-02-15 21:03:34 +03:00
# include "hsmmc.h"
2010-10-08 21:40:20 +04:00
# include "control.h"
2008-12-11 04:37:17 +03:00
2010-02-15 21:03:34 +03:00
# if defined(CONFIG_MMC_OMAP_HS) || defined(CONFIG_MMC_OMAP_HS_MODULE)
2008-12-11 04:37:17 +03:00
static u16 control_pbias_offset ;
static u16 control_devconf1_offset ;
2010-05-15 22:21:25 +04:00
static u16 control_mmc1 ;
2008-12-11 04:37:17 +03:00
# define HSMMC_NAME_LEN 9
2010-02-15 21:03:34 +03:00
static struct hsmmc_controller {
2009-05-29 01:04:03 +04:00
char name [ HSMMC_NAME_LEN + 1 ] ;
} hsmmc [ OMAP34XX_NR_MMC ] ;
2008-12-11 04:37:17 +03:00
2009-09-23 03:44:40 +04:00
# if defined(CONFIG_ARCH_OMAP3) && defined(CONFIG_PM)
2010-02-15 21:03:34 +03:00
static int hsmmc_get_context_loss ( struct device * dev )
2009-09-23 03:44:40 +04:00
{
2010-02-15 21:03:34 +03:00
return omap_pm_get_dev_context_loss_count ( dev ) ;
2009-09-23 03:44:40 +04:00
}
# else
2010-02-15 21:03:34 +03:00
# define hsmmc_get_context_loss NULL
2009-09-23 03:44:40 +04:00
# endif
2010-05-15 22:21:25 +04:00
static void omap_hsmmc1_before_set_reg ( struct device * dev , int slot ,
2010-02-15 21:03:34 +03:00
int power_on , int vdd )
2008-12-11 04:37:17 +03:00
{
2009-11-22 21:11:08 +03:00
u32 reg , prog_io ;
2008-12-11 04:37:17 +03:00
struct omap_mmc_platform_data * mmc = dev - > platform_data ;
2010-02-15 21:03:34 +03:00
if ( mmc - > slots [ 0 ] . remux )
mmc - > slots [ 0 ] . remux ( dev , slot , power_on ) ;
2009-03-24 04:23:47 +03:00
/*
* Assume we power both OMAP VMMC1 ( for CMD , CLK , DAT0 . .3 ) and the
2009-05-29 01:04:03 +04:00
* card with Vcc regulator ( from twl4030 or whatever ) . OMAP has both
2009-03-24 04:23:47 +03:00
* 1.8 V and 3.0 V modes , controlled by the PBIAS register .
*
* In 8 - bit modes , OMAP VMMC1A ( for DAT4 . .7 ) needs a supply , which
* is most naturally TWL VSIM ; those pins also use PBIAS .
2009-05-29 01:04:03 +04:00
*
* FIXME handle VMMC1A as needed . . .
2009-03-24 04:23:47 +03:00
*/
2008-12-11 04:37:17 +03:00
if ( power_on ) {
if ( cpu_is_omap2430 ( ) ) {
reg = omap_ctrl_readl ( OMAP243X_CONTROL_DEVCONF1 ) ;
if ( ( 1 < < vdd ) > = MMC_VDD_30_31 )
reg | = OMAP243X_MMC1_ACTIVE_OVERWRITE ;
else
reg & = ~ OMAP243X_MMC1_ACTIVE_OVERWRITE ;
omap_ctrl_writel ( reg , OMAP243X_CONTROL_DEVCONF1 ) ;
}
if ( mmc - > slots [ 0 ] . internal_clock ) {
reg = omap_ctrl_readl ( OMAP2_CONTROL_DEVCONF0 ) ;
reg | = OMAP2_MMCSDIO1ADPCLKISEL ;
omap_ctrl_writel ( reg , OMAP2_CONTROL_DEVCONF0 ) ;
}
reg = omap_ctrl_readl ( control_pbias_offset ) ;
2009-11-22 21:11:08 +03:00
if ( cpu_is_omap3630 ( ) ) {
/* Set MMC I/O to 52Mhz */
prog_io = omap_ctrl_readl ( OMAP343X_CONTROL_PROG_IO1 ) ;
prog_io | = OMAP3630_PRG_SDMMC1_SPEEDCTRL ;
omap_ctrl_writel ( prog_io , OMAP343X_CONTROL_PROG_IO1 ) ;
} else {
reg | = OMAP2_PBIASSPEEDCTRL0 ;
}
2008-12-11 04:37:17 +03:00
reg & = ~ OMAP2_PBIASLITEPWRDNZ0 ;
omap_ctrl_writel ( reg , control_pbias_offset ) ;
2010-02-15 21:03:34 +03:00
} else {
reg = omap_ctrl_readl ( control_pbias_offset ) ;
reg & = ~ OMAP2_PBIASLITEPWRDNZ0 ;
omap_ctrl_writel ( reg , control_pbias_offset ) ;
}
}
2010-05-15 22:21:25 +04:00
static void omap_hsmmc1_after_set_reg ( struct device * dev , int slot ,
2010-02-15 21:03:34 +03:00
int power_on , int vdd )
{
u32 reg ;
2008-12-11 04:37:17 +03:00
2010-02-15 21:03:34 +03:00
/* 100ms delay required for PBIAS configuration */
msleep ( 100 ) ;
2008-12-11 04:37:17 +03:00
2010-02-15 21:03:34 +03:00
if ( power_on ) {
2008-12-11 04:37:17 +03:00
reg = omap_ctrl_readl ( control_pbias_offset ) ;
reg | = ( OMAP2_PBIASLITEPWRDNZ0 | OMAP2_PBIASSPEEDCTRL0 ) ;
if ( ( 1 < < vdd ) < = MMC_VDD_165_195 )
reg & = ~ OMAP2_PBIASLITEVMODE0 ;
else
reg | = OMAP2_PBIASLITEVMODE0 ;
omap_ctrl_writel ( reg , control_pbias_offset ) ;
} else {
reg = omap_ctrl_readl ( control_pbias_offset ) ;
reg | = ( OMAP2_PBIASSPEEDCTRL0 | OMAP2_PBIASLITEPWRDNZ0 |
OMAP2_PBIASLITEVMODE0 ) ;
omap_ctrl_writel ( reg , control_pbias_offset ) ;
}
}
2010-05-15 22:21:25 +04:00
static void omap4_hsmmc1_before_set_reg ( struct device * dev , int slot ,
int power_on , int vdd )
{
u32 reg ;
/*
* Assume we power both OMAP VMMC1 ( for CMD , CLK , DAT0 . .3 ) and the
* card with Vcc regulator ( from twl4030 or whatever ) . OMAP has both
* 1.8 V and 3.0 V modes , controlled by the PBIAS register .
*
* In 8 - bit modes , OMAP VMMC1A ( for DAT4 . .7 ) needs a supply , which
* is most naturally TWL VSIM ; those pins also use PBIAS .
*
* FIXME handle VMMC1A as needed . . .
*/
2010-09-28 00:02:58 +04:00
reg = omap4_ctrl_pad_readl ( control_pbias_offset ) ;
reg & = ~ ( OMAP4_MMC1_PBIASLITE_PWRDNZ_MASK |
OMAP4_MMC1_PWRDNZ_MASK |
OMAP4_USBC1_ICUSB_PWRDNZ_MASK ) ;
omap4_ctrl_pad_writel ( reg , control_pbias_offset ) ;
2010-05-15 22:21:25 +04:00
}
static void omap4_hsmmc1_after_set_reg ( struct device * dev , int slot ,
int power_on , int vdd )
{
u32 reg ;
if ( power_on ) {
2010-09-28 00:02:58 +04:00
reg = omap4_ctrl_pad_readl ( control_pbias_offset ) ;
reg | = OMAP4_MMC1_PBIASLITE_PWRDNZ_MASK ;
2010-05-15 22:21:25 +04:00
if ( ( 1 < < vdd ) < = MMC_VDD_165_195 )
2010-09-28 00:02:58 +04:00
reg & = ~ OMAP4_MMC1_PBIASLITE_VMODE_MASK ;
2010-05-15 22:21:25 +04:00
else
2010-09-28 00:02:58 +04:00
reg | = OMAP4_MMC1_PBIASLITE_VMODE_MASK ;
reg | = ( OMAP4_MMC1_PBIASLITE_PWRDNZ_MASK |
OMAP4_MMC1_PWRDNZ_MASK |
OMAP4_USBC1_ICUSB_PWRDNZ_MASK ) ;
omap4_ctrl_pad_writel ( reg , control_pbias_offset ) ;
2010-05-15 22:21:25 +04:00
/* 4 microsec delay for comparator to generate an error*/
udelay ( 4 ) ;
2010-09-28 00:02:58 +04:00
reg = omap4_ctrl_pad_readl ( control_pbias_offset ) ;
if ( reg & OMAP4_MMC1_PBIASLITE_VMODE_ERROR_MASK ) {
2010-05-15 22:21:25 +04:00
pr_err ( " Pbias Voltage is not same as LDO \n " ) ;
/* Caution : On VMODE_ERROR Power Down MMC IO */
2010-09-28 00:02:58 +04:00
reg & = ~ ( OMAP4_MMC1_PWRDNZ_MASK |
OMAP4_USBC1_ICUSB_PWRDNZ_MASK ) ;
omap4_ctrl_pad_writel ( reg , control_pbias_offset ) ;
2010-05-15 22:21:25 +04:00
}
} else {
2010-09-28 00:02:58 +04:00
reg = omap4_ctrl_pad_readl ( control_pbias_offset ) ;
reg | = ( OMAP4_MMC1_PBIASLITE_PWRDNZ_MASK |
OMAP4_MMC1_PWRDNZ_MASK |
OMAP4_MMC1_PBIASLITE_VMODE_MASK |
OMAP4_USBC1_ICUSB_PWRDNZ_MASK ) ;
omap4_ctrl_pad_writel ( reg , control_pbias_offset ) ;
2010-05-15 22:21:25 +04:00
}
}
2010-02-15 21:03:34 +03:00
static void hsmmc23_before_set_reg ( struct device * dev , int slot ,
int power_on , int vdd )
2008-12-11 04:37:17 +03:00
{
struct omap_mmc_platform_data * mmc = dev - > platform_data ;
2009-05-29 01:04:03 +04:00
2010-02-15 21:03:34 +03:00
if ( mmc - > slots [ 0 ] . remux )
mmc - > slots [ 0 ] . remux ( dev , slot , power_on ) ;
2008-12-11 04:37:17 +03:00
if ( power_on ) {
2010-02-15 21:03:34 +03:00
/* Only MMC2 supports a CLKIN */
2008-12-11 04:37:17 +03:00
if ( mmc - > slots [ 0 ] . internal_clock ) {
u32 reg ;
reg = omap_ctrl_readl ( control_devconf1_offset ) ;
reg | = OMAP2_MMCSDIO2ADPCLKISEL ;
omap_ctrl_writel ( reg , control_devconf1_offset ) ;
}
2009-09-23 03:44:50 +04:00
}
}
2010-05-13 16:39:31 +04:00
static int nop_mmc_set_power ( struct device * dev , int slot , int power_on ,
int vdd )
{
return 0 ;
}
2008-12-11 04:37:17 +03:00
static struct omap_mmc_platform_data * hsmmc_data [ OMAP34XX_NR_MMC ] __initdata ;
2010-02-15 21:03:34 +03:00
void __init omap2_hsmmc_init ( struct omap2_hsmmc_info * controllers )
2008-12-11 04:37:17 +03:00
{
2010-02-15 21:03:34 +03:00
struct omap2_hsmmc_info * c ;
2008-12-11 04:37:17 +03:00
int nr_hsmmc = ARRAY_SIZE ( hsmmc_data ) ;
2010-02-04 14:06:59 +03:00
int i ;
2010-05-15 22:21:25 +04:00
u32 reg ;
2008-12-11 04:37:17 +03:00
2010-05-15 22:21:25 +04:00
if ( ! cpu_is_omap44xx ( ) ) {
if ( cpu_is_omap2430 ( ) ) {
control_pbias_offset = OMAP243X_CONTROL_PBIAS_LITE ;
control_devconf1_offset = OMAP243X_CONTROL_DEVCONF1 ;
} else {
control_pbias_offset = OMAP343X_CONTROL_PBIAS_LITE ;
control_devconf1_offset = OMAP343X_CONTROL_DEVCONF1 ;
}
2008-12-11 04:37:17 +03:00
} else {
2010-09-28 00:02:58 +04:00
control_pbias_offset =
OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_PBIASLITE ;
control_mmc1 = OMAP4_CTRL_MODULE_PAD_CORE_CONTROL_MMC1 ;
reg = omap4_ctrl_pad_readl ( control_mmc1 ) ;
reg | = ( OMAP4_SDMMC1_PUSTRENGTH_GRP0_MASK |
OMAP4_SDMMC1_PUSTRENGTH_GRP1_MASK ) ;
reg & = ~ ( OMAP4_SDMMC1_PUSTRENGTH_GRP2_MASK |
OMAP4_SDMMC1_PUSTRENGTH_GRP3_MASK ) ;
reg | = ( OMAP4_USBC1_DR0_SPEEDCTRL_MASK |
OMAP4_SDMMC1_DR1_SPEEDCTRL_MASK |
OMAP4_SDMMC1_DR2_SPEEDCTRL_MASK ) ;
omap4_ctrl_pad_writel ( reg , control_mmc1 ) ;
2008-12-11 04:37:17 +03:00
}
for ( c = controllers ; c - > mmc ; c + + ) {
2010-02-15 21:03:34 +03:00
struct hsmmc_controller * hc = hsmmc + c - > mmc - 1 ;
2008-12-11 04:37:17 +03:00
struct omap_mmc_platform_data * mmc = hsmmc_data [ c - > mmc - 1 ] ;
if ( ! c - > mmc | | c - > mmc > nr_hsmmc ) {
pr_debug ( " MMC%d: no such controller \n " , c - > mmc ) ;
continue ;
}
if ( mmc ) {
pr_debug ( " MMC%d: already configured \n " , c - > mmc ) ;
continue ;
}
2010-02-15 21:03:34 +03:00
mmc = kzalloc ( sizeof ( struct omap_mmc_platform_data ) ,
GFP_KERNEL ) ;
2008-12-11 04:37:17 +03:00
if ( ! mmc ) {
pr_err ( " Cannot allocate memory for mmc device! \n " ) ;
2010-02-04 14:06:59 +03:00
goto done ;
2008-12-11 04:37:17 +03:00
}
2009-03-24 04:23:48 +03:00
if ( c - > name )
2010-02-15 21:03:34 +03:00
strncpy ( hc - > name , c - > name , HSMMC_NAME_LEN ) ;
2009-03-24 04:23:48 +03:00
else
2010-02-15 21:03:34 +03:00
snprintf ( hc - > name , ARRAY_SIZE ( hc - > name ) ,
2009-03-24 04:23:48 +03:00
" mmc%islot%i " , c - > mmc , 1 ) ;
2010-02-15 21:03:34 +03:00
mmc - > slots [ 0 ] . name = hc - > name ;
2008-12-11 04:37:17 +03:00
mmc - > nr_slots = 1 ;
2010-09-15 18:49:23 +04:00
mmc - > slots [ 0 ] . caps = c - > caps ;
2008-12-11 04:37:17 +03:00
mmc - > slots [ 0 ] . internal_clock = ! c - > ext_clock ;
mmc - > dma_mask = 0xffffffff ;
2010-10-02 03:35:28 +04:00
if ( cpu_is_omap44xx ( ) )
mmc - > reg_offset = OMAP4_MMC_REG_OFFSET ;
else
mmc - > reg_offset = 0 ;
2008-12-11 04:37:17 +03:00
2010-02-15 21:03:34 +03:00
mmc - > get_context_loss_count = hsmmc_get_context_loss ;
2009-09-23 03:44:40 +04:00
2010-02-15 21:03:34 +03:00
mmc - > slots [ 0 ] . switch_pin = c - > gpio_cd ;
mmc - > slots [ 0 ] . gpio_wp = c - > gpio_wp ;
2008-12-11 04:37:17 +03:00
2010-02-15 21:03:34 +03:00
mmc - > slots [ 0 ] . remux = c - > remux ;
2010-08-11 05:01:52 +04:00
mmc - > slots [ 0 ] . init_card = c - > init_card ;
2010-02-15 21:03:34 +03:00
2010-02-15 21:03:34 +03:00
if ( c - > cover_only )
mmc - > slots [ 0 ] . cover = 1 ;
2008-12-11 04:37:17 +03:00
2009-09-23 03:44:48 +04:00
if ( c - > nonremovable )
mmc - > slots [ 0 ] . nonremovable = 1 ;
2009-09-23 03:44:49 +04:00
if ( c - > power_saving )
mmc - > slots [ 0 ] . power_saving = 1 ;
2010-02-15 21:03:34 +03:00
if ( c - > no_off )
mmc - > slots [ 0 ] . no_off = 1 ;
2010-02-15 21:03:34 +03:00
if ( c - > vcc_aux_disable_is_sleep )
mmc - > slots [ 0 ] . vcc_aux_disable_is_sleep = 1 ;
2009-05-29 01:04:03 +04:00
/* NOTE: MMC slots should have a Vcc regulator set up.
* This may be from a TWL4030 - family chip , another
* controllable regulator , or a fixed supply .
*
* temporary HACK : ocr_mask instead of fixed supply
2008-12-11 04:37:17 +03:00
*/
2009-05-29 01:04:03 +04:00
mmc - > slots [ 0 ] . ocr_mask = c - > ocr_mask ;
2008-12-11 04:37:17 +03:00
2010-05-13 16:39:31 +04:00
if ( cpu_is_omap3517 ( ) | | cpu_is_omap3505 ( ) )
mmc - > slots [ 0 ] . set_power = nop_mmc_set_power ;
else
mmc - > slots [ 0 ] . features | = HSMMC_HAS_PBIAS ;
2010-10-02 03:35:25 +04:00
if ( cpu_is_omap44xx ( ) & & ( omap_rev ( ) > OMAP4430_REV_ES1_0 ) )
mmc - > slots [ 0 ] . features | = HSMMC_HAS_UPDATED_RESET ;
2008-12-11 04:37:17 +03:00
switch ( c - > mmc ) {
case 1 :
2010-05-13 16:39:31 +04:00
if ( mmc - > slots [ 0 ] . features & HSMMC_HAS_PBIAS ) {
/* on-chip level shifting via PBIAS0/PBIAS1 */
2010-05-15 22:21:25 +04:00
if ( cpu_is_omap44xx ( ) ) {
mmc - > slots [ 0 ] . before_set_reg =
omap4_hsmmc1_before_set_reg ;
mmc - > slots [ 0 ] . after_set_reg =
omap4_hsmmc1_after_set_reg ;
} else {
mmc - > slots [ 0 ] . before_set_reg =
omap_hsmmc1_before_set_reg ;
mmc - > slots [ 0 ] . after_set_reg =
omap_hsmmc1_after_set_reg ;
}
2010-05-13 16:39:31 +04:00
}
2009-11-22 21:11:07 +03:00
/* Omap3630 HSMMC1 supports only 4-bit */
2010-09-15 18:49:23 +04:00
if ( cpu_is_omap3630 ( ) & &
( c - > caps & MMC_CAP_8_BIT_DATA ) ) {
c - > caps & = ~ MMC_CAP_8_BIT_DATA ;
c - > caps | = MMC_CAP_4_BIT_DATA ;
mmc - > slots [ 0 ] . caps = c - > caps ;
2009-11-22 21:11:07 +03:00
}
2008-12-11 04:37:17 +03:00
break ;
case 2 :
2009-05-29 01:04:03 +04:00
if ( c - > ext_clock )
c - > transceiver = 1 ;
2010-09-15 18:49:23 +04:00
if ( c - > transceiver & & ( c - > caps & MMC_CAP_8_BIT_DATA ) ) {
c - > caps & = ~ MMC_CAP_8_BIT_DATA ;
c - > caps | = MMC_CAP_4_BIT_DATA ;
}
2009-05-29 01:04:03 +04:00
/* FALLTHROUGH */
2009-03-24 04:23:47 +03:00
case 3 :
2010-05-13 16:39:31 +04:00
if ( mmc - > slots [ 0 ] . features & HSMMC_HAS_PBIAS ) {
/* off-chip level shifting, or none */
mmc - > slots [ 0 ] . before_set_reg = hsmmc23_before_set_reg ;
mmc - > slots [ 0 ] . after_set_reg = NULL ;
}
2009-03-24 04:23:47 +03:00
break ;
2011-02-15 11:40:36 +03:00
case 4 :
case 5 :
mmc - > slots [ 0 ] . before_set_reg = NULL ;
mmc - > slots [ 0 ] . after_set_reg = NULL ;
break ;
2008-12-11 04:37:17 +03:00
default :
pr_err ( " MMC%d configuration not supported! \n " , c - > mmc ) ;
2009-03-24 04:23:47 +03:00
kfree ( mmc ) ;
2008-12-11 04:37:17 +03:00
continue ;
}
hsmmc_data [ c - > mmc - 1 ] = mmc ;
}
omap2_init_mmc ( hsmmc_data , OMAP34XX_NR_MMC ) ;
2009-03-24 04:23:47 +03:00
/* pass the device nodes back to board setup code */
for ( c = controllers ; c - > mmc ; c + + ) {
struct omap_mmc_platform_data * mmc = hsmmc_data [ c - > mmc - 1 ] ;
if ( ! c - > mmc | | c - > mmc > nr_hsmmc )
continue ;
c - > dev = mmc - > dev ;
}
2010-02-04 14:06:59 +03:00
done :
for ( i = 0 ; i < nr_hsmmc ; i + + )
kfree ( hsmmc_data [ i ] ) ;
2008-12-11 04:37:17 +03:00
}
# endif