Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1152194
  • 博文数量: 241
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 2279
  • 用 户 组: 普通用户
  • 注册时间: 2012-11-27 19:53
个人简介

JustForFun

文章分类

全部博文(241)

文章存档

2023年(8)

2022年(2)

2021年(3)

2020年(30)

2019年(11)

2018年(27)

2017年(54)

2016年(83)

2015年(23)

我的朋友

分类: LINUX

2016-11-07 14:58:49

//////////////////////////////////////////////////////////////////////////////////////////////////////
图片
///
第一条线    mmc子系统核心初始化
         drivers/mmc/core/core.c
//子系统初始化 subsys_initcall(mmc_init);
第二条线 mmc控制器平台驱动注册

   
      
module_init(sdhci_drv_init);
第三条线  磁盘设备驱动注册
drivers/mmc/card/block.c
module_init(mmc_blk_init);
////////////////////////////////////////////////

图片

图片


/////////////////////////////////////////////////////////////////////////////////////////////////////////////

static int __init sdhci_drv_init(void)
{
    printk(KERN_INFO DRIVER_NAME
        ": Secure Digital Host Controller Interface driver\n");
    printk(KERN_INFO DRIVER_NAME ": Copyright(c) Pierre Ossman\n");

    return 0;
}
module_init(sdhci_drv_init);
///////////////////////////

