linux2.6.20 sd/mmc卡驱动学习日记


首先,我们来看Makefile文件吧,Makefile中文件的目标文件的顺序是很重要的,因为这个会涉及到模块的依赖关系,比如说,如果这些源文件中 有module_init(),则这些module_init就按在Makefile中的顺序链接进内核,之后也按照链接的顺序进行调用。根据我们的内核 配置选项,将要编译进内核的文件就只有mmc.c,mmc_sysfs.c,mmc_block.c,mmc_queue.c,s3cmci.c这几个文 件。其中mmc.c与mmc_queue.c主要是定义了一些其他文件中将要使用的函数,我们暂时不管它。接下来,我们来分析mmc_sysfs.c

  我们先来看mmc_init(),这是系统启动后将要调用的,在mmc_init函数中,主要完成3项工作 :
    workqueue = create_singlethread_workqueue("kmmcd");//创见一个单线程的工作队列
    bus_register(&mmc_bus_type);//注册总线
    class_register(&mmc_host_class);//注册mmc_host_class
mmc_bus_type的定义为:   

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, 
} ;

这里,我们主要关注mmc_bus_match与mmc_bus_probe,此两函数将要在device_register和driver_register向总线注册设备的时候被调用。


 

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) ; 
} 
# define mmc_card_bad( c) ( ( c) - > state& ( MMC_STATE_BAD)

可见,对于mmc总线,mmc_bus_match是通过返回struct mmc_card中的状态标示state位来实现的。

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) ; 
}

在使用bus_register之后,我们可以在sysfs的/sys/bus目录里看到它

static struct class mmc_host_class = { 
    . name        = "mmc_host" , 
    . dev_release    = mmc_host_classdev_release, 
} ; 
class_register之后,在/ sys/ class目录下将出现mmc_host目录



---------------------------------------------------------------------------------
接下来,我们看mmc_block.c,还是从初始化函数mmc_blk_init开始分析

static int __init mmc_blk_init( void ) 
{ 
    int res = - ENOMEM; 

    res = register_blkdev( major, "mmc" ) ; 
    if ( res < 0) { 
        printk( KERN_WARNING "Unable to get major %d for MMC media: %d/n" , 
         major, res) ; 
        goto out; 
    } 
    if ( major = = 0) 
        major = res; 

    return mmc_register_driver( & mmc_driver) ; 

 out: 
    return res; 
}

  在mmc_blk_init中, register_blkdev(major, "mmc")的作用是注册一个块设备。如果传递的major为0,这内核将分派一个新的主设备号给设备。
register_blkdev的功能比较少,一是动态分配设备号,二是在/proc/devices中创建一个入口项 。故通过它之后,系统还是不能使用块设备的。
  接着我们看 
    return mmc_register_driver(&mmc_driver);
  mmc_register_driver在mmc_sysfs.c中定义:

static struct mmc_driver mmc_driver = { 
    . drv        = { 
        . name    = "mmcblk" , 
    } , 
    . probe        = mmc_blk_probe, 
    . remove         = mmc_blk_remove, 
    . suspend    = mmc_blk_suspend, 
    . resume        = mmc_blk_resume, 
} ; 

int mmc_register_driver( struct mmc_driver * drv) 
{ 
    drv- > drv. bus = & mmc_bus_type; 
    return driver_register( & drv- > drv) ; 
}

这两句代码比较好理解吧。在注册一个struct driver之前,都要先设置它的bus,类似的,在platform_driver_register中我们也可所以看到:
drv->driver.bus=&platform_bus_type
driver_register将到相应总线mmc_bus_type上去搜索相应设备。找到设备后就设置dev->driver=drv,并调用mmc_bus_type总线的probe函数被调用,即mmmc_bus_probe函数.
我们跟踪driver_register(&drv->drv),它会调应bus_add_driver。


