2007-05-21 22:23:20 +04:00
/*
* linux / drivers / mmc / sdio . c
*
* Copyright 2006 - 2007 Pierre Ossman
*
* 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 , or ( at
* your option ) any later version .
*/
# include <linux/err.h>
2010-10-02 15:54:10 +04:00
# include <linux/pm_runtime.h>
2007-05-21 22:23:20 +04:00
# include <linux/mmc/host.h>
# include <linux/mmc/card.h>
2007-06-11 22:25:43 +04:00
# include <linux/mmc/sdio.h>
2007-05-26 15:48:18 +04:00
# include <linux/mmc/sdio_func.h>
2011-04-05 18:50:14 +04:00
# include <linux/mmc/sdio_ids.h>
2007-05-21 22:23:20 +04:00
# include "core.h"
# include "bus.h"
2010-08-11 05:01:40 +04:00
# include "sd.h"
2007-05-26 15:48:18 +04:00
# include "sdio_bus.h"
2007-05-21 22:23:20 +04:00
# include "mmc_ops.h"
# include "sd_ops.h"
# include "sdio_ops.h"
2007-06-16 10:04:16 +04:00
# include "sdio_cis.h"
2007-05-21 22:23:20 +04:00
2007-06-11 23:01:00 +04:00
static int sdio_read_fbr ( struct sdio_func * func )
{
int ret ;
unsigned char data ;
2011-04-05 18:50:14 +04:00
if ( mmc_card_nonstd_func_interface ( func - > card ) ) {
func - > class = SDIO_CLASS_NONE ;
return 0 ;
}
2007-06-11 23:01:00 +04:00
ret = mmc_io_rw_direct ( func - > card , 0 , 0 ,
2007-08-08 17:23:05 +04:00
SDIO_FBR_BASE ( func - > num ) + SDIO_FBR_STD_IF , 0 , & data ) ;
2007-06-11 23:01:00 +04:00
if ( ret )
goto out ;
data & = 0x0f ;
if ( data = = 0x0f ) {
ret = mmc_io_rw_direct ( func - > card , 0 , 0 ,
2007-08-08 17:23:05 +04:00
SDIO_FBR_BASE ( func - > num ) + SDIO_FBR_STD_IF_EXT , 0 , & data ) ;
2007-06-11 23:01:00 +04:00
if ( ret )
goto out ;
}
func - > class = data ;
out :
return ret ;
}
2007-05-26 15:48:18 +04:00
static int sdio_init_func ( struct mmc_card * card , unsigned int fn )
{
2007-06-11 23:01:00 +04:00
int ret ;
2007-05-26 15:48:18 +04:00
struct sdio_func * func ;
BUG_ON ( fn > SDIO_MAX_FUNCS ) ;
func = sdio_alloc_func ( card ) ;
if ( IS_ERR ( func ) )
return PTR_ERR ( func ) ;
func - > num = fn ;
2010-08-11 05:01:50 +04:00
if ( ! ( card - > quirks & MMC_QUIRK_NONSTD_SDIO ) ) {
ret = sdio_read_fbr ( func ) ;
if ( ret )
goto fail ;
2007-06-11 23:01:00 +04:00
2010-08-11 05:01:50 +04:00
ret = sdio_read_func_cis ( func ) ;
if ( ret )
goto fail ;
} else {
func - > vendor = func - > card - > cis . vendor ;
func - > device = func - > card - > cis . device ;
func - > max_blksize = func - > card - > cis . blksize ;
}
2007-06-16 10:04:16 +04:00
2007-05-26 15:48:18 +04:00
card - > sdio_func [ fn - 1 ] = func ;
return 0 ;
2007-06-11 23:01:00 +04:00
fail :
/*
* It is okay to remove the function here even though we hold
* the host lock as we haven ' t registered the device yet .
*/
sdio_remove_func ( func ) ;
return ret ;
2007-05-26 15:48:18 +04:00
}
2007-06-11 22:25:43 +04:00
static int sdio_read_cccr ( struct mmc_card * card )
{
int ret ;
int cccr_vsn ;
unsigned char data ;
memset ( & card - > cccr , 0 , sizeof ( struct sdio_cccr ) ) ;
ret = mmc_io_rw_direct ( card , 0 , 0 , SDIO_CCCR_CCCR , 0 , & data ) ;
if ( ret )
goto out ;
cccr_vsn = data & 0x0f ;
if ( cccr_vsn > SDIO_CCCR_REV_1_20 ) {
printk ( KERN_ERR " %s: unrecognised CCCR structure version %d \n " ,
mmc_hostname ( card - > host ) , cccr_vsn ) ;
return - EINVAL ;
}
card - > cccr . sdio_vsn = ( data & 0xf0 ) > > 4 ;
ret = mmc_io_rw_direct ( card , 0 , 0 , SDIO_CCCR_CAPS , 0 , & data ) ;
if ( ret )
goto out ;
if ( data & SDIO_CCCR_CAP_SMB )
card - > cccr . multi_block = 1 ;
if ( data & SDIO_CCCR_CAP_LSC )
card - > cccr . low_speed = 1 ;
if ( data & SDIO_CCCR_CAP_4BLS )
card - > cccr . wide_bus = 1 ;
if ( cccr_vsn > = SDIO_CCCR_REV_1_10 ) {
ret = mmc_io_rw_direct ( card , 0 , 0 , SDIO_CCCR_POWER , 0 , & data ) ;
if ( ret )
goto out ;
if ( data & SDIO_POWER_SMPC )
card - > cccr . high_power = 1 ;
}
if ( cccr_vsn > = SDIO_CCCR_REV_1_20 ) {
ret = mmc_io_rw_direct ( card , 0 , 0 , SDIO_CCCR_SPEED , 0 , & data ) ;
if ( ret )
goto out ;
if ( data & SDIO_SPEED_SHS )
card - > cccr . high_speed = 1 ;
}
out :
return ret ;
}
2007-07-30 20:23:53 +04:00
static int sdio_enable_wide ( struct mmc_card * card )
{
int ret ;
u8 ctrl ;
if ( ! ( card - > host - > caps & MMC_CAP_4_BIT_DATA ) )
return 0 ;
if ( card - > cccr . low_speed & & ! card - > cccr . wide_bus )
return 0 ;
ret = mmc_io_rw_direct ( card , 0 , 0 , SDIO_CCCR_IF , 0 , & ctrl ) ;
if ( ret )
return ret ;
ctrl | = SDIO_BUS_WIDTH_4BIT ;
ret = mmc_io_rw_direct ( card , 1 , 0 , SDIO_CCCR_IF , ctrl , NULL ) ;
if ( ret )
return ret ;
2010-08-11 05:01:40 +04:00
return 1 ;
2007-07-30 20:23:53 +04:00
}
2009-09-23 03:45:07 +04:00
/*
* If desired , disconnect the pull - up resistor on CD / DAT [ 3 ] ( pin 1 )
* of the card . This may be required on certain setups of boards ,
* controllers and embedded sdio device which do not need the card ' s
* pull - up . As a result , card detection is disabled and power is saved .
*/
static int sdio_disable_cd ( struct mmc_card * card )
{
int ret ;
u8 ctrl ;
2011-04-05 19:02:25 +04:00
if ( ! mmc_card_disable_cd ( card ) )
2009-09-23 03:45:07 +04:00
return 0 ;
ret = mmc_io_rw_direct ( card , 0 , 0 , SDIO_CCCR_IF , 0 , & ctrl ) ;
if ( ret )
return ret ;
ctrl | = SDIO_BUS_CD_DISABLE ;
return mmc_io_rw_direct ( card , 1 , 0 , SDIO_CCCR_IF , ctrl , NULL ) ;
}
2010-03-06 00:43:34 +03:00
/*
* Devices that remain active during a system suspend are
* put back into 1 - bit mode .
*/
static int sdio_disable_wide ( struct mmc_card * card )
{
int ret ;
u8 ctrl ;
if ( ! ( card - > host - > caps & MMC_CAP_4_BIT_DATA ) )
return 0 ;
if ( card - > cccr . low_speed & & ! card - > cccr . wide_bus )
return 0 ;
ret = mmc_io_rw_direct ( card , 0 , 0 , SDIO_CCCR_IF , 0 , & ctrl ) ;
if ( ret )
return ret ;
if ( ! ( ctrl & SDIO_BUS_WIDTH_4BIT ) )
return 0 ;
ctrl & = ~ SDIO_BUS_WIDTH_4BIT ;
ctrl | = SDIO_BUS_ASYNC_INT ;
ret = mmc_io_rw_direct ( card , 1 , 0 , SDIO_CCCR_IF , ctrl , NULL ) ;
if ( ret )
return ret ;
mmc_set_bus_width ( card - > host , MMC_BUS_WIDTH_1 ) ;
return 0 ;
}
2010-08-11 05:01:40 +04:00
static int sdio_enable_4bit_bus ( struct mmc_card * card )
{
int err ;
if ( card - > type = = MMC_TYPE_SDIO )
return sdio_enable_wide ( card ) ;
if ( ( card - > host - > caps & MMC_CAP_4_BIT_DATA ) & &
( card - > scr . bus_widths & SD_SCR_BUS_WIDTH_4 ) ) {
err = mmc_app_set_bus_width ( card , MMC_BUS_WIDTH_4 ) ;
if ( err )
return err ;
} else
return 0 ;
err = sdio_enable_wide ( card ) ;
if ( err < = 0 )
mmc_app_set_bus_width ( card , MMC_BUS_WIDTH_1 ) ;
return err ;
}
2008-08-31 19:22:46 +04:00
/*
* Test if the card supports high - speed mode and , if so , switch to it .
*/
2010-08-11 05:01:40 +04:00
static int mmc_sdio_switch_hs ( struct mmc_card * card , int enable )
2008-08-31 19:22:46 +04:00
{
int ret ;
u8 speed ;
if ( ! ( card - > host - > caps & MMC_CAP_SD_HIGHSPEED ) )
return 0 ;
if ( ! card - > cccr . high_speed )
return 0 ;
ret = mmc_io_rw_direct ( card , 0 , 0 , SDIO_CCCR_SPEED , 0 , & speed ) ;
if ( ret )
return ret ;
2010-08-11 05:01:40 +04:00
if ( enable )
speed | = SDIO_SPEED_EHS ;
else
speed & = ~ SDIO_SPEED_EHS ;
2008-08-31 19:22:46 +04:00
ret = mmc_io_rw_direct ( card , 1 , 0 , SDIO_CCCR_SPEED , speed , NULL ) ;
if ( ret )
return ret ;
2010-08-11 05:01:40 +04:00
return 1 ;
}
2008-08-31 19:22:46 +04:00
2010-08-11 05:01:40 +04:00
/*
* Enable SDIO / combo card ' s high - speed mode . Return 0 / 1 if [ not ] supported .
*/
static int sdio_enable_hs ( struct mmc_card * card )
{
int ret ;
ret = mmc_sdio_switch_hs ( card , true ) ;
if ( ret < = 0 | | card - > type = = MMC_TYPE_SDIO )
return ret ;
ret = mmc_sd_switch_hs ( card ) ;
if ( ret < = 0 )
mmc_sdio_switch_hs ( card , false ) ;
return ret ;
}
2010-08-11 05:01:40 +04:00
static unsigned mmc_sdio_get_max_clock ( struct mmc_card * card )
{
unsigned max_dtr ;
if ( mmc_card_highspeed ( card ) ) {
/*
* The SDIO specification doesn ' t mention how
* the CIS transfer speed register relates to
* high - speed , but it seems that 50 MHz is
* mandatory .
*/
max_dtr = 50000000 ;
} else {
max_dtr = card - > cis . max_dtr ;
}
2010-08-11 05:01:40 +04:00
if ( card - > type = = MMC_TYPE_SD_COMBO )
max_dtr = min ( max_dtr , mmc_sd_get_max_clock ( card ) ) ;
2010-08-11 05:01:40 +04:00
return max_dtr ;
2008-08-31 19:22:46 +04:00
}
2009-09-23 03:45:28 +04:00
/*
* Handle the detection and initialisation of a card .
*
* In the case of a resume , " oldcard " will contain the card
* we ' re trying to reinitialise .
*/
static int mmc_sdio_init_card ( struct mmc_host * host , u32 ocr ,
2010-03-06 00:43:33 +03:00
struct mmc_card * oldcard , int powered_resume )
2009-09-23 03:45:28 +04:00
{
struct mmc_card * card ;
int err ;
BUG_ON ( ! host ) ;
WARN_ON ( ! host - > claimed ) ;
/*
* Inform the card of the voltage
*/
2010-03-06 00:43:33 +03:00
if ( ! powered_resume ) {
err = mmc_send_io_op_cond ( host , host - > ocr , & ocr ) ;
if ( err )
goto err ;
}
2009-09-23 03:45:28 +04:00
/*
* For SPI , enable CRC as appropriate .
*/
if ( mmc_host_is_spi ( host ) ) {
err = mmc_spi_set_crc ( host , use_spi_crc ) ;
if ( err )
goto err ;
}
/*
* Allocate card structure .
*/
card = mmc_alloc_card ( host , NULL ) ;
if ( IS_ERR ( card ) ) {
err = PTR_ERR ( card ) ;
goto err ;
}
2011-05-05 10:48:59 +04:00
if ( ( ocr & R4_MEMORY_PRESENT ) & &
mmc_sd_get_cid ( host , host - > ocr & ocr , card - > raw_cid , NULL ) = = 0 ) {
2010-08-11 05:01:40 +04:00
card - > type = MMC_TYPE_SD_COMBO ;
if ( oldcard & & ( oldcard - > type ! = MMC_TYPE_SD_COMBO | |
memcmp ( card - > raw_cid , oldcard - > raw_cid , sizeof ( card - > raw_cid ) ) ! = 0 ) ) {
mmc_remove_card ( card ) ;
return - ENOENT ;
}
} else {
card - > type = MMC_TYPE_SDIO ;
if ( oldcard & & oldcard - > type ! = MMC_TYPE_SDIO ) {
mmc_remove_card ( card ) ;
return - ENOENT ;
}
}
2009-09-23 03:45:28 +04:00
2010-04-01 12:03:25 +04:00
/*
* Call the optional HC ' s init_card function to handle quirks .
*/
if ( host - > ops - > init_card )
host - > ops - > init_card ( host , card ) ;
2009-09-23 03:45:28 +04:00
/*
* For native busses : set card RCA and quit open drain mode .
*/
2010-03-06 00:43:33 +03:00
if ( ! powered_resume & & ! mmc_host_is_spi ( host ) ) {
2009-09-23 03:45:28 +04:00
err = mmc_send_relative_addr ( host , & card - > rca ) ;
if ( err )
goto remove ;
2011-03-01 16:41:04 +03:00
/*
* Update oldcard with the new RCA received from the SDIO
* device - - we ' re doing this so that it ' s updated in the
* " card " struct when oldcard overwrites that later .
*/
if ( oldcard )
oldcard - > rca = card - > rca ;
2009-09-23 03:45:28 +04:00
mmc_set_bus_mode ( host , MMC_BUSMODE_PUSHPULL ) ;
}
2010-08-11 05:01:40 +04:00
/*
* Read CSD , before selecting the card
*/
if ( ! oldcard & & card - > type = = MMC_TYPE_SD_COMBO ) {
err = mmc_sd_get_csd ( host , card ) ;
if ( err )
return err ;
mmc_decode_cid ( card ) ;
}
2009-09-23 03:45:28 +04:00
/*
* Select card , as all following commands rely on that .
*/
2010-03-06 00:43:33 +03:00
if ( ! powered_resume & & ! mmc_host_is_spi ( host ) ) {
2009-09-23 03:45:28 +04:00
err = mmc_select_card ( card ) ;
if ( err )
goto remove ;
}
2010-08-11 05:01:50 +04:00
if ( card - > quirks & MMC_QUIRK_NONSTD_SDIO ) {
/*
* This is non - standard SDIO device , meaning it doesn ' t
* have any CIA ( Common I / O area ) registers present .
* It ' s host ' s responsibility to fill cccr and cis
* structures in init_card ( ) .
*/
mmc_set_clock ( host , card - > cis . max_dtr ) ;
if ( card - > cccr . high_speed ) {
mmc_card_set_highspeed ( card ) ;
mmc_set_timing ( card - > host , MMC_TIMING_SD_HS ) ;
}
goto finish ;
}
2009-09-23 03:45:28 +04:00
/*
* Read the common registers .
*/
err = sdio_read_cccr ( card ) ;
if ( err )
goto remove ;
/*
* Read the common CIS tuples .
*/
err = sdio_read_common_cis ( card ) ;
if ( err )
goto remove ;
if ( oldcard ) {
int same = ( card - > cis . vendor = = oldcard - > cis . vendor & &
card - > cis . device = = oldcard - > cis . device ) ;
mmc_remove_card ( card ) ;
2010-08-11 05:01:40 +04:00
if ( ! same )
return - ENOENT ;
2009-09-23 03:45:28 +04:00
card = oldcard ;
}
2011-04-12 02:02:15 +04:00
mmc_fixup_device ( card , NULL ) ;
2009-09-23 03:45:28 +04:00
2010-08-11 05:01:40 +04:00
if ( card - > type = = MMC_TYPE_SD_COMBO ) {
err = mmc_sd_setup_card ( host , card , oldcard ! = NULL ) ;
/* handle as SDIO-only card if memory init failed */
if ( err ) {
mmc_go_idle ( host ) ;
if ( mmc_host_is_spi ( host ) )
/* should not fail, as it worked previously */
mmc_spi_set_crc ( host , use_spi_crc ) ;
card - > type = MMC_TYPE_SDIO ;
} else
card - > dev . type = & sd_type ;
}
/*
* If needed , disconnect card detection pull - up resistor .
*/
err = sdio_disable_cd ( card ) ;
if ( err )
goto remove ;
2009-09-23 03:45:28 +04:00
/*
* Switch to high - speed ( if supported ) .
*/
err = sdio_enable_hs ( card ) ;
2010-08-11 05:01:40 +04:00
if ( err > 0 )
mmc_sd_go_highspeed ( card ) ;
else if ( err )
2009-09-23 03:45:28 +04:00
goto remove ;
/*
* Change to the card ' s maximum speed .
*/
2010-08-11 05:01:40 +04:00
mmc_set_clock ( host , mmc_sdio_get_max_clock ( card ) ) ;
2009-09-23 03:45:28 +04:00
/*
* Switch to wider bus ( if supported ) .
*/
2010-08-11 05:01:40 +04:00
err = sdio_enable_4bit_bus ( card ) ;
if ( err > 0 )
mmc_set_bus_width ( card - > host , MMC_BUS_WIDTH_4 ) ;
else if ( err )
2009-09-23 03:45:28 +04:00
goto remove ;
2010-08-11 05:01:50 +04:00
finish :
2009-09-23 03:45:28 +04:00
if ( ! oldcard )
host - > card = card ;
return 0 ;
remove :
if ( ! oldcard )
mmc_remove_card ( card ) ;
err :
return err ;
}
2007-05-21 22:23:20 +04:00
/*
* Host is being removed . Free up the current card .
*/
static void mmc_sdio_remove ( struct mmc_host * host )
{
2007-05-26 15:48:18 +04:00
int i ;
2007-05-21 22:23:20 +04:00
BUG_ON ( ! host ) ;
BUG_ON ( ! host - > card ) ;
2007-05-26 15:48:18 +04:00
for ( i = 0 ; i < host - > card - > sdio_funcs ; i + + ) {
if ( host - > card - > sdio_func [ i ] ) {
sdio_remove_func ( host - > card - > sdio_func [ i ] ) ;
host - > card - > sdio_func [ i ] = NULL ;
}
}
2007-05-21 22:23:20 +04:00
mmc_remove_card ( host - > card ) ;
host - > card = NULL ;
}
/*
* Card detection callback from host .
*/
static void mmc_sdio_detect ( struct mmc_host * host )
{
int err ;
BUG_ON ( ! host ) ;
BUG_ON ( ! host - > card ) ;
2010-10-02 15:54:12 +04:00
/* Make sure card is powered before detecting it */
2010-11-19 10:29:09 +03:00
if ( host - > caps & MMC_CAP_POWER_OFF_CARD ) {
err = pm_runtime_get_sync ( & host - > card - > dev ) ;
if ( err < 0 )
goto out ;
}
2010-10-02 15:54:12 +04:00
2007-05-21 22:23:20 +04:00
mmc_claim_host ( host ) ;
/*
* Just check if our card has been removed .
*/
err = mmc_select_card ( host - > card ) ;
mmc_release_host ( host ) ;
2010-11-14 13:40:33 +03:00
/*
* Tell PM core it ' s OK to power off the card now .
*
* The _sync variant is used in order to ensure that the card
* is left powered off in case an error occurred , and the card
* is going to be removed .
*
* Since there is no specific reason to believe a new user
* is about to show up at this point , the _sync variant is
* desirable anyway .
*/
2010-11-19 10:29:09 +03:00
if ( host - > caps & MMC_CAP_POWER_OFF_CARD )
pm_runtime_put_sync ( & host - > card - > dev ) ;
2010-11-14 13:40:33 +03:00
2010-10-02 15:54:12 +04:00
out :
2007-05-21 22:23:20 +04:00
if ( err ) {
mmc_sdio_remove ( host ) ;
mmc_claim_host ( host ) ;
mmc_detach_bus ( host ) ;
mmc_release_host ( host ) ;
}
}
2009-09-23 03:45:28 +04:00
/*
* SDIO suspend . We need to suspend all functions separately .
* Therefore all registered functions must have drivers with suspend
* and resume methods . Failing that we simply remove the whole card .
*/
2009-09-23 03:45:29 +04:00
static int mmc_sdio_suspend ( struct mmc_host * host )
2009-09-23 03:45:28 +04:00
{
2009-09-23 03:45:29 +04:00
int i , err = 0 ;
2009-09-23 03:45:28 +04:00
for ( i = 0 ; i < host - > card - > sdio_funcs ; i + + ) {
struct sdio_func * func = host - > card - > sdio_func [ i ] ;
if ( func & & sdio_func_present ( func ) & & func - > dev . driver ) {
const struct dev_pm_ops * pmops = func - > dev . driver - > pm ;
if ( ! pmops | | ! pmops - > suspend | | ! pmops - > resume ) {
2009-09-23 03:45:29 +04:00
/* force removal of entire card in that case */
err = - ENOSYS ;
} else
err = pmops - > suspend ( & func - > dev ) ;
if ( err )
break ;
2009-09-23 03:45:28 +04:00
}
}
2009-09-23 03:45:29 +04:00
while ( err & & - - i > = 0 ) {
2009-09-23 03:45:28 +04:00
struct sdio_func * func = host - > card - > sdio_func [ i ] ;
if ( func & & sdio_func_present ( func ) & & func - > dev . driver ) {
const struct dev_pm_ops * pmops = func - > dev . driver - > pm ;
2009-09-23 03:45:29 +04:00
pmops - > resume ( & func - > dev ) ;
2009-09-23 03:45:28 +04:00
}
}
2009-09-23 03:45:29 +04:00
2011-04-05 18:43:21 +04:00
if ( ! err & & mmc_card_keep_power ( host ) & & mmc_card_wake_sdio_irq ( host ) ) {
2010-03-06 00:43:34 +03:00
mmc_claim_host ( host ) ;
sdio_disable_wide ( host - > card ) ;
mmc_release_host ( host ) ;
}
2009-09-23 03:45:29 +04:00
return err ;
2009-09-23 03:45:28 +04:00
}
2009-09-23 03:45:29 +04:00
static int mmc_sdio_resume ( struct mmc_host * host )
2009-09-23 03:45:28 +04:00
{
2010-11-28 08:21:29 +03:00
int i , err = 0 ;
2009-09-23 03:45:28 +04:00
BUG_ON ( ! host ) ;
BUG_ON ( ! host - > card ) ;
2009-09-23 03:45:29 +04:00
/* Basic card reinitialization. */
2009-09-23 03:45:28 +04:00
mmc_claim_host ( host ) ;
2010-11-28 08:21:29 +03:00
/* No need to reinitialize powered-resumed nonremovable cards */
2011-04-05 18:43:20 +04:00
if ( mmc_card_is_removable ( host ) | | ! mmc_card_keep_power ( host ) )
2010-11-28 08:21:29 +03:00
err = mmc_sdio_init_card ( host , host - > ocr , host - > card ,
2011-04-05 18:43:20 +04:00
mmc_card_keep_power ( host ) ) ;
2011-04-05 18:43:21 +04:00
else if ( mmc_card_keep_power ( host ) & & mmc_card_wake_sdio_irq ( host ) ) {
2010-11-28 08:21:29 +03:00
/* We may have switched to 1-bit mode during suspend */
err = sdio_enable_4bit_bus ( host - > card ) ;
if ( err > 0 ) {
mmc_set_bus_width ( host , MMC_BUS_WIDTH_4 ) ;
err = 0 ;
}
}
2010-03-06 00:43:34 +03:00
if ( ! err & & host - > sdio_irqs )
mmc_signal_sdio_irq ( host ) ;
2009-09-23 03:45:28 +04:00
mmc_release_host ( host ) ;
2009-09-23 03:45:29 +04:00
/*
* If the card looked to be the same as before suspending , then
* we proceed to resume all card functions . If one of them returns
* an error then we simply return that error to the core and the
* card will be redetected as new . It is the responsibility of
* the function driver to perform further tests with the extra
* knowledge it has of the card to confirm the card is indeed the
* same as before suspending ( same MAC address for network cards ,
* etc . ) and return an error otherwise .
*/
for ( i = 0 ; ! err & & i < host - > card - > sdio_funcs ; i + + ) {
2009-09-23 03:45:28 +04:00
struct sdio_func * func = host - > card - > sdio_func [ i ] ;
if ( func & & sdio_func_present ( func ) & & func - > dev . driver ) {
const struct dev_pm_ops * pmops = func - > dev . driver - > pm ;
2009-09-23 03:45:29 +04:00
err = pmops - > resume ( & func - > dev ) ;
2009-09-23 03:45:28 +04:00
}
}
2009-09-23 03:45:29 +04:00
return err ;
2009-09-23 03:45:28 +04:00
}
2007-05-21 22:23:20 +04:00
2010-10-02 15:54:07 +04:00
static int mmc_sdio_power_restore ( struct mmc_host * host )
{
int ret ;
BUG_ON ( ! host ) ;
BUG_ON ( ! host - > card ) ;
mmc_claim_host ( host ) ;
ret = mmc_sdio_init_card ( host , host - > ocr , host - > card ,
2011-04-05 18:43:20 +04:00
mmc_card_keep_power ( host ) ) ;
2010-10-02 15:54:07 +04:00
if ( ! ret & & host - > sdio_irqs )
mmc_signal_sdio_irq ( host ) ;
mmc_release_host ( host ) ;
return ret ;
}
2007-05-21 22:23:20 +04:00
static const struct mmc_bus_ops mmc_sdio_ops = {
. remove = mmc_sdio_remove ,
. detect = mmc_sdio_detect ,
2009-09-23 03:45:28 +04:00
. suspend = mmc_sdio_suspend ,
. resume = mmc_sdio_resume ,
2010-10-02 15:54:07 +04:00
. power_restore = mmc_sdio_power_restore ,
2007-05-21 22:23:20 +04:00
} ;
/*
* Starting point for SDIO card init .
*/
2011-01-03 21:36:56 +03:00
int mmc_attach_sdio ( struct mmc_host * host )
2007-05-21 22:23:20 +04:00
{
2011-01-03 21:36:56 +03:00
int err , i , funcs ;
u32 ocr ;
2007-05-21 22:23:20 +04:00
struct mmc_card * card ;
BUG_ON ( ! host ) ;
2007-08-09 15:23:56 +04:00
WARN_ON ( ! host - > claimed ) ;
2007-05-21 22:23:20 +04:00
2011-01-03 21:36:56 +03:00
err = mmc_send_io_op_cond ( host , 0 , & ocr ) ;
if ( err )
return err ;
2007-05-21 22:23:20 +04:00
mmc_attach_bus ( host , & mmc_sdio_ops ) ;
2010-12-08 12:04:30 +03:00
if ( host - > ocr_avail_sdio )
host - > ocr_avail = host - > ocr_avail_sdio ;
2007-05-21 22:23:20 +04:00
/*
* Sanity check the voltages that the card claims to
* support .
*/
if ( ocr & 0x7F ) {
printk ( KERN_WARNING " %s: card claims to support voltages "
" below the defined range. These will be ignored. \n " ,
mmc_hostname ( host ) ) ;
ocr & = ~ 0x7F ;
}
host - > ocr = mmc_select_voltage ( host , ocr ) ;
/*
* Can we support the voltage ( s ) of the card ( s ) ?
*/
if ( ! host - > ocr ) {
err = - EINVAL ;
goto err ;
}
/*
2009-09-23 03:45:28 +04:00
* Detect and init the card .
2007-05-21 22:23:20 +04:00
*/
2010-03-06 00:43:33 +03:00
err = mmc_sdio_init_card ( host , host - > ocr , NULL , 0 ) ;
2007-05-21 22:23:20 +04:00
if ( err )
goto err ;
2009-09-23 03:45:28 +04:00
card = host - > card ;
MMC core learns about SPI
Teach the MMC/SD/SDIO core about using SPI mode.
- Use mmc_host_is_spi() so enumeration works through SPI signaling
and protocols, not just the native versions.
- Provide the SPI response type flags with each request issued,
including requests from the new lock/unlock code.
- Understand that cmd->resp[0] and mmc_get_status() results for SPI
return different values than for "native" MMC/SD protocol; this
affects resetting, checking card lock status, and some others.
- Understand that some commands act a bit differently ... notably:
* OP_COND command doesn't return the OCR
* APP_CMD status doesn't have an R1_APP_CMD analogue
Those changes required some new and updated primitives:
- Provide utilities to access two SPI-only requests, and one
request that wasn't previously needed:
* mmc_spi_read_ocr() ... SPI only
* mmc_spi_set_crc() ... SPI only (override by module parm)
* mmc_send_cid() ... for use without broadcast mode
- Updated internal routines:
* Previous mmc_send_csd() modified into mmc_send_cxd_native();
it uses native "R2" responses, which include 16 bytes of data.
* Previous mmc_send_ext_csd() becomes new mmc_send_cxd_data()
helper for command-and-data access
* Bugfix to that mmc_send_cxd_data() code: dma-to-stack is
unsafe/nonportable, so kmalloc a bounce buffer instead.
- Modified mmc_send_ext_csd() now uses mmc_send_cxd_data() helper
- Modified mmc_send_csd(), and new mmc_spi_send_cid(), routines use
those helper routines based on whether they're native or SPI
The newest categories of cards supported by the MMC stack aren't expected
to work yet with SPI: MMC or SD cards with over 4GB data, and SDIO.
All those cards support SPI mode, so eventually they should work too.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Pierre Ossman <drzeus@drzeus.cx>
2007-08-08 20:11:32 +04:00
2010-10-02 15:54:10 +04:00
/*
2010-11-19 10:29:09 +03:00
* Enable runtime PM only if supported by host + card + board
2010-10-02 15:54:10 +04:00
*/
2010-11-19 10:29:09 +03:00
if ( host - > caps & MMC_CAP_POWER_OFF_CARD ) {
/*
* Let runtime PM core know our card is active
*/
err = pm_runtime_set_active ( & card - > dev ) ;
if ( err )
goto remove ;
2010-10-02 15:54:10 +04:00
2010-11-19 10:29:09 +03:00
/*
* Enable runtime PM for this card
*/
pm_runtime_enable ( & card - > dev ) ;
}
2010-10-02 15:54:10 +04:00
2007-05-21 22:23:20 +04:00
/*
* The number of functions on the card is encoded inside
* the ocr .
*/
2009-12-18 02:27:18 +03:00
funcs = ( ocr & 0x70000000 ) > > 28 ;
card - > sdio_funcs = 0 ;
2007-07-30 20:23:53 +04:00
2007-05-26 15:48:18 +04:00
/*
* Initialize ( but don ' t add ) all present functions .
*/
2009-12-18 02:27:18 +03:00
for ( i = 0 ; i < funcs ; i + + , card - > sdio_funcs + + ) {
2007-05-26 15:48:18 +04:00
err = sdio_init_func ( host - > card , i + 1 ) ;
if ( err )
goto remove ;
2010-10-02 15:54:11 +04:00
/*
2010-11-19 10:29:09 +03:00
* Enable Runtime PM for this func ( if supported )
2010-10-02 15:54:11 +04:00
*/
2010-11-19 10:29:09 +03:00
if ( host - > caps & MMC_CAP_POWER_OFF_CARD )
pm_runtime_enable ( & card - > sdio_func [ i ] - > dev ) ;
2007-05-26 15:48:18 +04:00
}
2007-05-21 22:23:20 +04:00
2007-05-26 15:48:18 +04:00
/*
* First add the card to the driver model . . .
*/
2011-01-03 21:36:56 +03:00
mmc_release_host ( host ) ;
2007-05-21 22:23:20 +04:00
err = mmc_add_card ( host - > card ) ;
if ( err )
2007-05-26 15:48:18 +04:00
goto remove_added ;
/*
* . . . then the SDIO functions .
*/
for ( i = 0 ; i < funcs ; i + + ) {
err = sdio_add_func ( host - > card - > sdio_func [ i ] ) ;
if ( err )
goto remove_added ;
}
2007-05-21 22:23:20 +04:00
2011-03-04 01:40:10 +03:00
mmc_claim_host ( host ) ;
2007-05-21 22:23:20 +04:00
return 0 ;
2007-05-26 15:48:18 +04:00
remove_added :
/* Remove without lock if the device has been added. */
mmc_sdio_remove ( host ) ;
2007-05-21 22:23:20 +04:00
mmc_claim_host ( host ) ;
2007-05-26 15:48:18 +04:00
remove :
/* And with lock if it hasn't been added. */
2011-01-03 21:36:56 +03:00
mmc_release_host ( host ) ;
2007-05-26 15:48:18 +04:00
if ( host - > card )
mmc_sdio_remove ( host ) ;
2011-01-03 21:36:56 +03:00
mmc_claim_host ( host ) ;
2007-05-21 22:23:20 +04:00
err :
mmc_detach_bus ( host ) ;
printk ( KERN_ERR " %s: error %d whilst initialising SDIO card \n " ,
mmc_hostname ( host ) , err ) ;
return err ;
}