2005-04-17 02:20:36 +04:00
/*
* linux / drivers / mmc / mmc_sysfs . c
*
* Copyright ( C ) 2003 Russell King , All Rights Reserved .
*
* 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 sysfs / driver model support .
*/
# include <linux/module.h>
# include <linux/init.h>
# include <linux/device.h>
2005-08-19 12:42:52 +04:00
# include <linux/idr.h>
2005-04-17 02:20:36 +04:00
# include <linux/mmc/card.h>
# include <linux/mmc/host.h>
# include "mmc.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)
2005-08-19 12:41:24 +04:00
# define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev)
2005-04-17 02:20:36 +04:00
# define MMC_ATTR(name, fmt, args...) \
2005-05-17 14:42:58 +04:00
static ssize_t mmc_ # # name # # _show ( struct device * dev , struct device_attribute * attr , char * buf ) \
2005-04-17 02:20:36 +04:00
{ \
struct mmc_card * card = dev_to_mmc_card ( dev ) ; \
return sprintf ( buf , fmt , args ) ; \
}
MMC_ATTR ( cid , " %08x%08x%08x%08x \n " , card - > raw_cid [ 0 ] , card - > raw_cid [ 1 ] ,
card - > raw_cid [ 2 ] , card - > raw_cid [ 3 ] ) ;
MMC_ATTR ( csd , " %08x%08x%08x%08x \n " , card - > raw_csd [ 0 ] , card - > raw_csd [ 1 ] ,
card - > raw_csd [ 2 ] , card - > raw_csd [ 3 ] ) ;
2005-09-07 02:18:54 +04:00
MMC_ATTR ( scr , " %08x%08x \n " , card - > raw_scr [ 0 ] , card - > raw_scr [ 1 ] ) ;
2005-04-17 02:20:36 +04:00
MMC_ATTR ( date , " %02d/%04d \n " , card - > cid . month , card - > cid . year ) ;
MMC_ATTR ( fwrev , " 0x%x \n " , card - > cid . fwrev ) ;
MMC_ATTR ( hwrev , " 0x%x \n " , card - > cid . hwrev ) ;
MMC_ATTR ( manfid , " 0x%06x \n " , card - > cid . manfid ) ;
MMC_ATTR ( name , " %s \n " , card - > cid . prod_name ) ;
MMC_ATTR ( oemid , " 0x%04x \n " , card - > cid . oemid ) ;
MMC_ATTR ( serial , " 0x%08x \n " , card - > cid . serial ) ;
# define MMC_ATTR_RO(name) __ATTR(name, S_IRUGO, mmc_##name##_show, NULL)
static struct device_attribute mmc_dev_attrs [ ] = {
MMC_ATTR_RO ( cid ) ,
MMC_ATTR_RO ( csd ) ,
MMC_ATTR_RO ( date ) ,
MMC_ATTR_RO ( fwrev ) ,
MMC_ATTR_RO ( hwrev ) ,
MMC_ATTR_RO ( manfid ) ,
MMC_ATTR_RO ( name ) ,
MMC_ATTR_RO ( oemid ) ,
MMC_ATTR_RO ( serial ) ,
__ATTR_NULL
} ;
2005-09-07 02:18:58 +04:00
static struct device_attribute mmc_dev_attr_scr = MMC_ATTR_RO ( scr ) ;
2005-04-17 02:20:36 +04:00
static void mmc_release_card ( struct device * dev )
{
struct mmc_card * card = dev_to_mmc_card ( dev ) ;
kfree ( card ) ;
}
/*
* This currently matches any MMC driver to any MMC card - drivers
* themselves make the decision whether to drive this card in their
* probe method . However , we force " bad " cards to fail .
*/
static int mmc_bus_match ( struct device * dev , struct device_driver * drv )
{
struct mmc_card * card = dev_to_mmc_card ( dev ) ;
return ! mmc_card_bad ( card ) ;
}
static int
2005-11-16 11:00:00 +03:00
mmc_bus_uevent ( struct device * dev , char * * envp , int num_envp , char * buf ,
2005-04-17 02:20:36 +04:00
int buf_size )
{
struct mmc_card * card = dev_to_mmc_card ( dev ) ;
char ccc [ 13 ] ;
int i = 0 ;
# define add_env(fmt,val) \
( { \
int len , ret = - ENOMEM ; \
if ( i < num_envp ) { \
envp [ i + + ] = buf ; \
len = snprintf ( buf , buf_size , fmt , val ) + 1 ; \
buf_size - = len ; \
buf + = len ; \
if ( buf_size > = 0 ) \
ret = 0 ; \
} \
ret ; \
} )
for ( i = 0 ; i < 12 ; i + + )
ccc [ i ] = card - > csd . cmdclass & ( 1 < < i ) ? ' 1 ' : ' 0 ' ;
ccc [ 12 ] = ' \0 ' ;
i = 0 ;
add_env ( " MMC_CCC=%s " , ccc ) ;
add_env ( " MMC_MANFID=%06x " , card - > cid . manfid ) ;
add_env ( " MMC_NAME=%s " , mmc_card_name ( card ) ) ;
add_env ( " MMC_OEMID=%04x " , card - > cid . oemid ) ;
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 ;
}
2006-01-05 17:40:27 +03:00
static int mmc_bus_probe ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
struct mmc_driver * drv = to_mmc_driver ( dev - > driver ) ;
struct mmc_card * card = dev_to_mmc_card ( dev ) ;
return drv - > probe ( card ) ;
}
2006-01-05 17:40:27 +03:00
static int mmc_bus_remove ( struct device * dev )
2005-04-17 02:20:36 +04:00
{
struct mmc_driver * drv = to_mmc_driver ( dev - > driver ) ;
struct mmc_card * card = dev_to_mmc_card ( dev ) ;
drv - > remove ( card ) ;
return 0 ;
}
2006-01-05 17:40:27 +03:00
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 ,
} ;
2005-04-17 02:20:36 +04:00
/**
* 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 ) ;
/*
* Internal function . Initialise a MMC card structure .
*/
void mmc_init_card ( struct mmc_card * card , struct mmc_host * host )
{
memset ( card , 0 , sizeof ( struct mmc_card ) ) ;
card - > host = host ;
device_initialize ( & card - > dev ) ;
card - > dev . parent = card - > host - > dev ;
card - > dev . bus = & mmc_bus_type ;
card - > dev . release = mmc_release_card ;
}
/*
* Internal function . Register a new MMC card with the driver model .
*/
int mmc_register_card ( struct mmc_card * card )
{
2005-09-07 02:18:58 +04:00
int ret ;
2005-04-17 02:20:36 +04:00
snprintf ( card - > dev . bus_id , sizeof ( card - > dev . bus_id ) ,
2005-08-19 12:40:08 +04:00
" %s:%04x " , mmc_hostname ( card - > host ) , card - > rca ) ;
2005-04-17 02:20:36 +04:00
2005-09-07 02:18:58 +04:00
ret = device_add ( & card - > dev ) ;
if ( ret = = 0 ) {
if ( mmc_card_sd ( card ) ) {
ret = device_create_file ( & card - > dev , & mmc_dev_attr_scr ) ;
if ( ret )
device_del ( & card - > dev ) ;
}
}
return ret ;
2005-04-17 02:20:36 +04:00
}
/*
* Internal function . Unregister a new MMC card with the
* driver model , and ( eventually ) free it .
*/
void mmc_remove_card ( struct mmc_card * card )
{
2005-09-07 02:18:58 +04:00
if ( mmc_card_present ( card ) ) {
if ( mmc_card_sd ( card ) )
device_remove_file ( & card - > dev , & mmc_dev_attr_scr ) ;
2005-04-17 02:20:36 +04:00
device_del ( & card - > dev ) ;
2005-09-07 02:18:58 +04:00
}
2005-04-17 02:20:36 +04:00
put_device ( & card - > dev ) ;
}
2005-08-19 12:41:24 +04:00
static void mmc_host_classdev_release ( struct class_device * dev )
{
struct mmc_host * host = cls_dev_to_mmc_host ( dev ) ;
kfree ( host ) ;
}
static struct class mmc_host_class = {
. name = " mmc_host " ,
. release = mmc_host_classdev_release ,
} ;
2005-08-19 12:42:52 +04:00
static DEFINE_IDR ( mmc_host_idr ) ;
static DEFINE_SPINLOCK ( mmc_host_lock ) ;
2005-08-19 12:41:24 +04:00
/*
* Internal function . Allocate a new MMC host .
*/
struct mmc_host * mmc_alloc_host_sysfs ( int extra , struct device * dev )
{
struct mmc_host * host ;
host = kmalloc ( sizeof ( struct mmc_host ) + extra , GFP_KERNEL ) ;
if ( host ) {
memset ( host , 0 , sizeof ( struct mmc_host ) + extra ) ;
host - > dev = dev ;
host - > class_dev . dev = host - > dev ;
host - > class_dev . class = & mmc_host_class ;
class_device_initialize ( & host - > class_dev ) ;
}
return host ;
}
/*
* Internal function . Register a new MMC host with the MMC class .
*/
int mmc_add_host_sysfs ( struct mmc_host * host )
{
2005-08-19 12:42:52 +04:00
int err ;
if ( ! idr_pre_get ( & mmc_host_idr , GFP_KERNEL ) )
return - ENOMEM ;
spin_lock ( & mmc_host_lock ) ;
err = idr_get_new ( & mmc_host_idr , host , & host - > index ) ;
spin_unlock ( & mmc_host_lock ) ;
if ( err )
return err ;
2005-08-19 12:41:24 +04:00
2005-08-19 12:42:21 +04:00
snprintf ( host - > class_dev . class_id , BUS_ID_SIZE ,
2005-08-19 12:42:52 +04:00
" mmc%d " , host - > index ) ;
2005-08-19 12:41:24 +04:00
return class_device_add ( & host - > class_dev ) ;
}
/*
* Internal function . Unregister a MMC host with the MMC class .
*/
void mmc_remove_host_sysfs ( struct mmc_host * host )
{
class_device_del ( & host - > class_dev ) ;
2005-08-19 12:42:52 +04:00
spin_lock ( & mmc_host_lock ) ;
idr_remove ( & mmc_host_idr , host - > index ) ;
spin_unlock ( & mmc_host_lock ) ;
2005-08-19 12:41:24 +04:00
}
/*
* Internal function . Free a MMC host .
*/
void mmc_free_host_sysfs ( struct mmc_host * host )
{
class_device_put ( & host - > class_dev ) ;
}
2005-04-17 02:20:36 +04:00
static int __init mmc_init ( void )
{
2005-08-19 12:41:24 +04:00
int ret = bus_register ( & mmc_bus_type ) ;
if ( ret = = 0 ) {
ret = class_register ( & mmc_host_class ) ;
if ( ret )
bus_unregister ( & mmc_bus_type ) ;
}
return ret ;
2005-04-17 02:20:36 +04:00
}
static void __exit mmc_exit ( void )
{
2005-08-19 12:41:24 +04:00
class_unregister ( & mmc_host_class ) ;
2005-04-17 02:20:36 +04:00
bus_unregister ( & mmc_bus_type ) ;
}
module_init ( mmc_init ) ;
module_exit ( mmc_exit ) ;