int bus_add_driver( struct device_driver * drv) 
{ 
    . . . 
    . . . 
    error = kobject_set_name( & drv- > kobj, "%s" , drv- > name) ; 
    if ( error ) 
        goto out_put_bus; 
    drv- > kobj. kset = & bus- > drivers; 
    if ( ( error = kobject_register( & drv- > kobj) ) ) 
        goto out_put_bus; 

    error = driver_attach( drv) ; 
    . . . 
    . . . 
}

   在调用kobject_register之后,我们可以在/sys/bus/mmc/driver目录下看到mmcblk文件driver_attach 函数会遍历相应总线(mmc_bus_type)上的dev,对这些dev执行总线的match函数(mmc_bus_match)。如果match成 功,它会调用mmc_bus_type总线的probe函数mmc_bus_probe(如果总线的probe存在的话).我们在以上走过的流程中可以看 到,我们并没有向总线添加任何设备,故mmc_bus_probe是不会调用的。但相应的driver已经注册到系统了。
   
  那么,现在mmc_block.c中的分析就先暂时到此为止。。。不过,等会儿我们还会继续回到这个文件的。。

----------------------------------未完待续-----------------------------------

linux2.6.20 sd/mmc卡驱动学习日记2(基于s3c2440)

我们接着来到s3cmci.c文件
s3cmci_init----->platform_driver_register(&s3cmci_driver_2440)------------>s3cmci_probe_2440----->s3cmci_probe

在s3cmci_probe中主要是分配及初始化
    struct mmc_host     *mmc;
    struct s3cmci_host     *host;
这两个结构体。分配DMA通道,注册irq中断。
以下对个别函数的作用进行说明:
1:    clk_get

    系统初始化的时候外围总线上的设备不是都给时钟的,主要是为了省电。 
    在2.6.20中,我们在文件arch/arm/mach-s3c2410/s3c2410-clock中可以看到nand,sdi,adc,i2c,iis这些是不给时钟的
    lcd,gpio,usb,uart这些是给时钟的
    clk_get在arch/arm/mach-s3c2410/clock.c中定义
    


2:     mmc_alloc_host
    mmc_alloc_host分配sizeof(struct mmc_host)+extra这么大的空间,并做以下初始化
    host = mmc_alloc_host_sysfs(extra, dev);
    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_LIST_HEAD(&host->cards);
    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_sectors = 1 << (PAGE_CACHE_SHIFT - 9);
    host->max_seg_size = PAGE_CACHE_SIZE;

3:
    mmc_add_host
    调用mmc_add_host的最终结果是device_add(&host->class->dev)(所以在/sys/class/mmc/目录下出现mmc0文件)。
    并会调用mmc_power_off(host);mmc_detect_change(host, 0);
    mmc_power_off函数比较简单,顾名思义,它是让SD/MMC卡停止工作的,相应的,此函数就会配置相应的IO口以及时钟等。
我们来看看mmc_detect_change函数吧。
void mmc_detect_change(struct mmc_host *host, unsigned long delay)
{
    mmc_schedule_delayed_work(&host->detect, delay);
}
int mmc_schedule_delayed_work(struct delayed_work *work, unsigned long delay)
{
    return queue_delayed_work(workqueue, work, delay);
}
queue_delayed_work把host->detect提交到workqueue工作对列中。
相应定义:
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
workqueue = create_singlethread_workqueue("kmmcd");
所以,当此delayed_work执行的时候,mmc_rescan将会被调用



