Chinaunix首页 | 论坛 | 博客
  • 博客访问: 41543
  • 博文数量: 21
  • 博客积分: 840
  • 博客等级: 准尉
  • 技术积分: 225
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-24 00:24
文章分类
文章存档

2010年(21)

我的朋友

分类: 嵌入式

2010-06-08 21:39:43

1: mmcsd的芯片适配层,暂且这么说吧。

static int mxcmci_probe(struct platform_device *pdev)
{
    struct mxc_mmc_platform_data *mmc_plat = pdev->dev.platform_data;
    struct mmc_host *mmc;
    struct mxcmci_host *host = NULL;
    int card_gpio_status;
    int ret = -ENODEV;

    if (!mmc_plat) {
        return -EINVAL;
    }

    mmc = mmc_alloc_host(sizeof(struct mxcmci_host), &pdev->dev);
    if (!mmc) {
        return -ENOMEM;
    }
    platform_set_drvdata(pdev, mmc);

    mmc->ops = &mxcmci_ops;
    mmc->ocr_avail = mmc_plat->ocr_mask;

    /* Hack to work with LP1070 */
    mmc->ocr_avail |= MMC_VDD_31_32;

    mmc->max_phys_segs = NR_SG;
    mmc->caps = MMC_CAP_4_BIT_DATA;

    host = mmc_priv(mmc);
    host->mmc = mmc;
    host->dma = -1;
    host->dma_dir = DMA_NONE;
    host->id = pdev->id;
    host->mxc_mmc_suspend_flag = 0;
    host->sdio_set_wake_enable = 0;
    host->mode = -1;
    host->plat_data = mmc_plat;
    if (!host->plat_data) {
        ret = -EINVAL;
        goto out;
    }

    host->clk = clk_get(&pdev->dev, "sdhc_clk");
    clk_enable(host->clk);

    mmc->f_min = mmc_plat->min_clk;
    mmc->f_max = mmc_plat->max_clk;
    pr_debug("SDHC:%d clock:%lu\n", pdev->id, clk_get_rate(host->clk));

    spin_lock_init(&host->lock);
    host->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    if (!host->res) {
        ret = -ENOMEM;
        goto out;
    }

    if (!request_mem_region(host->res->start,
                host->res->end -
                host->res->start + 1, pdev->name)) {
        printk(KERN_ERR "request_mem_region failed\n");
        ret = -ENOMEM;
        goto out;
    }
    host->base = (void *)IO_ADDRESS(host->res->start);
    if (!host->base) {
        ret = -ENOMEM;
        goto out1;
    }
printk(KERN_INFO "%s--irq:%d,,id:0x%x\n",__FUNCTION__,host->irq,pdev->id);
    host->irq = platform_get_irq(pdev, 0);//irq 11 INT_SDHC1

    if (!host->irq) {
        ret = -ENOMEM;
        goto out1;
    }
#ifdef CONFIG_MACH_MX27_MDK27V0
#ifdef CONFIG_MACH_MX27_TUOSI
#else
    if (pdev->id == 1) {
        goto skip_detect_irq;
    }
#endif
#endif

    host->detect_irq = platform_get_irq(pdev, 1);//gpio_irq: SSI3_RXDAT

printk(KERN_INFO"%s--dec irq:0x%x\n",__FUNCTION__,host->detect_irq);
    if (!host->detect_irq) {
        goto out1;
    }

    do {
        card_gpio_status = host->plat_data->status(host->mmc->dev);
        if (card_gpio_status) {
            set_irq_type(host->detect_irq, IRQT_FALLING);
        } else {
            set_irq_type(host->detect_irq, IRQT_RISING);
        }
    } while (card_gpio_status != host->plat_data->status(host->mmc->dev));

    ret =
     request_irq(host->detect_irq, mxcmci_gpio_irq, 0, pdev->name, host);
    if (ret) {
        goto out1;
    }

      skip_detect_irq:
    mxcmci_softreset(host);

    if (__raw_readl(host->base + MMC_REV_NO) != SDHC_REV_NO) {
        printk(KERN_ERR "%s: wrong rev.no. 0x%08x. aborting.\n",
         pdev->name, MMC_REV_NO);
        goto out3;
    }
    __raw_writel(READ_TO_VALUE, host->base + MMC_READ_TO);

    __raw_writel(INT_CNTR_END_CMD_RES, host->base + MMC_INT_CNTR);

    ret = request_irq(host->irq, mxcmci_irq, 0, pdev->name, host);
    if (ret) {
        goto out3;
    }

    host->mmc->sdio_irq = platform_get_irq(pdev, 2);
    if (host->mmc->sdio_irq) {
        mmc->caps |= MMC_CAP_SDIO;
#ifdef CONFIG_MACH_MX27_MDK27V0
        sdhc_setup_irq(host->mmc->sdio_irq, host->mmc);
#else
        set_irq_chip(host->mmc->sdio_irq, &mxc_sdio_irq_chip);
        set_irq_chipdata(host->mmc->sdio_irq, host->mmc);
        set_irq_handler(host->mmc->sdio_irq, handle_level_irq);
        set_irq_flags(host->mmc->sdio_irq, IRQF_VALID);
#endif
    }

    gpio_sdhc_active(pdev->id);

    if ((ret = mmc_add_host(mmc)) < 0) {
        goto out4;
    }

#ifdef CONFIG_MACH_MX27_MDK27V0
    if (pdev->id >= 0 && pdev->id < 2) {
        mxc_mci_hosts[pdev->id] = host;
    }
#endif

    printk(KERN_INFO "%s-%d found\n", pdev->name, pdev->id);

    return 0;

      out4:
    gpio_sdhc_inactive(pdev->id);
    free_irq(host->irq, host);
      out3:
    free_irq(host->detect_irq, host);
    pr_debug("%s: Error in initializing....", pdev->name);
      out1:
    release_mem_region(pdev->resource[0].start,
             pdev->resource[0].end - pdev->resource[0].start + 1);
      out:
    clk_disable(host->clk);
    mmc_free_host(mmc);
    platform_set_drvdata(pdev, NULL);
    return ret;
}