的sdhci_s3c_probe函数调用sdhci_add_host
 int sdhci_add_host(struct sdhci_host *host)
{
    struct mmc_host *mmc;
    unsigned int caps;
    int ret;

    WARN_ON(host == NULL);
    if (host == NULL)
        return -EINVAL;

    mmc = host->mmc;

    if (debug_quirks)
        host->quirks = debug_quirks;

    sdhci_reset(host, SDHCI_RESET_ALL);

    host->version = readw(host->ioaddr + SDHCI_HOST_VERSION);
    host->version = (host->version & SDHCI_SPEC_VER_MASK)
                >> SDHCI_SPEC_VER_SHIFT;
    if (host->version > SDHCI_SPEC_200) {
        printk(KERN_ERR "%s: Unknown controller version (%d). "
            "You may experience problems.\n", mmc_hostname(mmc),
            host->version);
    }

    caps = readl(host->ioaddr + SDHCI_CAPABILITIES);

    if (host->quirks & SDHCI_QUIRK_FORCE_DMA)
        host->flags |= SDHCI_USE_DMA;
    else if (!(caps & SDHCI_CAN_DO_DMA))
        DBG("Controller doesn't have DMA capability\n");
    else
        host->flags |= SDHCI_USE_DMA;

    if ((host->quirks & SDHCI_QUIRK_BROKEN_DMA) &&
        (host->flags & SDHCI_USE_DMA)) {
        DBG("Disabling DMA as it is marked broken\n");
        host->flags &= ~SDHCI_USE_DMA;
    }

    if (host->flags & SDHCI_USE_DMA) {
        if ((host->version >= SDHCI_SPEC_200) &&
                (caps & SDHCI_CAN_DO_ADMA2))
            host->flags |= SDHCI_USE_ADMA;
    }

    if ((host->quirks & SDHCI_QUIRK_BROKEN_ADMA) &&
        (host->flags & SDHCI_USE_ADMA)) {
        DBG("Disabling ADMA as it is marked broken\n");
        host->flags &= ~SDHCI_USE_ADMA;
    }

    if (host->flags & SDHCI_USE_DMA) {
        if (host->ops->enable_dma) {
            if (host->ops->enable_dma(host)) {
                printk(KERN_WARNING "%s: No suitable DMA "
                    "available. Falling back to PIO.\n",
                    mmc_hostname(mmc));
                host->flags &= ~(SDHCI_USE_DMA | SDHCI_USE_ADMA);
            }
        }
    }

    if (host->flags & SDHCI_USE_ADMA) {
        /*
         * We need to allocate descriptors for all sg entries
         * (128) and potentially one alignment transfer for
         * each of those entries.
         */
        host->adma_desc = kmalloc((128 * 2 + 1) * 4, GFP_KERNEL);
        host->align_buffer = kmalloc(128 * 4, GFP_KERNEL);
        if (!host->adma_desc || !host->align_buffer) {
            kfree(host->adma_desc);
            kfree(host->align_buffer);
            printk(KERN_WARNING "%s: Unable to allocate ADMA "
                "buffers. Falling back to standard DMA.\n",
                mmc_hostname(mmc));
            host->flags &= ~SDHCI_USE_ADMA;
        }
    }

    /*
     * If we use DMA, then it's up to the caller to set the DMA
     * mask, but PIO does not need the hw shim so we set a new
     * mask here in that case.
     */
    if (!(host->flags & SDHCI_USE_DMA)) {
        host->dma_mask = DMA_BIT_MASK(64);
        mmc_dev(host->mmc)->dma_mask = &host->dma_mask;
    }

    if (host->ops->get_max_clock)
        host->max_clk = host->ops->get_max_clock(host);
    else {
        host->max_clk =    (caps & SDHCI_CLOCK_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT;
        host->max_clk *= 1000000;
    }
    if (host->max_clk == 0) {
        printk(KERN_ERR "%s: Hardware doesn't specify base clock "
            "frequency.\n", mmc_hostname(mmc));
        return -ENODEV;
    }

    if (host->ops->get_timeout_clock)
        host->timeout_clk = host->ops->get_timeout_clock(host);
    else
        host->timeout_clk =
            (caps & SDHCI_TIMEOUT_CLK_MASK) >> SDHCI_TIMEOUT_CLK_SHIFT;
    if (host->timeout_clk == 0) {
        printk(KERN_ERR "%s: Hardware doesn't specify timeout clock "
            "frequency.\n", mmc_hostname(mmc));
        return -ENODEV;
    }
    if (caps & SDHCI_TIMEOUT_CLK_UNIT)
        host->timeout_clk *= 1000;

    /*
     * Set host parameters.
     */
//设置MMC操作接口
    mmc->ops = &sdhci_ops;
    mmc->f_min = host->max_clk / 256;
    mmc->f_max = host->max_clk;
#if defined(CONFIG_MMC_SDHCI_S3C) || defined(CONFIG_MMC_SDHCI_MODULE)
    mmc->caps |= MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
#else
    mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SDIO_IRQ;
#endif
    if ((caps & SDHCI_CAN_DO_HISPD) ||
        (host->quirks & SDHCI_QUIRK_FORCE_HIGHSPEED))
        mmc->caps |= (MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED);

    mmc->ocr_avail = 0;
    if (caps & SDHCI_CAN_VDD_330)
        mmc->ocr_avail |= MMC_VDD_32_33|MMC_VDD_33_34;
    if (caps & SDHCI_CAN_VDD_300)
        mmc->ocr_avail |= MMC_VDD_29_30|MMC_VDD_30_31;
    if (caps & SDHCI_CAN_VDD_180)
        mmc->ocr_avail |= MMC_VDD_165_195;

    if (mmc->ocr_avail == 0) {
        printk(KERN_ERR "%s: Hardware doesn't report any "
            "support voltages.\n", mmc_hostname(mmc));
        return -ENODEV;
    }

    spin_lock_init(&host->lock);

    /*
     * Maximum number of segments. Depends on if the hardware
     * can do scatter/gather or not.
     */
    if (host->flags & SDHCI_USE_ADMA)
        mmc->max_hw_segs = 128;
    else if (host->flags & SDHCI_USE_DMA)
        mmc->max_hw_segs = 1;
    else /* PIO */
        mmc->max_hw_segs = 128;
    mmc->max_phys_segs = 128;

    /*
     * Maximum number of sectors in one transfer. Limited by DMA boundary
     * size (512KiB).
     */
    mmc->max_req_size = 524288;

    /*
     * Maximum segment size. Could be one segment with the maximum number
     * of bytes. When doing hardware scatter/gather, each entry cannot
     * be larger than 64 KiB though.
     */
    if (host->flags & SDHCI_USE_ADMA)
        mmc->max_seg_size = 65536;
    else
        mmc->max_seg_size = mmc->max_req_size;

    /*
     * Maximum block size. This varies from controller to controller and
     * is specified in the capabilities register.
     */
    mmc->max_blk_size = (caps & SDHCI_MAX_BLOCK_MASK) >> SDHCI_MAX_BLOCK_SHIFT;
    if (mmc->max_blk_size >= 3) {
        printk(KERN_WARNING "%s: Invalid maximum block size, "
            "assuming 512 bytes\n", mmc_hostname(mmc));
        mmc->max_blk_size = 512;
    } else
        mmc->max_blk_size = 512 << mmc->max_blk_size;

    /*
     * Maximum block count.
     */
    mmc->max_blk_count = 65535;

    /*
     * Init tasklets.
     */
//添加两个tasklet并启动计时器
    tasklet_init(&host->card_tasklet,
        sdhci_tasklet_card, (unsigned long)host);
    tasklet_init(&host->finish_tasklet,
        sdhci_tasklet_finish, (unsigned long)host);

    setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host);

//中断映射,设置了可以共享标志
    ret = request_irq(host->irq, sdhci_irq, IRQF_SHARED,
        mmc_hostname(mmc), host);
    if (ret)
        goto untasklet;

//初始化MMC设备,包括复位控制器
    sdhci_init(host);

#ifdef CONFIG_MMC_DEBUG
    sdhci_dumpregs(host);
#endif

#ifdef CONFIG_LEDS_CLASS
    host->led.name = mmc_hostname(mmc);
    host->led.brightness = LED_OFF;
    host->led.default_trigger = mmc_hostname(mmc);
    host->led.brightness_set = sdhci_led_control;

    ret = led_classdev_register(mmc_dev(mmc), &host->led);
    if (ret)
        goto reset;
#endif

    mmiowb();
//加入MMC子系统
    mmc_add_host(mmc);

    printk(KERN_INFO "%s: SDHCI controller on %s [%s] using %s%s\n",
        mmc_hostname(mmc), host->hw_name, dev_name(mmc_dev(mmc)),
        (host->flags & SDHCI_USE_ADMA)?"A":"",
        (host->flags & SDHCI_USE_DMA)?"DMA":"PIO");

    return 0;

#ifdef CONFIG_LEDS_CLASS
reset:
    sdhci_reset(host, SDHCI_RESET_ALL);
    free_irq(host->irq, host);
#endif
untasklet:
    tasklet_kill(&host->card_tasklet);
    tasklet_kill(&host->finish_tasklet);

    return ret;
}
/////////////////////////////////////////////////////////////////////////////////

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 */
#define MMC_CAP_ON_BOARD    (1 << 7)    /* Do not need to rescan after bootup */
#define MMC_CAP_BOOT_ONTHEFLY    (1 << 8)    /* Can detect device at boot time */

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