static void mmc_rescan( struct work_struct * work) 
{ 
    struct mmc_host * host = 
        container_of( work, struct mmc_host, detect. work) ; //返回指向s3cmci_probe中分配的mmc_host结构的指针

    struct list_head * l, * n; 
    unsigned char power_mode; 

    mmc_claim_host( host) ; 
    /*
    驱动中使用mmc_claim_host(host);来得知,当前mmc控制器是否被占用,当前mmc控制器如果被占用,那么 host->claimed = 1;否则为0,如果为1,那么会在for(;;)循环中调用schedule切换出自己,当占用mmc控制器的操作完成之后,执行 mmc_release_host()的时候,会激活登记到等待队列&host->wq中的其他程序获得mmc主控制器的物理使用权
    */
 
    /*
     * Check for removed cards and newly inserted ones. We check for
     * removed cards first so we can intelligently re-select the VDD.
    */
 
    power_mode = host- > ios . power_mode; 
    if ( power_mode = = MMC_POWER_ON) 
        mmc_check_cards( host) ; 
    mmc_setup( host) ; 
    /*
     * Some broken cards process CMD1 even in stand-by state. There is
     * no reply, but an ILLEGAL_COMMAND error is cached and returned
     * after next command. We poll for card status here to clear any
     * possibly pending error.
    */
 
    if ( power_mode = = MMC_POWER_ON) 
        mmc_check_cards( host) ; 

    if ( ! list_empty( & host- > cards) ) { 
        /*
         * (Re-)calculate the fastest clock rate which the
         * attached cards and the host support.
        */
 
        host- > ios . clock = mmc_calculate_clock( host) ; 
        mmc_set_ios( host) ; 
    } 

    mmc_release_host( host) ; 

    list_for_each_safe( l, n, & host- > cards) { 
        struct mmc_card * card = mmc_list_to_card( l) ; 

        /*
         * If this is a new and good card, register it.
        */
 
        if ( ! mmc_card_present( card) & & ! mmc_card_dead( card) ) { 
            if ( mmc_register_card( card) ) 
                mmc_card_set_dead( card) ; 
            else 
                mmc_card_set_present( card) ; 
        } 

        /*s
         * If this card is dead, destroy it.
        */
 
        if ( mmc_card_dead( card) ) { 
            list_del( & card- > node) ; 
            mmc_remove_card( card) ; 
        } 
    } 

    /*
     * If we discover that there are no cards on the
     * bus, turn off the clock and power down.
    */
 
    if ( list_empty( & host- > cards) ) 
        mmc_power_off( host) ; 
}

起 初,power_mode = MMC_POWER_OFF,故第一个mmc_check_cards(host)是不会执行的。但mmc_setup函数中设置了power_mode = MMC_POWER_ON。故第二个mmc_check_cards(host)是会执行的。
在mmc_setup中,会调用mmc_discover_cards.如果发现有卡,则add new card to list.
因为现在没有发现卡,host->card=NULL,故mmc_check_cards与mmc_rescan中的list_for_each_safe循环体中的内容也是不会执行的。

static void mmc_check_cards( struct mmc_host * host) 
{ 
    struct list_head * l, * n; 

    mmc_deselect_cards( host) ; // Ensure that no card is selected.


    list_for_each_safe( l, n, & host- > cards) { 
        struct mmc_card * card = mmc_list_to_card( l) ; 
        struct mmc_command cmd; 
        int err; 

        cmd. opcode = MMC_SEND_STATUS; 
        cmd. arg = card- > rca < < 16; 
        cmd. flags = MMC_RSP_R1 | MMC_CMD_AC; 

        err = mmc_wait_for_cmd( host, & cmd, CMD_RETRIES) ; 
        if ( err = = MMC_ERR_NONE) 
            continue ; 

        mmc_card_set_dead( card) ; 
} 
}

执行完s3cmci_probe后,在终端会有以下信息:
s3c2410-sdi s3c2410-sdi: powered down.
s3c2410-sdi s3c2410-sdi: initialisation done.
s3c2410-sdi s3c2410-sdi: running at 0kHz (requested: 0kHz).
s3c2410-sdi s3c2410-sdi: running at 198kHz (requested: 197kHz).
s3c2410-sdi s3c2410-sdi: running at 198kHz (requested: 197kHz).
s3c2410-sdi s3c2410-sdi: running at 198kHz (requested: 197kHz).
s3c2410-sdi s3c2410-sdi: CMD[TIMEOUT] #2 op:APP_CMD(55) arg:0x00000000 flags:0xe
s3c2410-sdi s3c2410-sdi: CMD[TIMEOUT] #3 op:APP_CMD(55) arg:0x00000000 flags:0xe
s3c2410-sdi s3c2410-sdi: CMD[TIMEOUT] #4 op:APP_CMD(55) arg:0x00000000 flags:0xe
s3c2410-sdi s3c2410-sdi: CMD[TIMEOUT] #5 op:APP_CMD(55) arg:0x00000000 flags:0xe
s3c2410-sdi s3c2410-sdi: CMD[TIMEOUT] #6 op:ALL_SEND_OCR(1) arg:0x00000000 flage
s3c2410-sdi s3c2410-sdi: powered down.

