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函数。