2007-05-26 13:48:18 +02:00
/*
* linux / drivers / mmc / core / sdio_bus . c
*
* Copyright 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 .
*
* SDIO function driver model
*/
# include <linux/device.h>
# include <linux/err.h>
# include <linux/mmc/card.h>
# include <linux/mmc/sdio_func.h>
2007-06-16 02:06:47 -04:00
# include "sdio_cis.h"
2007-05-26 13:48:18 +02:00
# include "sdio_bus.h"
# define dev_to_sdio_func(d) container_of(d, struct sdio_func, dev)
2007-06-16 15:54:55 +02:00
# define to_sdio_driver(d) container_of(d, struct sdio_driver, drv)
2007-06-17 11:42:21 +02:00
/* show configuration fields */
# define sdio_config_attr(field, format_string) \
static ssize_t \
field # # _show ( struct device * dev , struct device_attribute * attr , char * buf ) \
{ \
struct sdio_func * func ; \
\
func = dev_to_sdio_func ( dev ) ; \
return sprintf ( buf , format_string , func - > field ) ; \
}
sdio_config_attr ( class , " 0x%02x \n " ) ;
sdio_config_attr ( vendor , " 0x%04x \n " ) ;
sdio_config_attr ( device , " 0x%04x \n " ) ;
static ssize_t modalias_show ( struct device * dev , struct device_attribute * attr , char * buf )
{
struct sdio_func * func = dev_to_sdio_func ( dev ) ;
return sprintf ( buf , " sdio:c%02Xv%04Xd%04X \n " ,
func - > class , func - > vendor , func - > device ) ;
}
2007-07-29 16:58:09 +02:00
static struct device_attribute sdio_dev_attrs [ ] = {
2007-06-17 11:42:21 +02:00
__ATTR_RO ( class ) ,
__ATTR_RO ( vendor ) ,
__ATTR_RO ( device ) ,
__ATTR_RO ( modalias ) ,
__ATTR_NULL ,
} ;
2007-06-16 15:54:55 +02:00
static const struct sdio_device_id * sdio_match_one ( struct sdio_func * func ,
const struct sdio_device_id * id )
{
if ( id - > class ! = ( __u8 ) SDIO_ANY_ID & & id - > class ! = func - > class )
return NULL ;
if ( id - > vendor ! = ( __u16 ) SDIO_ANY_ID & & id - > vendor ! = func - > vendor )
return NULL ;
if ( id - > device ! = ( __u16 ) SDIO_ANY_ID & & id - > device ! = func - > device )
return NULL ;
return id ;
}
static const struct sdio_device_id * sdio_match_device ( struct sdio_func * func ,
struct sdio_driver * sdrv )
{
const struct sdio_device_id * ids ;
ids = sdrv - > id_table ;
if ( ids ) {
while ( ids - > class | | ids - > vendor | | ids - > device ) {
if ( sdio_match_one ( func , ids ) )
return ids ;
ids + + ;
}
}
return NULL ;
}
2007-05-26 13:48:18 +02:00
static int sdio_bus_match ( struct device * dev , struct device_driver * drv )
{
2007-06-16 15:54:55 +02:00
struct sdio_func * func = dev_to_sdio_func ( dev ) ;
struct sdio_driver * sdrv = to_sdio_driver ( drv ) ;
if ( sdio_match_device ( func , sdrv ) )
return 1 ;
return 0 ;
2007-05-26 13:48:18 +02:00
}
static int
2007-10-14 05:46:09 +01:00
sdio_bus_uevent ( struct device * dev , struct kobj_uevent_env * env )
2007-05-26 13:48:18 +02:00
{
2007-06-17 11:34:23 +02:00
struct sdio_func * func = dev_to_sdio_func ( dev ) ;
2007-10-14 05:46:09 +01:00
if ( add_uevent_var ( env ,
2007-06-17 11:34:23 +02:00
" SDIO_CLASS=%02X " , func - > class ) )
return - ENOMEM ;
2007-10-14 05:46:09 +01:00
if ( add_uevent_var ( env ,
2007-06-17 11:34:23 +02:00
" SDIO_ID=%04X:%04X " , func - > vendor , func - > device ) )
return - ENOMEM ;
2007-10-14 05:46:09 +01:00
if ( add_uevent_var ( env ,
2007-06-17 11:34:23 +02:00
" MODALIAS=sdio:c%02Xv%04Xd%04X " ,
func - > class , func - > vendor , func - > device ) )
return - ENOMEM ;
2007-05-26 13:48:18 +02:00
return 0 ;
}
static int sdio_bus_probe ( struct device * dev )
{
2007-06-16 15:54:55 +02:00
struct sdio_driver * drv = to_sdio_driver ( dev - > driver ) ;
struct sdio_func * func = dev_to_sdio_func ( dev ) ;
const struct sdio_device_id * id ;
2007-08-08 14:23:48 +01:00
int ret ;
2007-06-16 15:54:55 +02:00
id = sdio_match_device ( func , drv ) ;
if ( ! id )
return - ENODEV ;
2007-08-08 14:23:48 +01:00
/* Set the default block size so the driver is sure it's something
* sensible . */
sdio_claim_host ( func ) ;
ret = sdio_set_block_size ( func , 0 ) ;
sdio_release_host ( func ) ;
if ( ret )
return ret ;
2007-06-16 15:54:55 +02:00
return drv - > probe ( func , id ) ;
2007-05-26 13:48:18 +02:00
}
static int sdio_bus_remove ( struct device * dev )
{
2007-06-16 15:54:55 +02:00
struct sdio_driver * drv = to_sdio_driver ( dev - > driver ) ;
struct sdio_func * func = dev_to_sdio_func ( dev ) ;
drv - > remove ( func ) ;
2007-06-30 16:29:41 +02:00
if ( func - > irq_handler ) {
printk ( KERN_WARNING " WARNING: driver %s did not remove "
" its interrupt handler! \n " , drv - > name ) ;
sdio_claim_host ( func ) ;
sdio_release_irq ( func ) ;
sdio_release_host ( func ) ;
}
2007-05-26 13:48:18 +02:00
return 0 ;
}
static struct bus_type sdio_bus_type = {
. name = " sdio " ,
2007-06-17 11:42:21 +02:00
. dev_attrs = sdio_dev_attrs ,
2007-05-26 13:48:18 +02:00
. match = sdio_bus_match ,
. uevent = sdio_bus_uevent ,
. probe = sdio_bus_probe ,
. remove = sdio_bus_remove ,
} ;
int sdio_register_bus ( void )
{
return bus_register ( & sdio_bus_type ) ;
}
void sdio_unregister_bus ( void )
{
bus_unregister ( & sdio_bus_type ) ;
}
2007-05-27 12:00:02 +02:00
/**
* sdio_register_driver - register a function driver
* @ drv : SDIO function driver
*/
int sdio_register_driver ( struct sdio_driver * drv )
{
drv - > drv . name = drv - > name ;
drv - > drv . bus = & sdio_bus_type ;
return driver_register ( & drv - > drv ) ;
}
EXPORT_SYMBOL_GPL ( sdio_register_driver ) ;
/**
* sdio_unregister_driver - unregister a function driver
* @ drv : SDIO function driver
*/
void sdio_unregister_driver ( struct sdio_driver * drv )
{
drv - > drv . bus = & sdio_bus_type ;
driver_unregister ( & drv - > drv ) ;
}
EXPORT_SYMBOL_GPL ( sdio_unregister_driver ) ;
2007-05-26 13:48:18 +02:00
static void sdio_release_func ( struct device * dev )
{
struct sdio_func * func = dev_to_sdio_func ( dev ) ;
2007-06-16 02:06:47 -04:00
2007-07-30 15:15:30 +02:00
sdio_free_func_cis ( func ) ;
2007-05-26 13:48:18 +02:00
2007-09-19 18:42:16 +02:00
if ( func - > info )
kfree ( func - > info ) ;
2007-05-26 13:48:18 +02:00
kfree ( func ) ;
}
/*
* Allocate and initialise a new SDIO function structure .
*/
struct sdio_func * sdio_alloc_func ( struct mmc_card * card )
{
struct sdio_func * func ;
2007-08-01 00:05:24 +02:00
func = kzalloc ( sizeof ( struct sdio_func ) , GFP_KERNEL ) ;
2007-05-26 13:48:18 +02:00
if ( ! func )
return ERR_PTR ( - ENOMEM ) ;
func - > card = card ;
device_initialize ( & func - > dev ) ;
func - > dev . parent = & card - > dev ;
func - > dev . bus = & sdio_bus_type ;
func - > dev . release = sdio_release_func ;
return func ;
}
/*
* Register a new SDIO function with the driver model .
*/
int sdio_add_func ( struct sdio_func * func )
{
int ret ;
snprintf ( func - > dev . bus_id , sizeof ( func - > dev . bus_id ) ,
" %s:%d " , mmc_card_id ( func - > card ) , func - > num ) ;
ret = device_add ( & func - > dev ) ;
if ( ret = = 0 )
sdio_func_set_present ( func ) ;
return ret ;
}
/*
* Unregister a SDIO function with the driver model , and
* ( eventually ) free it .
*/
void sdio_remove_func ( struct sdio_func * func )
{
if ( sdio_func_present ( func ) )
device_del ( & func - > dev ) ;
put_device ( & func - > dev ) ;
}