static const struct mmc_host_ops sdhci_ops = {
//request函数指针指向的函数用来处理host向从设备发送命令的请求,
    .request    = sdhci_request,
//set_ios用来设置电源、时钟等等之类(需要重点关注),
    .set_ios    = sdhci_set_ios,
// get_ro用来判断是否写保护
    .get_ro        = sdhci_get_ro,
    .enable_sdio_irq = sdhci_enable_sdio_irq,
};
//////////////////////////////////////////////////////////////////////////////

//获取 只读
static int sdhci_get_ro(struct mmc_host *mmc)
{
    struct sdhci_host *host;
    unsigned long flags;
    int present;

    host = mmc_priv(mmc);

    spin_lock_irqsave(&host->lock, flags);

//
//#define SDHCI_PRESENT_STATE    0x24      //当前状态寄存器
    if (host->flags & SDHCI_DEVICE_DEAD)
        present = 0;
    else
        present = readl(host->ioaddr + SDHCI_PRESENT_STATE);//基地址+寄存器偏移

    spin_unlock_irqrestore(&host->lock, flags);

    return !(present & SDHCI_WRITE_PROTECT);
}
////////////////////////////////////////////////////////////////////

/*****************************************************************************\
 *                                                                           *
 * MMC callbacks                                                             *
 *                                                                           *
\*****************************************************************************/

