- Mini2440开发板的MMC/SD硬件接口电路原路图如下:
从电路原理图上可以看出,SD分别使用S3C2440的复用IO端口GPE7-10作为4根数据信号线、使用 GPE6作命令信号线、使用GPE5作时钟信号线,使用复用端口GPG8的外部中断功能来作SD卡的插拔检测,使用GPH8端口来判断SD卡是否写有保 护。
- MMC/SD卡驱动程序的重要数据结构,该结果位于Core核心层,主要用于核心 层与主机驱动层的数据交换处理。定义在/include/linux/mmc/host.h中:
struct mmc_host
{
struct device * parent;
struct device class_dev;
int index;
const struct mmc_host_ops * ops;
unsigned int f_min;
unsigned int f_max;
u32 ocr_avail;
# define MMC_VDD_165_195 0x00000080 /* VDD voltage 1.65 - 1.95 */
# define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */
# define MMC_VDD_21_22 0x00000200 /* VDD voltage 2.1 ~ 2.2 */
# define MMC_VDD_22_23 0x00000400 /* VDD voltage 2.2 ~ 2.3 */
# define MMC_VDD_23_24 0x00000800 /* VDD voltage 2.3 ~ 2.4 */
# define MMC_VDD_24_25 0x00001000 /* VDD voltage 2.4 ~ 2.5 */
# define MMC_VDD_25_26 0x00002000 /* VDD voltage 2.5 ~ 2.6 */
# define MMC_VDD_26_27 0x00004000 /* VDD voltage 2.6 ~ 2.7 */
# define MMC_VDD_27_28 0x00008000 /* VDD voltage 2.7 ~ 2.8 */
# define MMC_VDD_28_29 0x00010000 /* VDD voltage 2.8 ~ 2.9 */
# define MMC_VDD_29_30 0x00020000 /* VDD voltage 2.9 ~ 3.0 */
# define MMC_VDD_30_31 0x00040000 /* VDD voltage 3.0 ~ 3.1 */
# define MMC_VDD_31_32 0x00080000 /* VDD voltage 3.1 ~ 3.2 */
# define MMC_VDD_32_33 0x00100000 /* VDD voltage 3.2 ~ 3.3 */
# define MMC_VDD_33_34 0x00200000 /* VDD voltage 3.3 ~ 3.4 */
# define MMC_VDD_34_35 0x00400000 /* VDD voltage 3.4 ~ 3.5 */
# define MMC_VDD_35_36 0x00800000 /* VDD voltage 3.5 ~ 3.6 */
unsigned long caps; /* Host capabilities */
# define MMC_CAP_4_BIT_DATA ( 1 < < 0) /* Can the host do 4 bit transfers */
# define MMC_CAP_MMC_HIGHSPEED ( 1 < < 1) /* Can do MMC high-speed timing */
# define MMC_CAP_SD_HIGHSPEED ( 1 < < 2) /* Can do SD high-speed timing */
# define MMC_CAP_SDIO_IRQ ( 1 < < 3) /* Can signal pending SDIO IRQs */
# define MMC_CAP_SPI ( 1 < < 4) /* Talks only SPI protocols */
# define MMC_CAP_NEEDS_POLL ( 1 < < 5) /* Needs polling for card-detection */
# define MMC_CAP_8_BIT_DATA ( 1 < < 6) /* Can the host do 8 bit transfers */
/* host specific block data */
unsigned int max_seg_size; /* see blk_queue_max_segment_size */
unsigned short max_hw_segs; /* see blk_queue_max_hw_segments */
unsigned short max_phys_segs; /* see blk_queue_max_phys_segments */
unsigned short unused;
unsigned int max_req_size; /* maximum number of bytes in one req */
unsigned int max_blk_size; /* maximum size of one mmc block */
unsigned int max_blk_count; /* maximum number of blocks in one req */
/* private data */
spinlock_t lock; /* lock for claim and bus ops */
struct mmc_ios ios ; /* current io bus settings */
u32 ocr; /* the current OCR setting */
/* group bitfields together to minimize padding */
unsigned int use_spi_crc: 1;
unsigned int claimed: 1; /* host exclusively claimed */
unsigned int bus_dead: 1; /* bus has been released */
# ifdef CONFIG_MMC_DEBUG
unsigned int removed: 1; /* host is being removed */
# endif
struct mmc_card * card; /* device attached to this host */
wait_queue_head_t wq;
struct delayed_work detect;
const struct mmc_bus_ops * bus_ops; /* current bus driver */
unsigned int bus_refs; /* reference counter */
unsigned int sdio_irqs;
struct task_struct * sdio_irq_thread;
atomic_t sdio_irq_thread_abort;
# ifdef CONFIG_LEDS_TRIGGERS
struct led_trigger * led; /* activity led */
# endif
struct dentry * debugfs_root;
unsigned long private [ 0] ____cacheline_aligned;
} ;
- MMC/SD卡驱动程序的头文件中一些变量的定 义,这些变量在驱动中都会用到。先不用看这些变量将用做什么,等驱动中用到时自然就明白了。代码如下:
# define S3CMCI_DMA 0
enum s3cmci_waitfor
{
COMPLETION_NONE,
COMPLETION_FINALIZE,
COMPLETION_CMDSENT,
COMPLETION_RSPFIN,
COMPLETION_XFERFINISH,
COMPLETION_XFERFINISH_RSPFIN,
} ;
struct s3cmci_host
{
struct platform_device * pdev;struct s3c24xx_mci_pdata *pdata ;
struct mmc_host * mmc;
struct resource * mem;
struct clk * clk;
void __iomem * base;
int irq;
int irq_cd;
int dma;
unsigned long clk_rate;
unsigned long clk_div;
unsigned long real_rate;
u8 prescaler;
unsigned sdiimsk;
unsigned sdidata;
int dodma;
int dmatogo;
struct mmc_request * mrq;
int cmd_is_stop;
spinlock_t complete_lock;
enum s3cmci_waitfor complete_what;
int dma_complete;
u32 pio_sgptr;
u32 pio_bytes;
u32 pio_count;
u32 * pio_ptr;
# define XFER_NONE 0
# define XFER_READ 1
# define XFER_WRITE 2
u32 pio_active;
int bus_width;
char dbgmsg_cmd[ 301] ;
char dbgmsg_dat[ 301] ;
char * status;
unsigned int ccnt, dcnt;
struct tasklet_struct pio_tasklet;
# ifdef CONFIG_CPU_FREQ
struct notifier_block freq_transition;
# endif
} ;
-
- MMC/SD卡驱动程序的加载与卸载部分:
在 Linux中,MMC/SD设备是被作为平台设备添加到系统的。可以查看内核代码:/arch/arm/plat-s3c24xx/devs.c中为 MMC/SD主机控制器SDI定义了平台设备和平台设备资源,然后在/arch/arm/mach-s3c2440/mach-smdk2440.c中的 系统初始化的时候添加到系统中。如下:
-
// 平台设备资源
static struct resource s3c_sdi_resource[ ] = {
[ 0] = {
. start = S3C24XX_PA_SDI,
. end = S3C24XX_PA_SDI + S3C24XX_SZ_SDI - 1,
. flags = IORESOURCE_MEM,
} ,
[ 1] = {
. start = IRQ_SDI,
. end = IRQ_SDI,
. flags = IORESOURCE_IRQ,
}
} ;
//定义 SDI平台设备
struct platform_device s3c_device_sdi = {
. name = "s3c2410-sdi" ,
. id = - 1,
. num_resources = ARRAY_SIZE( s3c_sdi_resource) ,
. resource = s3c_sdi_resource,
} ;
EXPORT_SYMBOL( s3c_device_sdi) ;
//添加 SDI平台设备到平台设备列表
static struct platform_device * smdk2440_devices[ ] __initdata = {
& s3c_device_usb,
& s3c_device_sdi,
& s3c_device_lcd,
& s3c_device_wdt,
& s3c_device_rtc,
& s3c_device_dm9000,
.
.
.
} ;
//平台 设备添加到系统
static void __init smdk2440_machine_init( void )
{
.
.
.
platform_add_devices( smdk2440_devices, ARRAY_SIZE( smdk2440_devices) ) ;
smdk_machine_init( ) ;
}-
所 以,MMC/SD设备驱动程序的加载和卸载部分很简单,就是注册和注销平台设备,代码如下:
-
//加载
static int __init s3cmci_init( void )
{
platform_driver_register( & s3cmci_driver) ;
return 0;
}
//卸载
static void __exit s3cmci_exit( void )
{
platform_driver_unregister( & s3cmci_driver) ;
}
//平台设备操作结构体
static struct platform_driver s3cmci_driver = {
. driver. name = "s3c2410-sdi" ,// 名称和平台设备定义中的对应
. driver. owner = THIS_MODULE,
. probe = s3cmci_probe,// 平台设备探测接口函数
. remove = __devexit_p( s3cmci_remove) ,//__devexit_p的作用以前将过
. shutdown = s3cmci_shutdown,
. suspend = s3cmci_suspend,
. resume = s3cmci_resume,
} ;
- MMC/SD卡驱动程序的加载与卸载部分:
-
- 平台探测函数s3cmci_probe的讲解:
-
static int __devinit s3cmci_probe( struct platform_device * pdev)
{
//该结构体定义在头文件中,现在实例 一个名为host的结构体指针为结构体中的成员赋值做准备
struct s3cmci_host * host;
//实例一个名为mmc的结构体指针, 用于与Core核心层中的mmc_host结构体指针相关联
struct mmc_host * mmc;
int ret;
//初始化一个名为 complete_lock的自旋锁以备后用,该自旋锁的定义在s3cmci_host结构体中
spin_lock_init( & host- > complete_lock) ;
//初始化一个名为pio_tasklet的tasklet,用于实现中断的底半部 机制,底半部服务函数为pio_tasklet,
//将host结构体变量作为服务函数的参数。注意:这里tasklet的变量名与 服务函数名称同名了(这是可以的)。
tasklet_init( & host- > pio_tasklet, pio_tasklet, ( unsigned long ) host) ;
//分配 mmc_host结构体指针的内存空间大小,该函数在host.c中实现,这里要注意一点,为什么参数
//是s3cmci_host结构体 的大小,到host.c中看,实际这里分配的是mmc_host加s3cmci_host的大小。
mmc = mmc_alloc_host( sizeof ( struct s3cmci_host) , & pdev- > dev) ;
if ( ! mmc)
{
ret = - ENOMEM;
goto probe_out;
}
//调用mmc_priv函数将 mmc_host和s3cmci_host结构体的对象关联起来,mmc_priv定义在host.h中
host = mmc_priv( mmc) ;
//下面就开始初始化 s3cmci_host结构体的各成员
host- > mmc = mmc;
host- > pdev = pdev;
host ->pdata = pdev ->dev .platform_data ;
//SDI主机控制器的中断屏蔽寄存 器和数据寄存器,他们定义在mach-s3c2410/include/mach/regs-sdi.h中
host- > sdiimsk = S3C2440_SDIIMSK;
host- > sdidata = S3C2440_SDIDATA;
//complete_what定义 在s3cmci_host结构体中,用来记录请求处理所处的当前状态,这里初始化为
//COMPLETION_NONE即 无状态,定义在头文件的s3cmci_waitfor中,里面枚举了6种状态。
host- > complete_what = COMPLETION_NONE;
//pio_active定义在 s3cmci_host结构体中,用来标记请求处理数据在FIFO方式下的数据方向是读还是写
host- > pio_active = XFER_NONE;
//dodma和dma方便用于标记 是否要使用DMA数据传输方式和DMA通道资源,0表示不使用DMA功能
host- > dodma = 0;
host- > dma = S3CMCI_DMA;
//从SDI平台设备资源中获取 SDI的IO端口资源,该资源在plat-s3c24xx/devs.c的s3c_sdi_resource中指定的
host- > mem = platform_get_resource( pdev, IORESOURCE_MEM, 0) ;
if ( ! host- > mem)
{
dev_err( & pdev- > dev, "failed to get io memory region resouce./n" ) ;
ret = - ENOENT;
goto probe_free_host;
}
//申请SDI的IO端口资源所占用的IO空间(要注意理解IO空间和内存空间的区 别)
host- > mem = request_mem_region( host- > mem- > start, RESSIZE( host- > mem) , pdev- > name) ;
if ( ! host- > mem)
{
dev_err( & pdev- > dev, "failed to request io memory region./n" ) ;
ret = - ENOENT;
goto probe_free_host;
}
//将SDI的IO端口占用的这段IO空间映射到内存的虚拟地址,ioremap定 义在io.h中。
//注意:IO空间要映射后才能使用,以后对虚拟地址的操作就是对IO空间的操作。
host- > base = ioremap( host- > mem- > start, RESSIZE( host- > mem) ) ;
if ( ! host- > base) {
dev_err( & pdev- > dev, "failed to ioremap() io memory region./n" ) ;
ret = - EINVAL;
goto probe_free_mem_region;
}
//同样从SDI平台设备资源中获取SDI的中断号
host- > irq = platform_get_irq( pdev, 0) ;
if ( host- > irq = = 0)
{
dev_err( & pdev- > dev, "failed to get interrupt resouce./n" ) ;
ret = - EINVAL;
goto probe_iounmap;
}
//申请SDI的中断服务,服务函数为 s3cmci_irq,主要参数为host
if ( request_irq( host- > irq, s3cmci_irq, 0, DRIVER_NAME, host) )
{
dev_err( & pdev- > dev, "failed to request mci interrupt./n" ) ;
ret = - ENOENT;
goto probe_iounmap;
}
//在SDI未准备好之前先屏蔽SDI的中断功能
disable_irq( host- > irq) ;
//根据开发板原理图分别设置GPG8、GPH8端口为SD卡插入拔出的检测和有无 写保护的检查,//注意:其实有没有写保护就是检查SD卡侧面有个移动按钮的开关,MMC卡无此功能
host- > pdata- > gpio_detect = S3C2410_GPG8;
host- > pdata- > gpio_wprotect = S3C2410_GPH8;
// 获取GPG8复用端口中断功能的中断号
host- > irq_cd = s3c2410_gpio_getirq (host ->pdata ->gpio_detect ) ;
//GPG8是复用端口,要使用中断功 能则要配置成中断功能,GPG8对应的中断功能是外部中断EINT16,这个数据手册上有讲到
s3c2410_gpio_cfgpin( S3C2410_GPG8, S3C2410_GPG8_EINT16) ;
//申请SDI的卡检测中断服务,服 务函数为s3cmci_irq_cd,主要参数也为host
if ( request_irq( host- > irq_cd, s3cmci_irq_cd, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, DRIVER_NAME, host) )
{
dev_err( & pdev- > dev, "can't get card detect irq./n" ) ;
ret = - ENOENT;
goto probe_free_irq;
}
//获取DMA通道并申请DMA中 断,S3C2440中DMA的使用在后续的文章中再了解
if ( s3c2410_dma_request( S3CMCI_DMA, & s3cmci_dma_client, NULL ) < 0)
{
dev_err( & pdev- > dev, "unable to get DMA channel./n" ) ;
ret = - EBUSY;
goto probe_free_irq_cd;
}
//从平台时钟队列中获取SDI的时钟源,在arch/arm/plat- s3c24xx/s3c2410-clock.c中有定义
host- > clk = clk_get( & pdev- > dev, "sdi" ) ;
if ( IS_ERR( host- > clk) )
{
dev_err( & pdev- > dev, "failed to find clock source./n" ) ;
ret = PTR_ERR( host- > clk) ;
host- > clk = NULL ;
goto probe_free_host;
}
//启动获取的时钟源
ret = clk_enable( host- > clk) ;
if ( ret)
{
dev_err( & pdev- > dev, "failed to enable clock source./n" ) ;
goto clk_free;
}
//通过SDI的时钟源获取CPU的 PCLK频率,这里为什么要获得CPU的PCLK频率呢,
//通过数据手册SDI控制器的方框图得知,SDI的时钟频率 (SDCLK)=PCLK/(Prescaler+1)
host- > clk_rate = clk_get_rate( host- > clk) ;
host- > clk_div = 1; //设置预分频值,即:Prescaler的值
//下面对mmc_host进行初始化
mmc- > ops = & s3cmci_ops; //SDI主机控制器操作结构体
mmc- > ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; //设置工作电压范围
mmc- > caps = MMC_CAP_4_BIT_DATA; //设置总线宽度为4位
mmc- > f_min = host- > clk_rate / ( host- > clk_div * 256) ; //设置最小工作频率
mmc- > f_max = host- > clk_rate / host- > clk_div; //设置最大工作频率
mmc- > max_blk_count = 4095;
mmc- > max_blk_size = 4095;
mmc- > max_req_size = 4095 * 512;
mmc- > max_seg_size = mmc- > max_req_size;
mmc- > max_phys_segs = 128;
mmc- > max_hw_segs = 128;
//Linux的通知链机制,实现到后面再讲
ret = s3cmci_cpufreq_register( host) ;
if ( ret)
{
dev_err( & pdev- > dev, "failed to register cpufreq/n" ) ;
goto free_dmabuf;
}
//将SDI host设备注册到系统中
ret = mmc_add_host( mmc) ;
if ( ret)
{
dev_err( & pdev- > dev, "failed to add mmc host./n" ) ;
goto free_cpufreq;
}
//将SDI host设备的数据赋值给系统平台设备
platform_set_drvdata( pdev, mmc) ;
return 0;
//以下是错误处理
free_cpufreq:
s3cmci_cpufreq_deregister( host) ;
free_dmabuf:
clk_disable( host- > clk) ;
clk_free:
clk_put( host- > clk) ;
probe_free_irq_cd:
if ( host- > irq_cd > = 0)
free_irq( host- > irq_cd, host) ;
probe_free_irq:
free_irq( host- > irq, host) ;
probe_iounmap:
iounmap( host- > base) ;
probe_free_mem_region:
release_mem_region( host- > mem- > start, RESSIZE( host- > mem) ) ;
probe_free_host:
mmc_free_host( mmc) ;
probe_out:
return ret;
}-
Linux 的通知链机制
-
//Linux的通知链机制,需要内核配置时的支持。
//这个通知链机制在MMC/SD卡驱动中应用的目的是,当CPU频率发生改变时,MMC/SD时钟频率也要改变。
//具体通知链机制的原理请看下一篇转载的文章。
# ifdef CONFIG_CPU_FREQ
static int s3cmci_cpufreq_transition( struct notifier_block * nb, unsigned long val, void * data)
{
struct s3cmci_host * host;
struct mmc_host * mmc;
unsigned long newclk;
unsigned long flags;
host = container_of( nb, struct s3cmci_host, freq_transition) ;
newclk = clk_get_rate( host- > clk) ;
mmc = host- > mmc;
if ( ( val = = CPUFREQ_PRECHANGE & & newclk > host- > clk_rate) | |
( val = = CPUFREQ_POSTCHANGE & & newclk < host- > clk_rate) ) {
spin_lock_irqsave( & mmc- > lock, flags) ;
host- > clk_rate = newclk;
if ( mmc- > ios . power_mode ! = MMC_POWER_OFF & &
mmc- > ios . clock ! = 0)
s3cmci_set_clk( host, & mmc- > ios ) ;
spin_unlock_irqrestore( & mmc- > lock, flags) ;
}
return 0;
}
static inline int s3cmci_cpufreq_register( struct s3cmci_host * host)
{
host- > freq_transition. notifier_call = s3cmci_cpufreq_transition;
return cpufreq_register_notifier( & host- > freq_transition, CPUFREQ_TRANSITION_NOTIFIER) ;
}
static inline void s3cmci_cpufreq_deregister( struct s3cmci_host * host)
{
cpufreq_unregister_notifier( & host- > freq_transition, CPUFREQ_TRANSITION_NOTIFIER) ;
}
# else //如果内核配置时没有选择此功能支持,则其实现为空即可
static inline int s3cmci_cpufreq_register( struct s3cmci_host * host)
{
return 0;
}
-
从 探测函数中可以看到,我们接下来要实现的功能就很清晰了。他们分别是:
a. s3cmci_ops SDI主机控制器操作接口函数功能;
b. s3cmci_irq_cd SDI的卡检测中断服务功能;
c. s3cmci_irq SDI的中断服务功能;
6. s3cmci_ops SDI主机控制器操作接口函数功能分析:
mmc_host_ops结构体定义了对host主机进行操作的各种方法,其定义在Core核心层的host.h中,也就是Core核心层对Host主机层提供的接口函数。这里各种方法的函数原型如下:static struct mmc_host_ops s3cmci_ops =
{
. request = s3cmci_request,//实现host的请求处理(即:命令和数据的发送和接收 )
. set_ios = s3cmci_set_ios,// 通 过核心层传递过来的 ios , 配置host寄存器( 使能时 钟、总线带宽等 )
. get_ro = s3cmci_get_ro,//通过读取GPIO端口来判断卡是否写有保护
. get_cd = s3cmci_card_present,//通过读取GPIO端口来判断卡是否存在
} ;
void ( * request) ( struct mmc_host * host, struct mmc_request * req) ;
void ( * set_ios) ( struct mmc_host * host, struct mmc_ios * ios ) ;
int ( * get_ro) ( struct mmc_host * host) ;
int ( * get_cd) ( struct mmc_host * host) ;
从 各函数原型上看,他们都将mmc_host结构体作为参数,所以我在刚开始的时候就说过mmc_host结构体是MMC/SD卡驱动中比较重要的数据结 构。 可以这样说,他是Core层与Host层进行数据交换的载体。那么,这些接口函数何时会被调用呢?答案可以在Core层的core.c和sd.c中找到, 我们可以看到如下部分代码:static void mmc_start_request( struct mmc_host * host, struct mmc_request * mrq)
{
. . . . . .
host- > ops- > request( host, mrq) ;//导致 s3cmci_request被调用
}
static inline void mmc_set_ios( struct mmc_host * host)
{
. . . . . .
host- > ops- > set_ios( host, ios ) ;//导致s3cmci_set_ios被调用
}
void mmc_rescan( struct work_struct * work)
{
. . . . . .//导致s3cmci_card_present被调用
if ( host- > ops- > get_cd & & host- > ops- > get_cd( host) = = 0)
goto out;
. . . . . .
}
static int mmc_sd_init_card( struct mmc_host * host, u32 ocr,
struct mmc_card * oldcard)
{
. . . . . .
/* Check if read-only switch is active.*/
if ( ! oldcard)
{ //导致s3cmci_get_ro被调用
if ( ! host- > ops- > get_ro | | host- > ops- > get_ro( host) < 0)
{
printk( KERN_WARNING "%s: host does not "
"support reading read-only "
"switch. assuming write-enable./n" ,
mmc_hostname( host) ) ;
}
else
{
if ( host- > ops- > get_ro( host) > 0)
mmc_card_set_readonly( card) ;
}
}
. . . . . .
}
好了,我们开始分析每个接口函数的具体实现吧,从简单的开始吧。 判断卡是否存在,如下代码:static int s3cmci_card_present( struct mmc_host * mmc)
{
//从mmc_host的对象中获取出s3cmci_host结构体的数据,在s3cmci_probe函数中进行关联的
struct s3cmci_host * host = mmc_priv( mmc) ;
struct s3c24xx_mci_pdata * pdata = host- > pdata;
int ret;
//判断有无设置卡检测引脚端口,引脚在s3cmci_probe函数中已设置
if ( pdata- > gpio_detect = = 0)
return - ENOSYS;
//从设置的卡检测引脚中读出当前的电平值,来判断卡是插入存在的还是被拔出不存在的
ret = s3c2410_gpio_getpin( pdata- > gpio_detect) ? 0 : 1;
return ret ^ pdata- > detect_invert;
}获取卡是否写有保护,其实实现跟卡检查类似,代码如下:
配置host寄存器的时钟和总线宽度,代码如下:static int s3cmci_get_ro( struct mmc_host * mmc)
{
//从mmc_host的对象中获取出s3cmci_host结构体的数据,在s3cmci_probe函数中进行关联的
struct s3cmci_host * host = mmc_priv( mmc) ;
struct s3c24xx_mci_pdata * pdata = host- > pdata;
int ret;
//判断有无设置卡写保护引脚端口,引脚在s3cmci_probe函数中已设置
if ( pdata- > gpio_wprotect = = 0)
return 0;
//从设置的卡写保护引脚中读出当前的电平值,来判断卡是否写有保护
ret = s3c2410_gpio_getpin( pdata- > gpio_wprotect) ;
if ( pdata- > wprotect_invert)
ret = ! ret;
return ret;
}static void s3cmci_set_ios( struct mmc_host * mmc, struct mmc_ios * ios )
{
//从mmc_host的对象中获取出s3cmci_host结构体的数据,在s3cmci_probe函数中进行关联的
struct s3cmci_host * host = mmc_priv( mmc) ;
u32 mci_con;
//读取SDI控制寄存器的值
mci_con = readl( host- > base + S3C2410_SDICON) ;
//ios结构体参数从Core层传递过来,根据不同的电源状态来配置SDI各寄存器
switch ( ios - > power_mode)
{
case MMC_POWER_ON:
case MMC_POWER_UP:
//根据开发板引脚连接情况配置SDI控制器的各信号线,包括:时钟线、命令线和四条数据线
s3c2410_gpio_cfgpin( S3C2410_GPE5, S3C2410_GPE5_SDCLK) ;
s3c2410_gpio_cfgpin( S3C2410_GPE6, S3C2410_GPE6_SDCMD) ;
s3c2410_gpio_cfgpin( S3C2410_GPE7, S3C2410_GPE7_SDDAT0) ;
s3c2410_gpio_cfgpin( S3C2410_GPE8, S3C2410_GPE8_SDDAT1) ;
s3c2410_gpio_cfgpin( S3C2410_GPE9, S3C2410_GPE9_SDDAT2) ;
s3c2410_gpio_cfgpin( S3C2410_GPE10, S3C2410_GPE10_SDDAT3) ;
if ( host- > pdata- > set_power)
host- > pdata- > set_power( ios - > power_mode, ios - > vdd) ;
break ;
case MMC_POWER_OFF:
default :
//如果电源状态为关闭或者默认情况下,关闭SDI的时钟信号
s3c2410_gpio_setpin( S3C2410_GPE5, 0) ;
s3c2410_gpio_cfgpin( S3C2410_GPE5, S3C2410_GPE5_OUTP) ;
//根据数据手册的SDICON寄存器位的介绍,此处是将整个sdmmc时钟复位
mci_con | = S3C2440_SDICON_SDRESET;
if ( host- > pdata- > set_power)
host- > pdata- > set_power( ios - > power_mode, ios - > vdd) ;
break ;
}
//设置SDI波特率预定标器寄存器以确定时钟,看其定义部分
s3cmci_set_clk( host, ios ) ;
//根据SDI当前的时钟频率来设置寄存器的使能时钟位
if ( ios - > clock )
mci_con | = S3C2410_SDICON_CLOCKTYPE;
else
mci_con & = ~ S3C2410_SDICON_CLOCKTYPE;
//将计算好的值写回SDI控制寄存器
writel( mci_con, host- > base + S3C2410_SDICON) ;
//下面只是一些调试信息,可以不要
if ( ( ios - > power_mode = = MMC_POWER_ON) | | ( ios - > power_mode = = MMC_POWER_UP) )
{
dbg( host, dbg_conf, "running at %lukHz (requested: %ukHz)./n" ,
host- > real_rate/ 1000, ios - > clock / 1000) ;
}
else
{
dbg( host, dbg_conf, "powered down./n" ) ;
}
//设置总线宽度
host- > bus_width = ios - > bus_width;
}
//设置SDI波特率预定标器寄存器以确定时钟
static void s3cmci_set_clk( struct s3cmci_host * host, struct mmc_ios * ios )
{
u32 mci_psc;
//根据SDI工作时钟频率范围来确定时钟预分频器值
for ( mci_psc = 0; mci_psc < 255; mci_psc+ + )
{
host- > real_rate = host- > clk_rate / ( host- > clk_div* ( mci_psc+ 1) ) ;
if ( host- > real_rate < = ios - > clock )
break ;
}
//根据数据手册描述,SDI波特率预定标器寄存器只有8个位,所以最大值为255
if ( mci_psc > 255)
mci_psc = 255;
host- > prescaler = mci_psc; //确定的预分频器值
//将预分频器值写于SDI波特率预定标器寄存器中
writel( host- > prescaler, host- > base + S3C2410_SDIPRE) ;
if ( ios - > clock = = 0)
host- > real_rate = 0;
}MMC/SD请求处理,这是Host驱动中比较重要的一部分。请求处理的整个流程请参考(一)中的流程图,他很好的描述了一个请求是怎样从Host层发出,通过Core层提交到Card层被块设备处理的。下面看代码:
static void s3cmci_request( struct mmc_host * mmc, struct mmc_request * mrq)
{
//从mmc_host的对象中获取出s3cmci_host结构体的数据,在s3cmci_probe函数中进行关联的
struct s3cmci_host * host = mmc_priv( mmc) ;
//s3cmci_host结构体定义的status主要是记录请求过程所处的阶段及状态,方便调试时使用
host- > status = "mmc request" ;
//请求处理主要包括MMC/SD命令和数据处理,所以定义cmd_is_stop来区分是哪种请求
host- > cmd_is_stop = 0;
//将Core层的mmc_request对象保存到Host层中以备使用
host- > mrq = mrq;
//在开始发出一个请求前先要检测一下卡是否还存在,否则提交到了块设备层而没有请求处理的对象发生错误
if ( s3cmci_card_present( mmc) = = 0)
{
dbg( host, dbg_err, "%s: no medium present/n" , __func__ ) ;
host- > mrq- > cmd- > error = - ENOMEDIUM;
mmc_request_done( mmc, mrq) ; //如果卡不存在则马上结束这次请求
}
else
{
s3cmci_send_request( mmc) ; //如果卡还存在则发出请求
}
}//发送请求
static void s3cmci_send_request( struct mmc_host * mmc)
{
//从mmc_host的对象中获取出s3cmci_host结构体的数据,在s3cmci_probe函数中进行关联的
struct s3cmci_host * host = mmc_priv( mmc) ;
//取出在s3cmci_request函数中保存的mmc_request对象以使用
struct mmc_request * mrq = host- > mrq;
//在s3cmci_request函数中设置的cmd_is_stop初始值为0,表示当前是命令请求
struct mmc_command * cmd = host- > cmd_is_stop ? mrq- > stop : mrq- > cmd;
//清空SDI命令状态寄存器、数据状态寄存器和FIFO状态寄存器
writel( 0xFFFFFFFF, host- > base + S3C2410_SDICMDSTAT) ;
writel( 0xFFFFFFFF, host- > base + S3C2410_SDIDSTA) ;
writel( 0xFFFFFFFF, host- > base + S3C2410_SDIFSTA) ;
//如果当前这次的请求是数据请求
if ( cmd- > data)
{
//进入数据请求处理设置,主要是数据控制寄存器的配置
int res = s3cmci_setup_data( host, cmd- > data) ;
if ( res)
{
//如果在数据请求设置中出现异常,则马上结束这次请求
dbg( host, dbg_err, "setup data error %d/n" , res) ;
cmd- > error = res;
cmd- > data- > error = res;
mmc_request_done( mmc, mrq) ;
return ;
}
//判断数据处理的方式是DAM还是FIFO,在s3cmci_probe函数中初始的是0,所以没有使用DMA的方式
if ( host- > dodma)
res = s3cmci_prepare_dma( host, cmd- > data) ;
else
res = s3cmci_prepare_pio( host, cmd- > data) ;
if ( res)
{
//如果请求处理数据失败则也要马上结束这次请求
dbg( host, dbg_err, "data prepare error %d/n" , res) ;
cmd- > error = res;
cmd- > data- > error = res;
mmc_request_done( mmc, mrq) ;
return ;
}
}
//否则这次请求是命令请求
s3cmci_send_command( host, cmd) ;
//还记得在s3cmci_probe中SDI未准备好是屏蔽了SD中断,所以这里就使能中断
enable_irq( host- > irq) ;
}http://www.linuxidc.com/Linux/2011-02/32646p6.htm
-