2006-10-04 13:15:37 +04:00
/*
* tifm_core . c - TI FlashMedia driver
*
* Copyright ( C ) 2006 Alex Dubov < oakad @ yahoo . com >
*
* 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 .
*
*/
# include <linux/tifm.h>
# include <linux/init.h>
# include <linux/idr.h>
# define DRIVER_NAME "tifm_core"
2007-04-12 10:59:12 +04:00
# define DRIVER_VERSION "0.8"
2006-10-04 13:15:37 +04:00
2007-04-12 10:59:15 +04:00
static struct workqueue_struct * workqueue ;
2006-10-04 13:15:37 +04:00
static DEFINE_IDR ( tifm_adapter_idr ) ;
static DEFINE_SPINLOCK ( tifm_adapter_lock ) ;
2007-04-12 10:59:14 +04:00
static const char * tifm_media_type_name ( unsigned char type , unsigned char nt )
2006-10-04 13:15:37 +04:00
{
2007-04-12 10:59:14 +04:00
const char * card_type_name [ 3 ] [ 3 ] = {
{ " SmartMedia/xD " , " MemoryStick " , " MMC/SD " } ,
{ " XD " , " MS " , " SD " } ,
{ " xd " , " ms " , " sd " }
} ;
if ( nt > 2 | | type < 1 | | type > 3 )
return NULL ;
return card_type_name [ nt ] [ type - 1 ] ;
2006-10-04 13:15:37 +04:00
}
2007-04-12 10:59:14 +04:00
static int tifm_dev_match ( struct tifm_dev * sock , struct tifm_device_id * id )
2006-10-04 13:15:37 +04:00
{
2007-04-12 10:59:14 +04:00
if ( sock - > type = = id - > type )
2006-10-04 13:15:37 +04:00
return 1 ;
2007-04-12 10:59:14 +04:00
return 0 ;
}
static int tifm_bus_match ( struct device * dev , struct device_driver * drv )
{
struct tifm_dev * sock = container_of ( dev , struct tifm_dev , dev ) ;
struct tifm_driver * fm_drv = container_of ( drv , struct tifm_driver ,
driver ) ;
struct tifm_device_id * ids = fm_drv - > id_table ;
if ( ids ) {
while ( ids - > type ) {
if ( tifm_dev_match ( sock , ids ) )
return 1 ;
+ + ids ;
}
}
return 0 ;
2006-10-04 13:15:37 +04:00
}
static int tifm_uevent ( struct device * dev , char * * envp , int num_envp ,
char * buffer , int buffer_size )
{
2007-04-12 10:59:14 +04:00
struct tifm_dev * sock = container_of ( dev , struct tifm_dev , dev ) ;
2006-10-04 13:15:37 +04:00
int i = 0 ;
int length = 0 ;
if ( add_uevent_var ( envp , num_envp , & i , buffer , buffer_size , & length ,
2007-04-12 10:59:14 +04:00
" TIFM_CARD_TYPE=%s " ,
tifm_media_type_name ( sock - > type , 1 ) ) )
2006-10-04 13:15:37 +04:00
return - ENOMEM ;
return 0 ;
}
2007-04-12 10:59:13 +04:00
static int tifm_device_probe ( struct device * dev )
{
struct tifm_dev * sock = container_of ( dev , struct tifm_dev , dev ) ;
struct tifm_driver * drv = container_of ( dev - > driver , struct tifm_driver ,
driver ) ;
int rc = - ENODEV ;
get_device ( dev ) ;
if ( dev - > driver & & drv - > probe ) {
rc = drv - > probe ( sock ) ;
if ( ! rc )
return 0 ;
}
put_device ( dev ) ;
return rc ;
}
static void tifm_dummy_event ( struct tifm_dev * sock )
{
return ;
}
static int tifm_device_remove ( struct device * dev )
{
struct tifm_dev * sock = container_of ( dev , struct tifm_dev , dev ) ;
struct tifm_driver * drv = container_of ( dev - > driver , struct tifm_driver ,
driver ) ;
if ( dev - > driver & & drv - > remove ) {
sock - > card_event = tifm_dummy_event ;
sock - > data_event = tifm_dummy_event ;
drv - > remove ( sock ) ;
sock - > dev . driver = NULL ;
}
put_device ( dev ) ;
return 0 ;
}
2006-12-10 17:55:37 +03:00
# ifdef CONFIG_PM
static int tifm_device_suspend ( struct device * dev , pm_message_t state )
{
struct tifm_dev * fm_dev = container_of ( dev , struct tifm_dev , dev ) ;
2007-04-12 10:59:13 +04:00
struct tifm_driver * drv = container_of ( dev - > driver , struct tifm_driver ,
driver ) ;
2006-12-10 17:55:37 +03:00
2007-04-12 10:59:13 +04:00
if ( dev - > driver & & drv - > suspend )
2006-12-10 17:55:37 +03:00
return drv - > suspend ( fm_dev , state ) ;
return 0 ;
}
static int tifm_device_resume ( struct device * dev )
{
struct tifm_dev * fm_dev = container_of ( dev , struct tifm_dev , dev ) ;
2007-04-12 10:59:13 +04:00
struct tifm_driver * drv = container_of ( dev - > driver , struct tifm_driver ,
driver ) ;
2006-12-10 17:55:37 +03:00
2007-04-12 10:59:13 +04:00
if ( dev - > driver & & drv - > resume )
2006-12-10 17:55:37 +03:00
return drv - > resume ( fm_dev ) ;
return 0 ;
}
# else
# define tifm_device_suspend NULL
# define tifm_device_resume NULL
# endif /* CONFIG_PM */
2006-10-04 13:15:37 +04:00
static struct bus_type tifm_bus_type = {
. name = " tifm " ,
2007-04-12 10:59:14 +04:00
. match = tifm_bus_match ,
2006-10-04 13:15:37 +04:00
. uevent = tifm_uevent ,
2007-04-12 10:59:13 +04:00
. probe = tifm_device_probe ,
. remove = tifm_device_remove ,
2006-12-10 17:55:37 +03:00
. suspend = tifm_device_suspend ,
. resume = tifm_device_resume
2006-10-04 13:15:37 +04:00
} ;
static void tifm_free ( struct class_device * cdev )
{
struct tifm_adapter * fm = container_of ( cdev , struct tifm_adapter , cdev ) ;
kfree ( fm ) ;
}
static struct class tifm_adapter_class = {
. name = " tifm_adapter " ,
. release = tifm_free
} ;
2007-04-12 10:59:17 +04:00
struct tifm_adapter * tifm_alloc_adapter ( unsigned int num_sockets ,
struct device * dev )
2006-10-04 13:15:37 +04:00
{
struct tifm_adapter * fm ;
2007-04-12 10:59:17 +04:00
fm = kzalloc ( sizeof ( struct tifm_adapter )
+ sizeof ( struct tifm_dev * ) * num_sockets , GFP_KERNEL ) ;
2006-10-04 13:15:37 +04:00
if ( fm ) {
fm - > cdev . class = & tifm_adapter_class ;
2007-04-12 10:59:17 +04:00
fm - > cdev . dev = dev ;
2006-10-04 13:15:37 +04:00
class_device_initialize ( & fm - > cdev ) ;
2007-04-12 10:59:17 +04:00
spin_lock_init ( & fm - > lock ) ;
fm - > num_sockets = num_sockets ;
2006-10-04 13:15:37 +04:00
}
return fm ;
}
EXPORT_SYMBOL ( tifm_alloc_adapter ) ;
2007-04-12 10:59:15 +04:00
int tifm_add_adapter ( struct tifm_adapter * fm )
2006-10-04 13:15:37 +04:00
{
int rc ;
if ( ! idr_pre_get ( & tifm_adapter_idr , GFP_KERNEL ) )
return - ENOMEM ;
spin_lock ( & tifm_adapter_lock ) ;
rc = idr_get_new ( & tifm_adapter_idr , fm , & fm - > id ) ;
spin_unlock ( & tifm_adapter_lock ) ;
2007-04-12 10:59:17 +04:00
if ( rc )
return rc ;
snprintf ( fm - > cdev . class_id , BUS_ID_SIZE , " tifm%u " , fm - > id ) ;
rc = class_device_add ( & fm - > cdev ) ;
if ( rc ) {
spin_lock ( & tifm_adapter_lock ) ;
idr_remove ( & tifm_adapter_idr , fm - > id ) ;
spin_unlock ( & tifm_adapter_lock ) ;
2006-10-04 13:15:37 +04:00
}
2007-04-12 10:59:17 +04:00
2006-10-04 13:15:37 +04:00
return rc ;
}
EXPORT_SYMBOL ( tifm_add_adapter ) ;
void tifm_remove_adapter ( struct tifm_adapter * fm )
{
2007-04-12 10:59:17 +04:00
unsigned int cnt ;
2007-04-12 10:59:15 +04:00
flush_workqueue ( workqueue ) ;
2007-04-12 10:59:17 +04:00
for ( cnt = 0 ; cnt < fm - > num_sockets ; + + cnt ) {
if ( fm - > sockets [ cnt ] )
device_unregister ( & fm - > sockets [ cnt ] - > dev ) ;
}
2006-10-04 13:15:37 +04:00
spin_lock ( & tifm_adapter_lock ) ;
idr_remove ( & tifm_adapter_idr , fm - > id ) ;
spin_unlock ( & tifm_adapter_lock ) ;
2007-04-12 10:59:17 +04:00
class_device_del ( & fm - > cdev ) ;
2006-10-04 13:15:37 +04:00
}
EXPORT_SYMBOL ( tifm_remove_adapter ) ;
2007-04-12 10:59:17 +04:00
void tifm_free_adapter ( struct tifm_adapter * fm )
{
class_device_put ( & fm - > cdev ) ;
}
EXPORT_SYMBOL ( tifm_free_adapter ) ;
2006-10-04 13:15:37 +04:00
void tifm_free_device ( struct device * dev )
{
2007-04-12 10:59:18 +04:00
struct tifm_dev * sock = container_of ( dev , struct tifm_dev , dev ) ;
kfree ( sock ) ;
2006-10-04 13:15:37 +04:00
}
EXPORT_SYMBOL ( tifm_free_device ) ;
2007-04-12 10:59:18 +04:00
struct tifm_dev * tifm_alloc_device ( struct tifm_adapter * fm , unsigned int id ,
unsigned char type )
2006-10-04 13:15:37 +04:00
{
2007-04-12 10:59:18 +04:00
struct tifm_dev * sock = NULL ;
if ( ! tifm_media_type_name ( type , 0 ) )
return sock ;
2006-10-04 13:15:37 +04:00
2007-04-12 10:59:18 +04:00
sock = kzalloc ( sizeof ( struct tifm_dev ) , GFP_KERNEL ) ;
if ( sock ) {
spin_lock_init ( & sock - > lock ) ;
sock - > type = type ;
sock - > socket_id = id ;
sock - > card_event = tifm_dummy_event ;
sock - > data_event = tifm_dummy_event ;
2006-12-08 08:50:51 +03:00
2007-04-12 10:59:18 +04:00
sock - > dev . parent = fm - > cdev . dev ;
sock - > dev . bus = & tifm_bus_type ;
sock - > dev . dma_mask = fm - > cdev . dev - > dma_mask ;
sock - > dev . release = tifm_free_device ;
snprintf ( sock - > dev . bus_id , BUS_ID_SIZE ,
" tifm_%s%u:%u " , tifm_media_type_name ( type , 2 ) ,
fm - > id , id ) ;
printk ( KERN_INFO DRIVER_NAME
" : %s card detected in socket %u:%u \n " ,
tifm_media_type_name ( type , 0 ) , fm - > id , id ) ;
2006-10-04 13:15:37 +04:00
}
2007-04-12 10:59:18 +04:00
return sock ;
2006-10-04 13:15:37 +04:00
}
EXPORT_SYMBOL ( tifm_alloc_device ) ;
void tifm_eject ( struct tifm_dev * sock )
{
struct tifm_adapter * fm = dev_get_drvdata ( sock - > dev . parent ) ;
fm - > eject ( fm , sock ) ;
}
EXPORT_SYMBOL ( tifm_eject ) ;
int tifm_map_sg ( struct tifm_dev * sock , struct scatterlist * sg , int nents ,
int direction )
{
return pci_map_sg ( to_pci_dev ( sock - > dev . parent ) , sg , nents , direction ) ;
}
EXPORT_SYMBOL ( tifm_map_sg ) ;
void tifm_unmap_sg ( struct tifm_dev * sock , struct scatterlist * sg , int nents ,
int direction )
{
pci_unmap_sg ( to_pci_dev ( sock - > dev . parent ) , sg , nents , direction ) ;
}
EXPORT_SYMBOL ( tifm_unmap_sg ) ;
2007-04-12 10:59:15 +04:00
void tifm_queue_work ( struct work_struct * work )
{
queue_work ( workqueue , work ) ;
}
EXPORT_SYMBOL ( tifm_queue_work ) ;
2006-10-04 13:15:37 +04:00
int tifm_register_driver ( struct tifm_driver * drv )
{
drv - > driver . bus = & tifm_bus_type ;
return driver_register ( & drv - > driver ) ;
}
EXPORT_SYMBOL ( tifm_register_driver ) ;
void tifm_unregister_driver ( struct tifm_driver * drv )
{
driver_unregister ( & drv - > driver ) ;
}
EXPORT_SYMBOL ( tifm_unregister_driver ) ;
static int __init tifm_init ( void )
{
2007-04-12 10:59:15 +04:00
int rc ;
2006-10-04 13:15:37 +04:00
2007-04-12 10:59:15 +04:00
workqueue = create_freezeable_workqueue ( " tifm " ) ;
if ( ! workqueue )
return - ENOMEM ;
rc = bus_register ( & tifm_bus_type ) ;
if ( rc )
goto err_out_wq ;
rc = class_register ( & tifm_adapter_class ) ;
if ( ! rc )
return 0 ;
bus_unregister ( & tifm_bus_type ) ;
err_out_wq :
destroy_workqueue ( workqueue ) ;
2006-10-04 13:15:37 +04:00
return rc ;
}
static void __exit tifm_exit ( void )
{
class_unregister ( & tifm_adapter_class ) ;
bus_unregister ( & tifm_bus_type ) ;
2007-04-12 10:59:15 +04:00
destroy_workqueue ( workqueue ) ;
2006-10-04 13:15:37 +04:00
}
subsys_initcall ( tifm_init ) ;
module_exit ( tifm_exit ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Alex Dubov " ) ;
MODULE_DESCRIPTION ( " TI FlashMedia core driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_VERSION ( DRIVER_VERSION ) ;