------------------------------------未完待续------------------------------------

linux2.6.20 sd/mmc卡驱动学习日记3(基于s3c2440)

至此,我们已经跟踪了mmc/sd卡驱动的注册。。我们接着来看插入拔除卡的中断处理函数:

static irqreturn_t s3cmci_irq_cd( int irq, void * dev_id) 
{ 
    struct s3cmci_host * host = ( struct s3cmci_host * ) dev_id; 

    dbg( host, dbg_irq, "card detect/n" ) ; 

    mmc_detect_change( host- > mmc, 500) ; 

    return IRQ_HANDLED; 
}

可 见,这里也会调用mmc_detect_change。。。我们跟着前面的分析来到mmc_setup这里,此时mmc_setup调用 mmc_discover_cards。Create a mmc_card entry for each discovered card,add new card to list.同时还会调用mmc_read_switch_caps或者mmc_process_ext_csds来实现对大容量卡的支持(>4G)
跟着程序的流程我们来到
if (!mmc_card_present(card) && !mmc_card_dead(card)) {
            if (mmc_register_card(card))
来看

int mmc_register_card( struct mmc_card * card) 
{ 
    int ret; 

    snprintf( card- > dev. bus_id, sizeof ( card- > dev. bus_id) , 
         "%s:%04x" , mmc_hostname( card- > host) , card- > rca) ; 

    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; 
}

此 函数在mmc_sysfs.c中定义。 device_add(&card->dev)将到相应总线mmc_bus_type上去搜索相应驱动。找到驱动后就设置 dev->driver=drv,并调用mmc_bus_type总线的probe函数被调用,即mmmc_bus_probe函数


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) ; 
}

mmc_bus_probe会调用mmc_blk_probe
mmc_blk_probe()首先分配一个新的mmc_blk_data结构变量,然后调用mmc_init_queue,初始化blk队列。然后建立一个线程mmc_queue_thread()。

static int mmc_blk_probe( struct mmc_card * card) 
{ 
    struct mmc_blk_data * md; 
    int err; 

    /*
     * Check that the card supports the command class(es) we need.
    */
 
    if ( ! ( card- > csd. cmdclass & CCC_BLOCK_READ) ) 
        return - ENODEV; 

    md = mmc_blk_alloc( card) ; //

    if ( IS_ERR( md) ) 
        return PTR_ERR( md) ; 

    err = mmc_blk_set_blksize( md, card) ; 
    if ( err) 
        goto out; 

    printk( KERN_INFO "%s: %s %s %lluKiB %s/n" , 
        md- > disk- > disk_name, mmc_card_id( card) , mmc_card_name( card) , 
        ( unsigned long long ) ( get_capacity( md- > disk) > > 1) , 
        md- > read_only ? "(ro)" : "" ) ; 

    mmc_set_drvdata( card, md) ; 
    add_disk( md- > disk) ; 
    return 0; 

 out: 
    mmc_blk_put( md) ; 

    return err; 
}

struct mmc_blk_data封装了struct gendisk  与 struct mmc_queue,而struct mmc_queue封装了struct mmc_card与struct request。


static struct mmc_blk_data * mmc_blk_alloc( struct mmc_card * card) 
{ 
    struct mmc_blk_data * md; 
    int devidx, ret; 

    devidx = find_first_zero_bit( dev_use, MMC_NUM_MINORS) ; 
    if ( devidx > = MMC_NUM_MINORS) 
        return ERR_PTR( - ENOSPC) ; 
    __set_bit( devidx, dev_use) ; 

    md = kmalloc( sizeof ( struct mmc_blk_data) , GFP_KERNEL) ; 
    if ( ! md) { 
        ret = - ENOMEM; 
        goto out; 
} 

    memset ( md, 0, sizeof ( struct mmc_blk_data) ) ; 

    /*
     * Set the read-only status based on the supported commands
     * and the write protect switch.
    */
 
    md- > read_only = mmc_blk_readonly( card) ; //写保护