static void sdhci_request(struct mmc_host *mmc, struct mmc_request *mrq)
{
    struct sdhci_host *host;
    unsigned long flags;

    host = mmc_priv(mmc);////指定mmc_host的private域赋值给struct sdhci_host *host;

    spin_lock_irqsave(&host->lock, flags);

    WARN_ON(host->mrq != NULL);

#ifndef CONFIG_LEDS_CLASS
    sdhci_activate_led(host);
#endif

    host->mrq = mrq;

    if ((mmc->caps & MMC_CAP_ON_BOARD) || (host->flags & SDHCI_DEVICE_ALIVE))
        sdhci_send_command(host, mrq->cmd);
    else {
        if (!(readl(host->ioaddr + SDHCI_PRESENT_STATE) & SDHCI_CARD_PRESENT)
            || (host->flags & SDHCI_DEVICE_DEAD)) {
            host->mrq->cmd->error = -ENOMEDIUM;
            tasklet_schedule(&host->finish_tasklet);
        } else
            sdhci_send_command(host, mrq->cmd);//发送命令
    }
    mmiowb();
    spin_unlock_irqrestore(&host->lock, flags);
}
/////////////////////////////////////////////////////////////////////////////////////

中断注册函_irq的第一个参数中断号就取自于s3c_device_hsmmc1.resource里面的irq参数,

sdhci_irq就是中断服务程序,该中断函数一般在插卡、拔卡或者从设备反馈给host信息时会被调用数request

static irqreturn_t sdhci_irq(int irq, void *dev_id)
{
    irqreturn_t result;
    struct sdhci_host* host = dev_id;
    u32 intmask;
    int cardint = 0;

    spin_lock(&host->lock);

    intmask = readl(host->ioaddr + SDHCI_INT_STATUS);

    if (!intmask || intmask == 0xffffffff) {
        result = IRQ_NONE;
        goto out;
    }

    DBG("*** %s got interrupt: 0x%08x\n",
        mmc_hostname(host->mmc), intmask);

    if (intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE)) {
        writel(intmask & (SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE),
            host->ioaddr + SDHCI_INT_STATUS);
        tasklet_schedule(&host->card_tasklet);
    }

    intmask &= ~(SDHCI_INT_CARD_INSERT | SDHCI_INT_CARD_REMOVE);

    if (intmask & SDHCI_INT_CMD_MASK) {
#if defined(CONFIG_MMC_SDHCI_S3C) || defined(CONFIG_MMC_SDHCI_MODULE)
        /* read until all status bit is up. by scsuh */
        int i;
        for (i=0; i<0x1000000; i++) {
            intmask = readl(host->ioaddr + SDHCI_INT_STATUS);
            if (intmask & SDHCI_INT_RESPONSE)
                break;
        }
        if (0x1000000 == i) {
            printk("FAIL: waiting for status update.\n");
        }
#endif
        writel(intmask & SDHCI_INT_CMD_MASK,
            host->ioaddr + SDHCI_INT_STATUS);
        sdhci_cmd_irq(host, intmask & SDHCI_INT_CMD_MASK);
    }

    if (intmask & SDHCI_INT_DATA_MASK) {
        writel(intmask & SDHCI_INT_DATA_MASK,
            host->ioaddr + SDHCI_INT_STATUS);
        sdhci_data_irq(host, intmask & SDHCI_INT_DATA_MASK);
    }

    intmask &= ~(SDHCI_INT_CMD_MASK | SDHCI_INT_DATA_MASK);

    intmask &= ~SDHCI_INT_ERROR;

    if (intmask & SDHCI_INT_BUS_POWER) {
        printk(KERN_ERR "%s: Card is consuming too much power!\n",
            mmc_hostname(host->mmc));
        writel(SDHCI_INT_BUS_POWER, host->ioaddr + SDHCI_INT_STATUS);
    }

    intmask &= ~SDHCI_INT_BUS_POWER;

    if (intmask & SDHCI_INT_CARD_INT)
        cardint = 1;

    intmask &= ~SDHCI_INT_CARD_INT;

    if (intmask) {
        printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n",
            mmc_hostname(host->mmc), intmask);
        sdhci_dumpregs(host);

        writel(intmask, host->ioaddr + SDHCI_INT_STATUS);
    }

    result = IRQ_HANDLED;

    mmiowb();
out:
    spin_unlock(&host->lock);

    /*
     * We have to delay this as it calls back into the driver.
     */
    if (cardint)
        mmc_signal_sdio_irq(host->mmc);

    return result;
}




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