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>
# 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>
2007-05-21 22:23:20 +04:00
# include "core.h"
# include "bus.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 ;
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 ;
2007-06-11 23:01:00 +04:00
ret = sdio_read_fbr ( func ) ;
if ( ret )
goto fail ;
2007-07-30 17:15:30 +04:00
ret = sdio_read_func_cis ( func ) ;
2007-06-16 10:04:16 +04:00
if ( ret )
goto fail ;
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 ;
mmc_set_bus_width ( card - > host , MMC_BUS_WIDTH_4 ) ;
return 0 ;
}
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 ;
if ( ! card - > cccr . disable_cd )
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 ) ;
}
2008-08-31 19:22:46 +04:00
/*
* Test if the card supports high - speed mode and , if so , switch to it .
*/
static int sdio_enable_hs ( struct mmc_card * card )
{
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 ;
speed | = SDIO_SPEED_EHS ;
ret = mmc_io_rw_direct ( card , 1 , 0 , SDIO_CCCR_SPEED , speed , NULL ) ;
if ( ret )
return ret ;
mmc_card_set_highspeed ( card ) ;
mmc_set_timing ( card - > host , MMC_TIMING_SD_HS ) ;
return 0 ;
}
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 ,
struct mmc_card * oldcard )
{
struct mmc_card * card ;
int err ;
BUG_ON ( ! host ) ;
WARN_ON ( ! host - > claimed ) ;
/*
* Inform the card of the voltage
*/
err = mmc_send_io_op_cond ( host , host - > ocr , & ocr ) ;
if ( err )
goto err ;
/*
* 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 ;
}
card - > type = MMC_TYPE_SDIO ;
/*
* For native busses : set card RCA and quit open drain mode .
*/
if ( ! mmc_host_is_spi ( host ) ) {
err = mmc_send_relative_addr ( host , & card - > rca ) ;
if ( err )
goto remove ;
mmc_set_bus_mode ( host , MMC_BUSMODE_PUSHPULL ) ;
}
/*
* Select card , as all following commands rely on that .
*/
if ( ! mmc_host_is_spi ( host ) ) {
err = mmc_select_card ( card ) ;
if ( err )
goto remove ;
}
/*
* 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 ) ;
if ( ! same ) {
err = - ENOENT ;
goto err ;
}
card = oldcard ;
2009-09-23 03:45:29 +04:00
return 0 ;
2009-09-23 03:45:28 +04:00
}
/*
* Switch to high - speed ( if supported ) .
*/
err = sdio_enable_hs ( card ) ;
if ( err )
goto remove ;
/*
* Change to the card ' s maximum speed .
*/
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 .
*/
mmc_set_clock ( host , 50000000 ) ;
} else {
mmc_set_clock ( host , card - > cis . max_dtr ) ;
}
/*
* Switch to wider bus ( if supported ) .
*/
err = sdio_enable_wide ( card ) ;
if ( err )
goto remove ;
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 ) ;
mmc_claim_host ( host ) ;
/*
* Just check if our card has been removed .
*/
err = mmc_select_card ( host - > card ) ;
mmc_release_host ( host ) ;
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
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
{
int i , err ;
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 ) ;
err = mmc_sdio_init_card ( host , host - > ocr , host - > card ) ;
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
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 ,
2007-05-21 22:23:20 +04:00
} ;
/*
* Starting point for SDIO card init .
*/
int mmc_attach_sdio ( struct mmc_host * host , u32 ocr )
{
int err ;
2007-05-26 15:48:18 +04:00
int i , funcs ;
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
mmc_attach_bus ( host , & mmc_sdio_ops ) ;
/*
* 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
*/
2009-09-23 03:45:28 +04:00
err = mmc_sdio_init_card ( host , host - > ocr , NULL ) ;
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
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
2009-09-23 03:45:07 +04:00
/*
* If needed , disconnect card detection pull - up resistor .
*/
err = sdio_disable_cd ( card ) ;
if ( err )
goto remove ;
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 ;
}
2007-05-21 22:23:20 +04:00
mmc_release_host ( host ) ;
2007-05-26 15:48:18 +04:00
/*
* First add the card to the driver model . . .
*/
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
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. */
if ( host - > card )
mmc_sdio_remove ( host ) ;
2007-05-21 22:23:20 +04:00
err :
mmc_detach_bus ( host ) ;
mmc_release_host ( host ) ;
printk ( KERN_ERR " %s: error %d whilst initialising SDIO card \n " ,
mmc_hostname ( host ) , err ) ;
return err ;
}