    /*
     * Both SD and MMC specifications state (although a bit
     * unclearly in the MMC case) that a block size of 512
     * bytes must always be supported by the card.
    */
 
    md- > block_bits = 9; //块大小


    md- > disk = alloc_disk( 1 < < MMC_SHIFT) ; 
        //分配struct gendist,驱动程序不能自己动态分配该结构,而是必须调用

        //alloc_disk,其参数为次设备号数目,注意了,是次设备号数目,不是次设备号

    if ( md- > disk = = NULL ) { 
        ret = - ENOMEM; 
        goto err_kfree; 
} 

    spin_lock_init( & md- > lock) ; 
    md- > usage = 1; 

    ret = mmc_init_queue( & md- > queue , card, & md- > lock) ; //

    if ( ret) 
        goto err_putdisk; 

    md- > queue . prep_fn = mmc_blk_prep_rq; 
    md- > queue . issue_fn = mmc_blk_issue_rq; 
    md- > queue . data = md; 
        //上面设置了请求队列,现在就可以初始化及安装相应的gendisk结构了

    md- > disk- > major    = major; 
    md- > disk- > first_minor = devidx < < MMC_SHIFT; 
    md- > disk- > fops = & mmc_bdops; 
    md- > disk- > private_data = md; 
    md- > disk- > queue = md- > queue . queue ; 
    md- > disk- > driverfs_dev = & card- > dev; 

    /*
     * As discussed on lkml, GENHD_FL_REMOVABLE should:
     *
     * - be set for removable media with permanent block devices
     * - be unset for removable block devices with permanent media
     *
     * Since MMC block devices clearly fall under the second
     * case, we do not set GENHD_FL_REMOVABLE. Userspace
     * should use the block device creation/destruction hotplug
     * messages to tell when the card is present.
    */
 

    sprintf ( md- > disk- > disk_name, "mmcblk%d" , devidx) ; 

    blk_queue_hardsect_size( md- > queue . queue , 1 < < md- > block_bits) ; 

    /*
     * The CSD capacity field is in units of read_blkbits.
     * set_capacity takes units of 512 bytes.
    */
 
    set_capacity( md- > disk, card- > csd. capacity < < ( card- > csd.read_blkbits - 9) ) ; 
    return md; 

 err_putdisk: 
    put_disk( md- > disk) ; 
 err_kfree: 
    kfree( md) ; 
 out: 
    return ERR_PTR( ret) ; 
}

至此,驱动向系统添加了一个块设备。
请求处理过程:
mmc_request--->mmc_queue_thread----->mmc_blk_issue_rq---->mmc_wait_for_req--->mmc_start_request---->s3cmci_requ


发现了两篇讲SD/MMC卡驱动的文章,觉得不错,转了过来,谢谢原作者
文一:FROM:http://blog.chinaunix.net/u2/69999/showart_734099.html

 关于linux 2.6 mmc/sd驱动    

 linux 2.6 中的mmc/sd驱动分为以下几方面的内容        

1. sysfs 层的总线类型处理: 注册一组 mmc 类型处理函数, 标志为 "mmc"    

   具体在mmc_sysfs.c文件中实现

2. mmc/sd 快设备管理:注册一个块设备和一组 mmc 总线类型的 driver 子函数, 实现块设备的队列管理等

drivers/mmc/mmc_block.c

3. mmc/sd host管理: 实现 host 的管理

. drivers/mmc/mmc.c:主要的 MMC command 與 protocol 實作。

4. 针对特定的mcu实现一个host驱动实例:主要是注册一个 host实体,中断处理函数,io设置函数,请求处理函数等    

       以上1.2.3基本是不需要修改的,需要处理的就是 4.要做的工作.当有卡插入时,由4中实现的插卡中断激活卡初始化程序和总线探测函数. 由mmc总线探测函数会调用块设备的探测函数,在卡设备探测函数中会初始化块设备的请求队列和注册一个gendisk实体(以后文件系统会通过 gendisk实体访问 mmc 块设备),同时在sysfs中建立真正的 mmc/sd 设备.块设备通过具体的 host 注册的io设置函数和请求函数与具体的host通讯.

