首次出关于SD方面的驱动文章,网上也很少有这方面的文章,所以我分享几篇关于SD的文章来引导刚出道的朋友们,咱们一起努力!
大家都说MTK简单,但我个人做过高通平台一年时间,然后转而做MTK平台,感觉还是能学到东西的,代码并不比所谓的高通简单,只是它做的好,封装的好,所以做这块驱动可能开发者做的事情没高通多,所以大家觉得容易,学不到东西。但只要自己对技术执着,深入到架构的实现,还是能学到很多的。我要让大家有这样一个思想,我们做MTK平台的不比做其他任何平台的差。
一. SD卡的基本知识:
SD卡有9个pin脚(micro-SD为8个,少一个接地pin脚),如图所示,
SD的数据传输方式有两种,普通SD模式和SPI模式,以SD模式为例,9个pin脚分别是VDD,VSS,CLK,以及我们需要关注的一根指令线CMD,4根数据线DAT0~DAT3。
分类:关于SD驱动的基础知识:
MMC/SD介绍及SDI主机控制器
首先我们来理清几个概念:
1.MMC:(Multi Media Card)由西门子公司和首推CF的SanDisk于1997年推出的多媒体记忆卡标准。
2.SD:(Secure Digital Memory Card)由日本松下、东芝及美国SanDisk公司于1999年8月共同开发研制的新一代记忆卡标准,已完全兼容MMC标准。
3.SDIO:(Secure Digital Input and Output Card)安全数字输入输出卡。SDIO是在SD标 准上定义了一种外设接口,通过SD的I/O接脚来连接外围设备,并且通过SD上的 I/O数据接位与这些外围设备进行数据传输。是目前较热门的技术,如下图中的一些设备:GPS、相机、Wi-Fi、调频广播、条形码读卡器、蓝牙等。
涉及到的文件有:mediatek/platform/mt6573/kernel/drivers/mmc-host/sd.c
mediatek/platform/mt6573/kernel/drivers/mmc-host/mt6573_sdc.c
下面就整个驱动的流程过一下:
系统起来的时候执行 static int __init mt6573_sd_init(void) 在这个函数里最重要的是执行platform_driver_register(&mt6573_sd_driver),即注册到内核的虚拟总线上,注册的原则是把驱动mt6573_sd_driver各参数进行初始化。
下面进入变量mt6573_sd_driver各成员的初始化。其中最重要的成员是mt6573_sd_probe的执行。当在虚拟platform总线上driver和device的名字"mt6573-sd"相匹配时即执行probe函数。
下面先看看文件mt6573_sdc.c中的static struct platform_device
mt6573_device_sd[] = { { .name = "mt6573-sd", .id = 0, .num_resources = ARRAY_SIZE(mt6573_resource_sd0), .resource = mt6573_resource_sd0, .dev = { .platform_data = &mt6573_sd0_hw, }, },
从这个结构体可以得出platform_device和platform_driver的name是相同的,所以会遍历到执行probe函数,这个结构体中有个重要的参数mt6573_sd0_hw,这个成员即是SD卡的初始状态值:
struct mt6573_sd_host_hw mt6573_sd0_hw = { .clk_src = MSDC_CLKSRC_98MHZ, .cmd_edge = EDGE_RISING, .data_edge = EDGE_RISING, .cmd_odc = MSDC_ODC_8MA, .data_odc = MSDC_ODC_8MA, .cmd_slew_rate = MSDC_ODC_SLEW_SLOW, .data_slew_rate = MSDC_ODC_SLEW_SLOW, .cmd_pull_res = MSDC_PULL_RES_23K, .dat_pull_res = MSDC_PULL_RES_23K, .clk_pull_res = MSDC_PULL_RES_23K, .rst_wp_pull_res= MSDC_PULL_RES_23K, .data_pins = 4, .data_offset = 0, .flags = MSDC_SYS_SUSPEND | MSDC_HIGHSPEED | MSDC_CD_PIN_EN, };
这个相当于SD卡的私有数据。
来分析下mt6573_sd_probe(struct platform_device *pdev)
hw = (struct mt6573_sd_host_hw*)pdev->dev.platform_data; //这个函数相当于hw =mt6573_sd0_hw 红色字体的变量是等价的 reg = platform_get_resource(pdev, IORESOURCE_MEM, 0); //申请驱动的内存 dma = platform_get_resource(pdev, IORESOURCE_DMA, 0); //申请驱动的DMA空间 irq = platform_get_irq(pdev, 0); //中断申请 cirq = platform_get_irq(pdev, 1); //插卡外部中断申请 mmc->ops = &mt6573_sd_ops; //SD卡的处理函数,这个也是重点,等下再进入分析 mmc->f_min = HOST_MIN_SCLK; mmc->f_max = HOST_MAX_SCLK; //SD卡的工作时钟 ................ tasklet_init(&host->card_tasklet, mt6573_sd_tasklet_card, (ulong)host); tasklet_init(&host->fifo_tasklet, mt6573_sd_tasklet_fifo, (ulong)host); tasklet_init(&host->dma_tasklet, mt6573_sd_tasklet_dma, (ulong)host); 这三个函数是中断处理下半部分别是处理识别卡/卡传输数据的buffer/卡传输数据的DMA通道。 mt6573_sd_init_hw(host, dma); //具体SD卡的硬件寄存器参数的设置 if (hw->flags & MSDC_CD_PIN_EN) { if (hw->request_cd_eirq) { hw->request_cd_eirq(mt6573_sd_cd_eirq, (void*)host); } else { mt65xx_irq_set_sens(cirq, MT65xx_EDGE_SENSITIVE); ret = request_irq((unsigned int)cirq, (irq_handler_t)mt6573_sd_cd_irq, 0, DRV_NAME, host); if (ret) goto free_irq; } }
这一段是热插拔识别SD卡的重要函数,做热插拔这里必须实现。
platform_set_drvdata(pdev, mmc); //把mmc的数据挂到pdev私有数据下 ret = mmc_add_host(mmc); //把mmc加载到主控制器队列里面去 ret = misc_register(&msdc_em_dev[host->id]); //建立SD卡的另外ops的设置,等下也做分析 static struct miscdevice msdc_em_dev[] = { { .minor = MISC_DYNAMIC_MINOR, .name = "mt6573-sd0", .fops = &msdc_em_fops, }, 从中可以看出主要是为了处理msdc_em_fops,点进去, static struct file_operations msdc_em_fops = { .owner = THIS_MODULE, .unlocked_ioctl = mt6573_sd_ioctl, .open = mt6573_sd_open, };
即分别实现了ioctl 和 open。
刚说要分析mt6573_sd_ops,点进去看,
static struct mmc_host_ops mt6573_sd_ops = { .request = mt6573_sd_request, //SD卡实现通信的请求,这个函数很重要,等下再分析 .set_ios = mt6573_sd_set_ios, //SD卡的时钟,电压,数据通道等设置 .get_ro = mt6573_sd_card_readonly, //实现SD卡的只读形式 .get_cd = mt6573_sd_card_inserted, //实现SD卡插入函数 .enable_sdio_irq = mt6573_sd_enable_sdio_irq, //SD卡中断的使能 };
即分别实现了上述各函数。
重点来看看函数:
static void mt6573_sd_request(struct mmc_host *mmc, struct mmc_request *mrq) { struct mt6573_sd_host *host = mmc_priv(mmc); //host为mmc的私有值 if (!is_card_present(host) || host->power_mode == MMC_POWER_OFF) { .......... } //判断又没SD卡插入以及SD电源开了没 if ((host->id ==0) || (host->id == 1)) { MSG(OPS, "Enable SD host/card working ability!\n"); clr_device_working_ability(clock_id[host->id], SLOW_IDLE_STATE); clr_device_working_ability(clock_id[host->id], DEEP_IDLE_STATE); } //设置host时钟,card的时钟保持工作状态 msdc_set_bksz(data->blksz); //设置SD卡的block数据大小 msdc_clr_fifo(); //传输之前先清空fifo缓冲区的数据 if (dma) { //判断用DMA方式还是FIFO方式来传输数据 msdc_dma_on(); msdc_fifo_irq_off(); } else { msdc_dma_off(); msdc_fifo_irq_on(); }
if (mt6573_sd_send_command(host, cmd, polling, CMD_TIMEOUT) != 0)
这个函数是向SD卡发送指令,进入这个函数是一系列SD卡的指令分类处理,具体参照SD的手册看这部分代码,我个人就没怎么仔细研究啦。
if (cmd->opcode == SD_IO_RW_EXTENDED) { if (cmd->arg & 0x08000000) { /* SDIO workaround for CMD53 multiple block transfer */ if (data->blocks > 1) { struct mmc_command abort; memset(&abort, 0, sizeof(struct mmc_command)); abort.opcode = SD_IO_RW_DIRECT; abort.arg = 0x80000000; /* write */ abort.arg |= 0 << 28; /* function 0 */ abort.arg |= SDIO_CCCR_ABORT << 9; /* address */ abort.arg |= 0; /* abort function 0 */ abort.flags = (unsigned int)-1; (void)mt6573_sd_send_command(host, &abort, 1, CMD_TIMEOUT); } } else { /* SDIO workaround for CMD53 multiple byte transfer, which * is not 4 byte-alignment */ if (data->blksz % 4) { /* The delay is required and tunable. The delay time must * be not too small. Currently, it is tuned to 25us.(CHECKME) */ udelay(25); msdc_reset(); } }
这一段主要是读写的函数了,是否工作在cmd53模式下。
这样整个request函数分析完了,整体感觉就是先设置SD参数,然后向SD发指令,等待SD应答。应答后以何种方式来传送数据,数据是怎样传送滴。等等。
下面分享一篇关于SD卡驱动的文章,这文章还是非常的不错!
http://www.linuxidc.com/Linux/2011-02/32646.htm