2007-05-19 15:39:01 +04:00
/*
* linux / drivers / mmc / core / bus . c
*
* Copyright ( C ) 2003 Russell King , All Rights Reserved .
* Copyright ( C ) 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 version 2 as
* published by the Free Software Foundation .
*
* MMC card bus driver model
*/
# include <linux/device.h>
# include <linux/err.h>
# include <linux/mmc/card.h>
# include <linux/mmc/host.h>
# include "core.h"
2007-07-30 17:15:30 +04:00
# include "sdio_cis.h"
2007-05-19 15:39:01 +04:00
# include "bus.h"
# define dev_to_mmc_card(d) container_of(d, struct mmc_card, dev)
# define to_mmc_driver(d) container_of(d, struct mmc_driver, drv)
static ssize_t mmc_type_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
{
struct mmc_card * card = dev_to_mmc_card ( dev ) ;
switch ( card - > type ) {
case MMC_TYPE_MMC :
return sprintf ( buf , " MMC \n " ) ;
case MMC_TYPE_SD :
return sprintf ( buf , " SD \n " ) ;
2007-05-21 22:23:20 +04:00
case MMC_TYPE_SDIO :
return sprintf ( buf , " SDIO \n " ) ;
2007-05-19 15:39:01 +04:00
default :
return - EFAULT ;
}
}
static struct device_attribute mmc_dev_attrs [ ] = {
2008-03-22 01:54:50 +03:00
__ATTR ( type , S_IRUGO , mmc_type_show , NULL ) ,
2007-05-19 15:39:01 +04:00
__ATTR_NULL ,
} ;
/*
* This currently matches any MMC driver to any MMC card - drivers
* themselves make the decision whether to drive this card in their
* probe method .
*/
static int mmc_bus_match ( struct device * dev , struct device_driver * drv )
{
return 1 ;
}
static int
2007-08-14 17:15:12 +04:00
mmc_bus_uevent ( struct device * dev , struct kobj_uevent_env * env )
2007-05-19 15:39:01 +04:00
{
struct mmc_card * card = dev_to_mmc_card ( dev ) ;
2007-06-17 13:18:46 +04:00
const char * type ;
2007-08-14 17:15:12 +04:00
int retval = 0 ;
2007-05-19 15:39:01 +04:00
switch ( card - > type ) {
case MMC_TYPE_MMC :
2007-06-17 13:18:46 +04:00
type = " MMC " ;
2007-05-19 15:39:01 +04:00
break ;
case MMC_TYPE_SD :
2007-06-17 13:18:46 +04:00
type = " SD " ;
2007-05-19 15:39:01 +04:00
break ;
2007-05-21 22:23:20 +04:00
case MMC_TYPE_SDIO :
2007-06-17 13:18:46 +04:00
type = " SDIO " ;
2007-05-21 22:23:20 +04:00
break ;
2007-06-17 13:18:46 +04:00
default :
type = NULL ;
2007-05-19 15:39:01 +04:00
}
2007-06-17 13:18:46 +04:00
if ( type ) {
2007-08-14 17:15:12 +04:00
retval = add_uevent_var ( env , " MMC_TYPE=%s " , type ) ;
if ( retval )
return retval ;
2007-06-17 13:18:46 +04:00
}
2007-05-19 15:39:01 +04:00
2007-08-14 17:15:12 +04:00
retval = add_uevent_var ( env , " MMC_NAME=%s " , mmc_card_name ( card ) ) ;
2007-05-19 15:39:01 +04:00
2007-08-14 17:15:12 +04:00
return retval ;
2007-05-19 15:39:01 +04:00
}
static int mmc_bus_probe ( struct device * dev )
{
struct mmc_driver * drv = to_mmc_driver ( dev - > driver ) ;
struct mmc_card * card = dev_to_mmc_card ( dev ) ;
return drv - > probe ( card ) ;
}
static int mmc_bus_remove ( struct device * dev )
{
struct mmc_driver * drv = to_mmc_driver ( dev - > driver ) ;
struct mmc_card * card = dev_to_mmc_card ( dev ) ;
drv - > remove ( card ) ;
return 0 ;
}
static int mmc_bus_suspend ( struct device * dev , pm_message_t state )
{
struct mmc_driver * drv = to_mmc_driver ( dev - > driver ) ;
struct mmc_card * card = dev_to_mmc_card ( dev ) ;
int ret = 0 ;
if ( dev - > driver & & drv - > suspend )
ret = drv - > suspend ( card , state ) ;
return ret ;
}
static int mmc_bus_resume ( struct device * dev )
{
struct mmc_driver * drv = to_mmc_driver ( dev - > driver ) ;
struct mmc_card * card = dev_to_mmc_card ( dev ) ;
int ret = 0 ;
if ( dev - > driver & & drv - > resume )
ret = drv - > resume ( card ) ;
return ret ;
}
static struct bus_type mmc_bus_type = {
. name = " mmc " ,
. dev_attrs = mmc_dev_attrs ,
. match = mmc_bus_match ,
. uevent = mmc_bus_uevent ,
. probe = mmc_bus_probe ,
. remove = mmc_bus_remove ,
. suspend = mmc_bus_suspend ,
. resume = mmc_bus_resume ,
} ;
int mmc_register_bus ( void )
{
return bus_register ( & mmc_bus_type ) ;
}
void mmc_unregister_bus ( void )
{
bus_unregister ( & mmc_bus_type ) ;
}
/**
* mmc_register_driver - register a media driver
* @ drv : MMC media driver
*/
int mmc_register_driver ( struct mmc_driver * drv )
{
drv - > drv . bus = & mmc_bus_type ;
return driver_register ( & drv - > drv ) ;
}
EXPORT_SYMBOL ( mmc_register_driver ) ;
/**
* mmc_unregister_driver - unregister a media driver
* @ drv : MMC media driver
*/
void mmc_unregister_driver ( struct mmc_driver * drv )
{
drv - > drv . bus = & mmc_bus_type ;
driver_unregister ( & drv - > drv ) ;
}
EXPORT_SYMBOL ( mmc_unregister_driver ) ;
static void mmc_release_card ( struct device * dev )
{
struct mmc_card * card = dev_to_mmc_card ( dev ) ;
2007-07-30 17:15:30 +04:00
sdio_free_common_cis ( card ) ;
2007-09-19 20:42:16 +04:00
if ( card - > info )
kfree ( card - > info ) ;
2007-05-19 15:39:01 +04:00
kfree ( card ) ;
}
/*
* Allocate and initialise a new MMC card structure .
*/
2008-03-22 01:54:50 +03:00
struct mmc_card * mmc_alloc_card ( struct mmc_host * host , struct device_type * type )
2007-05-19 15:39:01 +04:00
{
struct mmc_card * card ;
2007-08-11 01:00:47 +04:00
card = kzalloc ( sizeof ( struct mmc_card ) , GFP_KERNEL ) ;
2007-05-19 15:39:01 +04:00
if ( ! card )
return ERR_PTR ( - ENOMEM ) ;
card - > host = host ;
device_initialize ( & card - > dev ) ;
card - > dev . parent = mmc_classdev ( host ) ;
card - > dev . bus = & mmc_bus_type ;
card - > dev . release = mmc_release_card ;
2008-03-22 01:54:50 +03:00
card - > dev . type = type ;
2007-05-19 15:39:01 +04:00
return card ;
}
/*
* Register a new MMC card with the driver model .
*/
int mmc_add_card ( struct mmc_card * card )
{
int ret ;
2007-07-23 02:12:10 +04:00
const char * type ;
2007-05-19 15:39:01 +04:00
snprintf ( card - > dev . bus_id , sizeof ( card - > dev . bus_id ) ,
" %s:%04x " , mmc_hostname ( card - > host ) , card - > rca ) ;
2007-07-23 02:12:10 +04:00
switch ( card - > type ) {
case MMC_TYPE_MMC :
type = " MMC " ;
break ;
case MMC_TYPE_SD :
type = " SD " ;
if ( mmc_card_blockaddr ( card ) )
type = " SDHC " ;
break ;
2007-05-21 22:23:20 +04:00
case MMC_TYPE_SDIO :
type = " SDIO " ;
break ;
2007-07-23 02:12:10 +04:00
default :
type = " ? " ;
break ;
}
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
if ( mmc_host_is_spi ( card - > host ) ) {
printk ( KERN_INFO " %s: new %s%s card on SPI \n " ,
mmc_hostname ( card - > host ) ,
mmc_card_highspeed ( card ) ? " high speed " : " " ,
type ) ;
} else {
printk ( KERN_INFO " %s: new %s%s card at address %04x \n " ,
mmc_hostname ( card - > host ) ,
mmc_card_highspeed ( card ) ? " high speed " : " " ,
type , card - > rca ) ;
}
2007-07-23 02:12:10 +04:00
2007-05-19 15:39:01 +04:00
ret = device_add ( & card - > dev ) ;
if ( ret )
return ret ;
2008-07-24 16:18:58 +04:00
# ifdef CONFIG_DEBUG_FS
mmc_add_card_debugfs ( card ) ;
# endif
2007-05-19 15:39:01 +04:00
mmc_card_set_present ( card ) ;
return 0 ;
}
/*
* Unregister a new MMC card with the driver model , and
* ( eventually ) free it .
*/
void mmc_remove_card ( struct mmc_card * card )
{
2008-07-24 16:18:58 +04:00
# ifdef CONFIG_DEBUG_FS
mmc_remove_card_debugfs ( card ) ;
# endif
2007-05-19 15:39:01 +04:00
if ( mmc_card_present ( 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
if ( mmc_host_is_spi ( card - > host ) ) {
printk ( KERN_INFO " %s: SPI card removed \n " ,
mmc_hostname ( card - > host ) ) ;
} else {
printk ( KERN_INFO " %s: card %04x removed \n " ,
mmc_hostname ( card - > host ) , card - > rca ) ;
}
2007-05-19 15:39:01 +04:00
device_del ( & card - > dev ) ;
}
put_device ( & card - > dev ) ;
}