后面的文章将具体对几个部分进行分析
-----------------------------------------------------------------------------
文二:
FROM:http://blog.chinaunix.net/u1/42456/showart_516030.html

最近要让s3c2440在linux2.6.18上 支持4G的SD卡.

原文地址:http://linux4u.wikidot.com/mmc-controller-driver

==============================================================

linux-2.6.2x的mmc驱动与linux-2.6.1x的mmc驱动的区别 
在linux-2.6.2x中,mmc驱动用到的block_device_operations结构已重新定义,请看:

linux-2.6.1x:

struct block_device_operations { 
int (*open) (struct inode *, struct file *); 
int (*release) (struct inode *, struct file *); 
int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long); 
int (*media_changed) (struct gendisk *); 
int (*revalidate_disk) (struct gendisk *);
struct module *owner; 
}; 

linux-2.6.2x

struct block_device_operations { 
int (*open) (struct inode *, struct file *); 
int (*release) (struct inode *, struct file *); 
int (*ioctl) (struct inode *, struct file *, unsigned, unsigned long); 
long (*unlocked_ioctl) (struct file *, unsigned, unsigned long); 
long (*compat_ioctl) (struct file *, unsigned, unsigned long); 
int (*direct_access) (struct block_device *, sector_t, unsigned long *); 
int (*media_changed) (struct gendisk *); 
int (*revalidate_disk) (struct gendisk *); 
int (*getgeo)(struct block_device *, struct hd_geometry *);
struct module *owner; 
}; 


注意到新版本的block驱动接口结构增加了gntgeo成员,使调用者可以直接调用此函数获得设备的几何结构。

工作流程: 
mmc驱动主要文件包括
drivers/mmc/card/block.c
drivers/mmc/card/queue.c
drivers/mmc/core/core.c
drivers/mmc/core/host.c
drivers/mmc/core/
内核启动时,首先执行core/core.c的mmc_init,注册mmc、sd总线,以及一个host class设备。接着执行card/block.c中,申请一个块设备。

数据结构: 
mmc总线操作相关函数,由于mmc卡支持多种总数据线,如SPI、SDIO、8LineMMC,而不同的总线的操作控制方式不尽相同,所以通过此结构与相应的总线回调函数相关联。

//总线操作结构
struct mmc_bus_ops { 
void (*remove)(struct mmc_host *); 
void (*detect)(struct mmc_host *); 
int (*sysfs_add)(struct mmc_host *, struct mmc_card *card); 
void (*sysfs_remove)(struct mmc_host *, struct mmc_card *card); 
void (*suspend)(struct mmc_host *); 
void (*resume)(struct mmc_host *); 
}; 
// mmc卡的总线操作 core/mmc.c 
static const struct mmc_bus_ops mmc_ops = { 
.remove = mmc_remove,
.detect = mmc_detect, 
.sysfs_add = mmc_sysfs_add, 
.sysfs_remove = mmc_sysfs_remove, 
.suspend = mmc_suspend, 
.resume = mmc_resume, 
}; 
// sd卡的总线操作 core/sd.c 
static const struct mmc_bus_ops mmc_sd_ops = { 
.remove = mmc_sd_remove, 
.detect = mmc_sd_detect, 
.sysfs_add = mmc_sd_sysfs_add, 
.sysfs_remove = mmc_sd_sysfs_remove, 
.suspend = mmc_sd_suspend,
.resume = mmc_sd_resume, 
}; 
// sdio的总线操作 core/sdio.c 
static const struct mmc_bus_ops mmc_sdio_ops = { 
.remove = mmc_sdio_remove, 
.detect = mmc_sdio_detect, 
}; 

关于总线操作的函数: 
.detect,驱动程序经常需要调用此函数去检测mmc卡的状态,具体实现是发送CMD13命令,并读回响应,如果响应错误,则依次调用.remove、detach_bus来移除卡及释放总线。

