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