首先调用mmc_alloc_host去申请一个struct mmc_host *mmc,即是:
struct mmc_host {
    struct device        *dev;
    struct class_device    class_dev;
    int            index;
    const struct mmc_host_ops *ops;
    unsigned int        f_min;
    unsigned int        f_max;
    u32            ocr_avail;

    unsigned long        caps;        /* Host capabilities */

#define MMC_CAP_4_BIT_DATA    (1 << 0)    /* Can the host do 4 bit transfers */
#define MMC_CAP_MULTIWRITE    (1 << 1)    /* Can accurately report bytes sent to card on error */
#define MMC_CAP_BYTEBLOCK    (1 << 2)    /* Can do non-log2 block sizes */
#define MMC_CAP_SDIO        (1 << 3)    /* Can the host support SDIO */

    unsigned int        sdio_irq;

    /* 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        max_sectors;    /* see blk_queue_max_sectors */
    unsigned short        unused;

    /* private data */
    struct mmc_ios        ios;        /* current io bus settings */
    u32            ocr;        /* the current OCR setting */

    unsigned int        mode;        /* current card mode of host */
#define MMC_MODE_MMC        0
#define MMC_MODE_SD        1
#define MMC_MODE_SDIO        2

    struct list_head    cards;        /* devices attached to this host */

    wait_queue_head_t    wq;
    spinlock_t        lock;        /* card_busy lock */
    struct mmc_card        *card_busy;    /* the MMC card claiming host */
    struct mmc_card        *card_selected;    /* the selected MMC card */

    struct work_struct    detect;

