@ -767,6 +767,7 @@ static int mmc_get_capabilities(struct mmc *mmc)
mmc - > card_caps | = MMC_MODE_4BIT | MMC_MODE_8BIT ;
cardtype = ext_csd [ EXT_CSD_CARD_TYPE ] & 0x3f ;
mmc - > cardtype = cardtype ;
if ( cardtype & ( EXT_CSD_CARD_TYPE_HS200_1_2V |
EXT_CSD_CARD_TYPE_HS200_1_8V ) ) {
@ -1441,10 +1442,30 @@ struct mode_width_tuning {
uint tuning ;
} ;
int mmc_voltage_to_mv ( enum mmc_voltage voltage )
{
switch ( voltage ) {
case MMC_SIGNAL_VOLTAGE_000 : return 0 ;
case MMC_SIGNAL_VOLTAGE_330 : return 3300 ;
case MMC_SIGNAL_VOLTAGE_180 : return 1800 ;
case MMC_SIGNAL_VOLTAGE_120 : return 1200 ;
}
return - EINVAL ;
}
static int mmc_set_signal_voltage ( struct mmc * mmc , uint signal_voltage )
{
int err ;
if ( mmc - > signal_voltage = = signal_voltage )
return 0 ;
mmc - > signal_voltage = signal_voltage ;
return mmc_set_ios ( mmc ) ;
err = mmc_set_ios ( mmc ) ;
if ( err )
debug ( " unable to set voltage (err %d) \n " , err ) ;
return err ;
}
static const struct mode_width_tuning sd_modes_by_pref [ ] = {
@ -1584,6 +1605,43 @@ static int mmc_read_and_compare_ext_csd(struct mmc *mmc)
return - EBADMSG ;
}
static int mmc_set_lowest_voltage ( struct mmc * mmc , enum bus_mode mode ,
uint32_t allowed_mask )
{
u32 card_mask = 0 ;
switch ( mode ) {
case MMC_HS_200 :
if ( mmc - > cardtype & EXT_CSD_CARD_TYPE_HS200_1_8V )
card_mask | = MMC_SIGNAL_VOLTAGE_180 ;
if ( mmc - > cardtype & EXT_CSD_CARD_TYPE_HS200_1_2V )
card_mask | = MMC_SIGNAL_VOLTAGE_120 ;
break ;
case MMC_DDR_52 :
if ( mmc - > cardtype & EXT_CSD_CARD_TYPE_DDR_1_8V )
card_mask | = MMC_SIGNAL_VOLTAGE_330 |
MMC_SIGNAL_VOLTAGE_180 ;
if ( mmc - > cardtype & EXT_CSD_CARD_TYPE_DDR_1_2V )
card_mask | = MMC_SIGNAL_VOLTAGE_120 ;
break ;
default :
card_mask | = MMC_SIGNAL_VOLTAGE_330 ;
break ;
}
while ( card_mask & allowed_mask ) {
enum mmc_voltage best_match ;
best_match = 1 < < ( ffs ( card_mask & allowed_mask ) - 1 ) ;
if ( ! mmc_set_signal_voltage ( mmc , best_match ) )
return 0 ;
allowed_mask & = ~ best_match ;
}
return - ENOTSUPP ;
}
static const struct mode_width_tuning mmc_modes_by_pref [ ] = {
{
. mode = MMC_HS_200 ,
@ -1655,10 +1713,17 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps)
for_each_mmc_mode_by_pref ( card_caps , mwt ) {
for_each_supported_width ( card_caps & mwt - > widths ,
mmc_is_mode_ddr ( mwt - > mode ) , ecbw ) {
enum mmc_voltage old_voltage ;
debug ( " trying mode %s width %d (at %d MHz) \n " ,
mmc_mode_name ( mwt - > mode ) ,
bus_width ( ecbw - > cap ) ,
mmc_mode2freq ( mmc , mwt - > mode ) / 1000000 ) ;
old_voltage = mmc - > signal_voltage ;
err = mmc_set_lowest_voltage ( mmc , mwt - > mode ,
MMC_ALL_SIGNAL_VOLTAGE ) ;
if ( err )
continue ;
/* configure the bus width (card + host) */
err = mmc_switch ( mmc , EXT_CSD_CMD_SET_NORMAL ,
EXT_CSD_BUS_WIDTH ,
@ -1702,6 +1767,7 @@ static int mmc_select_mode_and_width(struct mmc *mmc, uint card_caps)
if ( ! err )
return 0 ;
error :
mmc_set_signal_voltage ( mmc , old_voltage ) ;
/* if an error occured, revert to a safer bus mode */
mmc_switch ( mmc , EXT_CSD_CMD_SET_NORMAL ,
EXT_CSD_BUS_WIDTH , EXT_CSD_BUS_WIDTH_1 ) ;