设备驱动中的mmc(kernel-4.7)

MMC的体系结构,其分为三层

        /dev下设备文件访问MMC/SD/SDIO
用户空间             |
--------------------|-----------------------------------------------------
内核空间            \ /
         MMC Card层(对应具体的设备驱动,如MMC/SD卡块设备驱动,SDIO UART)
                     |
                    \ /
          MMC core层(为上次设备驱动实现提供操作接口,和下层host注册提供机制)
                     |
                    \ /
           Host层(具体MMC/SD/SDIO控制器驱动层。如S3C2440 MMC/SD控制器驱动)
                     |
                    \ /
-----------------------------------------------------------------------------
                    硬件层 

MMC/SD模块中最重要的部分是Core核心层,他提供了一系列的接口函数,对上提供了将主机驱动注册到系统,给应用程序提供设备访问接口,对下提供了对主机控制器控制的方法及块设备请求的支持。对于主机控制器的操作就是对相关寄存器进行读写,而对于MMC/SD设备的请求处理则比较复杂。
在主机驱动层中的一个请求处理的提交转换过程:
这里写图片描述

命令、数据发送流程如下图:
这里写图片描述

分析MMC/SD卡设备驱动程序

Mini2440 MMC/SD硬件接口电路原理图如下:
这里写图片描述

从Mini2440原理图可以看出,Mini2440 SDI使用的GPE7-GPE10作为4根数据信号线,使用GPE6作为命令信号线,使用GPE5作为时钟信号线。另外,使用GPG8的外部中断功能来作SD卡的插拨检测,使用GPH8来判断SD卡是否有写保护。

在arch/arm/mach-s3c24xx/mach-mini2440.c文件中,进行设备注册:

static struct platform_device *mini2440_devices[] __initdata = {
    &s3c_device_ohci,
    &s3c_device_wdt,
    &s3c_device_i2c0,
    &s3c_device_rtc,
    &s3c_device_usbgadget,
    &mini2440_device_eth,
    &mini2440_led1,
    &mini2440_led2,
    &mini2440_led3,
    &mini2440_led4,
    &mini2440_button_device,
    &s3c_device_nand,
    &s3c_device_sdi,        //Mini2440的SDI控制器
    &s3c_device_iis,
    &uda1340_codec,
    &mini2440_audio,
};

s3c_device_sdi定义在arch/arm/plat-samsung/devs.c文件中:


struct platform_device s3c_device_sdi = {
    .name       = "s3c2410-sdi",
    .id     = -1,
    .num_resources  = ARRAY_SIZE(s3c_sdi_resource),
    .resource   = s3c_sdi_resource,
};

其中,s3c_sdi_resource定义在arch/arm/plat-samsung/devs.c文件中:

static struct resource s3c_sdi_resource[] = {
    [0] = DEFINE_RES_MEM(S3C24XX_PA_SDI, S3C24XX_SZ_SDI),
    [1] = DEFINE_RES_IRQ(IRQ_SDI),
};

S3C24XX_PA_SDI定义在arch/arm/mach-s3c24xx/include/mach/map.h文件中:

#define S3C24XX_PA_SDI      S3C2410_PA_SDI

S3C2410_PA_SDI定义在arch/arm/mach-s3c24xx/include/mach/map.h文件中:

#define S3C24XX_SZ_SDI      SZ_1M 

S3C2410_PA_SDI定义在arch/arm/mach-s3c24xx/include/mach/map.h文件中:

/* SDI */
#define S3C2410_PA_SDI     (0x5A000000) 

0x5A000000是S3C2440 SDICON寄存器的地址。

IRQ_SDI定义在arch/arm/mach-s3c24xx/include/mach/irqs.h文件中:

#define S3C2410_CPUIRQ_OFFSET    (16)

#define S3C2410_IRQ(x) ((x) + S3C2410_CPUIRQ_OFFSET)

.....
#define IRQ_SDI        S3C2410_IRQ(21) 

在Mini2440的平台初始化中将调用 platform_add_devicesmini2440_devices,中的设备资源全部注册为平台设备,也包括s3c_device_sdi

Mini2440的SDI驱动定义在drivers/mmc/host/s3cmci.c文件中:


static struct platform_driver s3cmci_driver = {
    .driver = {
        .name   = "s3c-sdi",
    },
    .id_table   = s3cmci_driver_ids,
    .probe      = s3cmci_probe,
    .remove     = s3cmci_remove,
    .shutdown   = s3cmci_shutdown,
};

s3cmci_driver_ids定义在drivers/mmc/host/s3cmci.c文件中:


static const struct platform_device_id s3cmci_driver_ids[] = {
    {
        .name   = "s3c2410-sdi",
        .driver_data    = 0,
    }, {
        .name   = "s3c2412-sdi",
        .driver_data    = 1,
    }, {
        .name   = "s3c2440-sdi",
        .driver_data    = 1,
    },
    { }
};

注册s3cmci_driver的过程中,会触发s3cmci_probe函数的执行。
s3cmci_probe函数,它定义在drivers/mmc/host/s3cmci.c文件中,其内容如下:


