linux2.6.28块设备mmc_sd卡mmc控制器平台驱动注册

//
图片
///
第一条线 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© 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;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xx-xx-xxx-xxx

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值