2007-05-19 14:06:24 +02:00
/*
* linux / drivers / mmc / core / host . 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 host class device management
*/
# include <linux/device.h>
# include <linux/err.h>
# include <linux/idr.h>
# include <linux/pagemap.h>
2007-09-24 07:15:48 +02:00
# include <linux/leds.h>
2007-05-19 14:06:24 +02:00
# include <linux/mmc/host.h>
# include "core.h"
# include "host.h"
# define cls_dev_to_mmc_host(d) container_of(d, struct mmc_host, class_dev)
static void mmc_host_classdev_release ( struct device * dev )
{
struct mmc_host * host = cls_dev_to_mmc_host ( dev ) ;
kfree ( host ) ;
}
static struct class mmc_host_class = {
. name = " mmc_host " ,
. dev_release = mmc_host_classdev_release ,
} ;
int mmc_register_host_class ( void )
{
return class_register ( & mmc_host_class ) ;
}
void mmc_unregister_host_class ( void )
{
class_unregister ( & mmc_host_class ) ;
}
static DEFINE_IDR ( mmc_host_idr ) ;
static DEFINE_SPINLOCK ( mmc_host_lock ) ;
/**
* mmc_alloc_host - initialise the per - host structure .
* @ extra : sizeof private data structure
* @ dev : pointer to host device model structure
*
* Initialise the per - host structure .
*/
struct mmc_host * mmc_alloc_host ( int extra , struct device * dev )
{
struct mmc_host * host ;
2007-08-10 14:00:50 -07:00
host = kzalloc ( sizeof ( struct mmc_host ) + extra , GFP_KERNEL ) ;
2007-05-19 14:06:24 +02:00
if ( ! host )
return NULL ;
host - > parent = dev ;
host - > class_dev . parent = dev ;
host - > class_dev . class = & mmc_host_class ;
device_initialize ( & host - > class_dev ) ;
spin_lock_init ( & host - > lock ) ;
init_waitqueue_head ( & host - > wq ) ;
INIT_DELAYED_WORK ( & host - > detect , mmc_rescan ) ;
/*
* By default , hosts do not support SGIO or large requests .
* They have to set these according to their abilities .
*/
host - > max_hw_segs = 1 ;
host - > max_phys_segs = 1 ;
host - > max_seg_size = PAGE_CACHE_SIZE ;
host - > max_req_size = PAGE_CACHE_SIZE ;
host - > max_blk_size = 512 ;
host - > max_blk_count = PAGE_CACHE_SIZE / 512 ;
return host ;
}
EXPORT_SYMBOL ( mmc_alloc_host ) ;
/**
* mmc_add_host - initialise host hardware
* @ host : mmc host
2007-07-11 20:22:11 +02:00
*
* Register the host with the driver model . The host must be
* prepared to start servicing requests before this function
* completes .
2007-05-19 14:06:24 +02:00
*/
int mmc_add_host ( struct mmc_host * host )
{
int err ;
2007-07-24 02:09:39 -04:00
WARN_ON ( ( host - > caps & MMC_CAP_SDIO_IRQ ) & &
! host - > ops - > enable_sdio_irq ) ;
2007-05-19 14:06:24 +02:00
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 ;
snprintf ( host - > class_dev . bus_id , BUS_ID_SIZE ,
" mmc%d " , host - > index ) ;
2007-09-24 07:15:48 +02:00
led_trigger_register_simple ( host - > class_dev . bus_id , & host - > led ) ;
2007-05-19 14:06:24 +02:00
err = device_add ( & host - > class_dev ) ;
if ( err )
return err ;
mmc_start_host ( host ) ;
return 0 ;
}
EXPORT_SYMBOL ( mmc_add_host ) ;
/**
* mmc_remove_host - remove host hardware
* @ host : mmc host
*
* Unregister and remove all cards associated with this host ,
2007-07-11 20:22:11 +02:00
* and power down the MMC bus . No new requests will be issued
* after this function has returned .
2007-05-19 14:06:24 +02:00
*/
void mmc_remove_host ( struct mmc_host * host )
{
mmc_stop_host ( host ) ;
device_del ( & host - > class_dev ) ;
2007-10-12 22:48:46 +02:00
led_trigger_unregister_simple ( host - > led ) ;
2007-09-24 07:15:48 +02:00
2007-05-19 14:06:24 +02:00
spin_lock ( & mmc_host_lock ) ;
idr_remove ( & mmc_host_idr , host - > index ) ;
spin_unlock ( & mmc_host_lock ) ;
}
EXPORT_SYMBOL ( mmc_remove_host ) ;
/**
* mmc_free_host - free the host structure
* @ host : mmc host
*
* Free the host once all references to it have been dropped .
*/
void mmc_free_host ( struct mmc_host * host )
{
put_device ( & host - > class_dev ) ;
}
EXPORT_SYMBOL ( mmc_free_host ) ;