总体架构: 
kernel启动时,先后执行mmc_init()及mmc_blk_init(),以对mmc设备及mmc块模块进行初始化。
然后在挂载mmc设备驱动时,执行驱动程序中的xx_mmc_probe(),检测host设备中挂载的sd设备。此时probe函数会创建一个host设备,然后开启一个延时任务mmc_rescan()。
驱动挂载成功后,mmc_rescan()函数被执行,然后对卡进行初始化(步骤后面详细讲述)。
假如扫描到总线上挂有有效的设备,就调用相对应的函数把设备装到系统中,mmc_attach_sdio()、mmc_attach_sd()、mmc_attach_mmc()这三个函数分别是装载sdio设备,sd卡和mmc卡的。
在 sd卡中,驱动循环发送ACMD41、CMD55给卡,读取OCR寄存器,成功后,依次发送CMD2(读CID)、CMD3(得到RCA)、CMD9(读 CSD)、CMD7(选择卡)。后面还有几个命令分别是ACMD41&CMD51,使用CMD6切换一些功能,如切换到高速模式。
经过上述步骤,已经确定当前插入的卡是一张有效、可识别的存储卡。然后调用mmc_add_card()把存储卡加到系统中。正式与系统驱动连接在一起。
卡 设备加到系统中后,通知mmc块设备驱动。块设备驱动此时调用probe函数,即mmc_blk_probe()函数,mmc_blk_probe()首 先分配一个新的mmc_blk_data结构变量,然后调用mmc_init_queue,初始化blk队列。然后建立一个线程 mmc_queue_thread()。

mmc_rescan:mmc_rescan()函数是在驱动装载的时候,由驱动xx_mmc_probe()调用 mmc_alloc_host()时启动的一个延时任务。 xx_mmc_probe()->mmc_alloc_host()->INIT_DELAYED_WORK(&host->detect, mmc_rescan);

core部分 
1、取得总线
2、检查总线操作结构指针bus_ops,如果为空,则重新利用各总线对端口进行扫描,检测顺序依次为:SDIO、Normal SD、MMC。当检测到相应的卡类型后,就使用mmc_attach_bus()把相对应的总线操作与host连接起来。

void mmc_attach_bus(struct mmc_host *host, const struct mmc_bus_ops *ops)
{
...
host->bus_ops = ops;
...
} 

3、初始化卡接以下流程初始化:
a、发送CMD0使卡进入IDLE状态
b、发送CMD8,检查卡是否SD2.0。SD1.1是不支持CMD8的,因此在SD2.0 Spec中提出了先发送CMD8,如响应为无效命令,则卡为SD1.1,否则就是SD2.0(请参考SD2.0 Spec)。
c、发送CMD5读取OCR寄存器。
d、发送ACMD55、CMD41,使卡进入工作状态。MMC卡并不支持ACMD55、CMD41,如果这步通过了,则证明这张卡是SD卡。
e、如果d步骤错误,则发送CMD1判断卡是否为MMC。SD卡不支持CMD1,而MMC卡支持,这就是SD和MMC类型的判断依据。
f、如果ACMD41和CMD1都不能通过,那这张卡恐怕就是无效卡了,初始化失败。


linux2.6.20 sd/mmc卡驱动学习日记4(基于s3c2440)

下面就驱动移植时需要注意的地方进行分析。


static void s3cmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
mrq所指的struct mmc_request中的值在mmc_blk_issue_rq函数中被设置:

        brq.mrq.cmd = &brq.cmd;
        brq.mrq.data = &brq.data;

        brq.cmd.arg = req->sector << 9;
        brq.cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC;
        brq.data.blksz = 1 << md->block_bits;
        brq.data.blocks = req->nr_sectors >> (md->block_bits - 9);
        brq.stop.opcode = MMC_STOP_TRANSMISSION;
        brq.stop.arg = 0;
        brq.stop.flags = MMC_RSP_R1B | MMC_CMD_AC;
        ...
        
        brq.data.sg = mq->sg;
        brq.data.sg_len = blk_rq_map_sg(req->q, req, brq.data.sg);//从指定的要求中获得全部的段,然后把他们填写到给定的表中
        mmc_wait_for_req(card->host, &brq.mrq);

        brq.data.sg是个struct scatterlist数组。指明了读/写的各个段的长度,及其偏移地址


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值