static int s3cmci_probe(struct platform_device *pdev)
{
    struct s3cmci_host *host;
    struct mmc_host *mmc;
    int ret;
    int is2440;
    int i;

    is2440 = platform_get_device_id(pdev)->driver_data; //由s3cmci_driver_ids,可知driver_data=1

    mmc = mmc_alloc_host(sizeof(struct s3cmci_host), &pdev->dev);//初始化host,被分配mmc
    if (!mmc) {
        ret = -ENOMEM;
        goto probe_out;
    }

    for (i = S3C2410_GPE(5); i <= S3C2410_GPE(10); i++) {
        ret = gpio_request(i, dev_name(&pdev->dev));  //请获取GPE5-GPE10,从Mini2440原理图可以看出,Mini2440SDI使用的GPE7-GPE10作为4根数据信号线,使用GPE6作为命令信号线,使用GPE5作为时钟信号线。另外,使用GPG8的外部中断功能来作SD卡的插拨检测,使用GPH8来判断SD卡是否有写保护。
        if (ret) {
            dev_err(&pdev->dev, "failed to get gpio %d\n", i);

            for (i--; i >= S3C2410_GPE(5); i--)
                gpio_free(i);

            goto probe_free_host;
        }
    }

    host = mmc_priv(mmc);  //取得s3cmci_host指针变量host
    host->mmc   = mmc;
    host->pdev  = pdev;
    host->is2440    = is2440;

    host->pdata = pdev->dev.platform_data;
    if (!host->pdata) {
        pdev->dev.platform_data = &s3cmci_def_pdata;
        host->pdata = &s3cmci_def_pdata;
    }

    spin_lock_init(&host->complete_lock);
    tasklet_init(&host->pio_tasklet, pio_tasklet, (unsigned long) host);

    if (is2440) {
        host->sdiimsk   = S3C2440_SDIIMSK;
        host->sdidata   = S3C2440_SDIDATA;
        host->clk_div   = 1;
    } else {
        host->sdiimsk   = S3C2410_SDIIMSK;
        host->sdidata   = S3C2410_SDIDATA;
        host->clk_div   = 2;
    }

    host->complete_what     = COMPLETION_NONE;
    host->pio_active    = XFER_NONE;

    host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);//取得IORESOURCE_MEM类型资源
    if (!host->mem) {
        dev_err(&pdev->dev,
            "failed to get io memory region resource.\n");

        ret = -ENOENT;
        goto probe_free_gpio;
    }

    host->mem = request_mem_region(host->mem->start,
                       resource_size(host->mem), pdev->name);

    if (!host->mem) {
        dev_err(&pdev->dev, "failed to request io memory region.\n");
        ret = -ENOENT;
        goto probe_free_gpio;
    }

    host->base = ioremap(host->mem->start, resource_size(host->mem));
    if (!host->base) {
        dev_err(&pdev->dev, "failed to ioremap() io memory region.\n");
        ret = -EINVAL;
        goto probe_free_mem_region;
    }

    host->irq = platform_get_irq(pdev, 0);//
    if (host->irq == 0) {
        dev_err(&pdev->dev, "failed to get interrupt resource.\n");
        ret = -EINVAL;
        goto probe_iounmap;
    }

    if (request_irq(host->irq, s3cmci_irq, 0, DRIVER_NAME, host)) {
  //申请中断,中断处理函数是s3cmci_irq
        dev_err(&pdev->dev, "failed to request mci interrupt.\n");
        ret = -ENOENT;
        goto probe_iounmap;
    }

    /* We get spurious interrupts even when we have set the IMSK
     * register to ignore everything, so use disable_irq() to make
     * ensure we don't lock the system with un-serviceable requests. */

    disable_irq(host->irq);
    host->irq_state = false;

    if (!host->pdata->no_detect) {
  //处理SD卡探测
        ret = gpio_request(host->pdata->gpio_detect, "s3cmci detect");
        if (ret) {
            dev_err(&pdev->dev, "failed to get detect gpio\n");
            goto probe_free_irq;
        }

        host->irq_cd = gpio_to_irq(host->pdata->gpio_detect);

        if (host->irq_cd >= 0) { 
            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_gpio_cd;
            }
        } else {
            dev_warn(&pdev->dev,
                 "host detect has no irq available\n");
            gpio_direction_input(host->pdata->gpio_detect);
        }
    } else
        host->irq_cd = -1;

    if (!host->pdata->no_wprotect) {
  //处理SD卡写保护
        ret = gpio_request(host->pdata->gpio_wprotect, "s3cmci wp");
        if (ret) {
            dev_err(&pdev->dev, "failed to get writeprotect\n");
            goto probe_free_irq_cd;
        }

        gpio_direction_input(host->pdata->gpio_wprotect);
    }

    /* depending on the dma state, get a dma channel to use. */

    if (s3cmci_host_usedma(host)) { //处理DMA
        dma_cap_mask_t mask;

        dma_cap_zero(mask);
        dma_cap_set(DMA_SLAVE, mask);

        host->dma = dma_request_slave_channel_compat(mask,
            s3c24xx_dma_filter, (
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值