    unsigned long        private[0] ____cacheline_aligned;
};
这个结构跟mmc_alloc_host函数都是由MMC核心层mmc.c文件提供的,注意的是,申请的时候还多申请了一个结构体空间struct mxcmci_host *host,即为struct mmc_host的private所指向的地址。接下来看一下这个申请函数:
struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{
    struct mmc_host *host;

    host = mmc_alloc_host_sysfs(extra, dev);
    if (host) {
        spin_lock_init(&host->lock);
        init_waitqueue_head(&host->wq);
        INIT_LIST_HEAD(&host->cards);
        INIT_WORK(&host->detect, mmc_rescan, host);

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

    return host;
}
看到第一行,又调用mmc_alloc_host_sysfs申请空间,这个函数由mmc_sysfs.c文件提供:
struct mmc_host *mmc_alloc_host_sysfs(int extra, struct device *dev)
{
    struct mmc_host *host;

    host = kmalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
    if (host) {
        memset(host, 0, sizeof(struct mmc_host) + extra);

        host->dev = dev;
        host->class_dev.dev = host->dev;
        host->class_dev.class = &mmc_host_class;
        class_device_initialize(&host->class_dev);
    }

    return host;
}
可以看到,除申请空间外,它还初始化一个class,是供sys目录使用的。所以退出该函数,我们回到mmc_alloc_host中,接下来初始化host->lock,等待队列host->wq,host所要管理的card链表host->cards,工作队列host->detect和工作函数mmc_rescan,具体这个mmc_rescan函数以后再解。接下来看,就是设置几个host specific block data,这几个变量做什么用的还不清楚。然后函数返回host。
回到mxcmci_probe,下面调用platform_set_drvdata将pdev->dev->driver_data指向mmc即刚才申请到的host结构体。下一行初始化host的ops为mxcmci_ops,即:
    static struct mmc_host_ops mxcmci_ops = {
        .request = mxcmci_request,
        .set_ios = mxcmci_set_ios,
    #ifdef CONFIG_MACH_MX27_MDK27V0
        .get_ro = mxcmci_get_ro
    #endif
    };
    前面两个函数应该是与SD的协议有关的实现,最后一个是通过读取GPIO来获得SD卡是否有是只读的(是否有写保护)
接下来初始化mmc->ocr_avail,这个参数好像是与SD的供电有关的,下面的一些max_phys_regs,caps,等等都还不清楚,到plat_data的时候是指向mmc_plat即是platform_data,在devices.c中有:
    static struct platform_device mxcsdhc1_device = {
        .name = "mxcmci",
        .id = 0,
        .dev = {
            .release = mxc_nop_release,
            .platform_data = &mmc_data,
            },
        .num_resources = ARRAY_SIZE(mxcsdhc1_resources),
        .resource = mxcsdhc1_resources,
    };
    其中,我们可以看到,platform_data指向mmc_data,即:
    static struct mxc_mmc_platform_data mmc_data = {
    .ocr_mask = MMC_VDD_27_28 | MMC_VDD_28_29 | MMC_VDD_29_30,
    .min_clk = 150000,
    .max_clk = 25000000,
    .card_inserted_state = 0,
    .status = sdhc_get_card_det_status,
    };
    sdhc_get_card_det_status通过查询GOIO返回SD卡的状态,1为remove,0为insert。
接下来设置CLK,初始化mxcmmc_host的host->lock,下面调用platform_get_resource获得MX27关于SD卡的寄存器段,这个是用物理地址描述的,接着调用request_mem_region申请注册这段IO内存的使用权,然后调用IO_ADDRESS转换该寄存器段的首地址,赋给host->base,这个为虚拟地址,后面的驱动就可以直接使用这个虚拟地址读写寄存器了。下面再调用platform_get_irq获得SDHC给ARM端的中断号,为宏INT_SDHC1,值为11。下面来看pdev->id,因为我们系统中只用一个SD接口,所以在platform_data初始化的时候设置该值为0,所以这里就不会goto skip_detect_irq了。下面再调用platform_get_irq获得host->detect_irq中断号,这里需要说明的是:
    detect_irq中断其实就是一个GPIO中断,接在SD卡座的CD_SW端,当SD卡未考入时为高电平,当插入时为低电平,拨出时恢复为高电平,我们系统里设置GPIO的中断类型为边沿触发,所以检测插入时为上升沿,下降沿时为拨出。
下面的do while即是根据SD卡的状态来设置这个中断的触发沿。接下来request_irq,不用说,是申请中断处理函数--mxcmci_gpio_irq,这个中断处理函数以后再说。下面就是调用mxcmci_softreset:
    static void mxcmci_softreset(struct mxcmci_host *host)
    {
        /* reset sequence */
        __raw_writel(0x8, host->base + MMC_STR_STP_CLK);
        __raw_writel(0x9, host->base + MMC_STR_STP_CLK);
        __raw_writel(0x1, host->base + MMC_STR_STP_CLK);
        __raw_writel(0x1, host->base + MMC_STR_STP_CLK);
        __raw_writel(0x1, host->base + MMC_STR_STP_CLK);
        __raw_writel(0x1, host->base + MMC_STR_STP_CLK);
        __raw_writel(0x1, host->base + MMC_STR_STP_CLK);
        __raw_writel(0x1, host->base + MMC_STR_STP_CLK);
        __raw_writel(0x1, host->base + MMC_STR_STP_CLK);
        __raw_writel(0x1, host->base + MMC_STR_STP_CLK);
        __raw_writel(0x3f, host->base + MMC_CLK_RATE);
    
        __raw_writel(0xff, host->base + MMC_RES_TO);
        __raw_writel(512, host->base + MMC_BLK_LEN);
        __raw_writel(1, host->base + MMC_NOB);
    }
    该函数就是设置MX27的SD卡相关的寄存器,复位SD控制器。
接下来的三个__raw_writel也都是设置SD控制器,下面再调用request_irq申请MMC的中断,处理函数为mxcmci_irq接下来还是platform_get_irq,获得host->mmc->sdio_irq,这个就看不懂了,这个中断是用来做什么的呢???
下面调用gpio_sdhc_active设置GPIO用作MMC,终于差不多结束了,调用mmc_add_host,为mmc.c里的一个函数:
int mmc_add_host(struct mmc_host *host)
{
    int ret;

    ret = mmc_add_host_sysfs(host);
    if (ret == 0) {
        mmc_power_off(host);
        mmc_detect_change(host, 0);
    }

    return ret;
}
    该函数创建sys文件里mmc对应的目录,成功后调用mmc_power_off及mmc_detect_change,其中:
        static void mmc_power_off(struct mmc_host *host)
        {
            host->ios.clock = 0;
            host->ios.vdd = 0;
            host->ios.bus_mode = MMC_BUSMODE_OPENDRAIN;
            host->ios.chip_select = MMC_CS_DONTCARE;
            host->ios.power_mode = MMC_POWER_OFF;
            host->ios.bus_width = MMC_BUS_WIDTH_1;
            mmc_set_ios(host);
        }
        函数调用了mmc_set_ios:
            static inline void mmc_set_ios(struct mmc_host *host)
            {
                struct mmc_ios *ios = &host->ios;
            
                pr_debug("%s: clock %uHz busmode %u powermode %u cs %u Vdd %u width %u\n",
                     mmc_hostname(host), ios->clock, ios->bus_mode,
                     ios->power_mode, ios->chip_select, ios->vdd,
                     ios->bus_width);
                
                host->ops->set_ios(host, ios);
            }
            ops->set_ios即是我们上面所初始化的,platform_data里的一个东东。
    而:
        void mmc_detect_change(struct mmc_host *host, unsigned long delay)
        {
            if (delay)
                mmc_schedule_delayed_work(&host->detect, delay);
            else
                mmc_schedule_work(&host->detect);
        }
        应该是唤醒工作队列中的mmc_rescan函数。


阅读(1217) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~