本文章主要通过源代码分析EMMC的驱动框架,解释如何注册一个host驱动、并通过card层的块设备驱动接口和core层相关接口访问host驱动接口,进而访问emmc器件。
dw_mci_drv_data
这个是dw_mci框架的私有数据,主要包含一些回调接口,通过接口访问具体芯片mmc控制器的一些参数。
struct dw_mci_drv_data {
unsigned long *caps;
int (*init)(struct dw_mci *host);
int (*setup_clock)(struct dw_mci *host);
void (*prepare_command)(struct dw_mci *host, u32 *cmdr);
void (*set_ios)(struct dw_mci *host, struct mmc_ios *ios);
int (*parse_dt)(struct dw_mci *host);
int (*execute_tuning)(struct dw_mci_slot *slot, u32 opcode);
int (*prepare_hs400_tuning)(struct dw_mci *host,
struct mmc_ios *ios);
int (*switch_voltage)(struct mmc_host *mmc,
struct mmc_ios *ios);
};
dw_mci_dma_ops
struct dw_mci_dma_ops {
int (*init)(struct dw_mci *host);
int (*start)(struct dw_mci *host, unsigned int sg_len);
void (*complete)(void *host);
void (*stop)(struct dw_mci *host);
void (*cleanup)(struct dw_mci *host);
void (*exit)(struct dw_mci *host);
};
dw_mci
struct dw_mci {
int use_dma;
const struct dw_mci_dma_ops *dma_ops;
struct device *dev;
const struct dw_mci_drv_data *drv_data;
struct dw_mci_slot *slot[MAX_MCI_SLOTS];
};
dw_mci_pltfm_register
该接口由具体mmc控制器驱动调用,注册为一个dw_mci兼容框架的驱动,这一类驱动访问的寄存器,时钟参数都一样的,彼此兼容。
int dw_mci_pltfm_register(struct platform_device *pdev,
const struct dw_mci_drv_data *drv_data)
{
struct dw_mci *host;
host = devm_kzalloc(&pdev->dev, sizeof(struct dw_mci), GFP_KERNEL);
/* 填充host实例 */
return dw_mci_probe(host);
}
dw_mci_probe
int dw_mci_probe(struct dw_mci *host)
{
if (!host->pdata) {
/* 从具体平台dts解析出mmc接口的节点参数 */
host->pdata = dw_mci_parse_dt(host);
}
/* 获取时钟 */
host->biu_clk = devm_clk_get(host->dev, "biu");
host->ciu_clk = devm_clk_get(host->dev, "ciu");
/* 调用具体mmc控制器的驱动回调 */
if (drv_data && drv_data->init) {
}
if (drv_data && drv_data->setup_clock) {
}
host->dma_ops = host->pdata->dma_ops;
dw_mci_init_dma(host);
for (i = 0; i < host->num_slots; i++) {
/* 一个mmc控制器就是一个slot,例如rk3399有两个mmc控制器,一个是sdio0,一个是sdmmc */
ret = dw_mci_init_slot(host, i);
}
}
dw_mci_init_slot
/* 这个就是mmc控制器的回调接口 */
static const struct mmc_host_ops dw_mci_ops = {
.request = dw_mci_request,
.pre_req = dw_mci_pre_req,
.post_req = dw_mci_post_req,
.set_ios = dw_mci_set_ios,
.set_sdio_status = dw_mci_set_sdio_status,
.get_ro = dw_mci_get_ro,
.get_cd = dw_mci_get_cd,
.enable_sdio_irq = dw_mci_enable_sdio_irq,
.execute_tuning = dw_mci_execute_tuning,
.card_busy = dw_mci_card_busy,
.start_signal_voltage_switch = dw_mci_switch_voltage,
.init_card = dw_mci_init_card,
.prepare_hs400_tuning = dw_mci_prepare_hs400_tuning,
};
static int dw_mci_init_slot(struct dw_mci *host, unsigned int id)
{
struct mmc_host *mmc;
mmc = mmc_alloc_host(sizeof(struct dw_mci_slot), host->dev);
/* 绑定回调,以便core层调用,例如request、card_busy等 */
mmc->ops = &dw_mci_ops;
ret = mmc_add_host(mmc);
}
dw_mci_init_dma
static const struct dw_mci_dma_ops dw_mci_idmac_ops = {
.init = dw_mci_idmac_init,
.start = dw_mci_idmac_start_dma,
.stop = dw_mci_idmac_stop_dma,
.complete = dw_mci_dmac_complete_dma,
.cleanup = dw_mci_dma_cleanup,
};
static const struct dw_mci_dma_ops dw_mci_edmac_ops = {
.init = dw_mci_edmac_init,
.exit = dw_mci_edmac_exit,
.start = dw_mci_edmac_start_dma,
.stop = dw_mci_edmac_stop_dma,
.complete = dw_mci_dmac_complete_dma,
.cleanup = dw_mci_dma_cleanup,
};
static void dw_mci_init_dma(struct dw_mci *host)
{
if (host->use_dma == TRANS_MODE_IDMAC) {
host->dma_ops = &dw_mci_idmac_ops;
} else {
host->dma_ops = &dw_mci_edmac_ops;
}
}
dw_mci_rockchip_probe
/* 这里实现了dm_mci框架的回调接口 */
static const struct dw_mci_drv_data rk3288_drv_data = {
.caps = dw_mci_rk3288_dwmmc_caps,
.prepare_command = dw_mci_rockchip_prepare_command,
.set_ios = dw_mci_rk3288_set_ios,
.execute_tuning = dw_mci_rk3288_execute_tuning,
.parse_dt = dw_mci_rk3288_parse_dt,
.setup_clock = dw_mci_rk3288_setup_clock,
.init = dw_mci_rockchip_init,
};
static const struct of_device_id dw_mci_rockchip_match[] = {
{ .compatible = "rockchip,rk2928-dw-mshc",
.data = &rk2928_drv_data },
{ .compatible = "rockchip,rk3288-dw-mshc",
.data = &rk3288_drv_data },
{},
};
static int dw_mci_rockchip_probe(struct platform_device *pdev)
{
const struct dw_mci_drv_data *drv_data;
const struct of_device_id *match;
if (!pdev->dev.of_node)
return -ENODEV;
match = of_match_node(dw_mci_rockchip_match, pdev->dev.of_node);
drv_data = match->data;
/* 调用dw_mci框架接口注册为一个dw_mci驱动 */
return dw_mci_pltfm_register(pdev, drv_data);
}
mmc_host_ops
这个结构描述mmc控制器需要实现的回调接口,如mmc卡初始化,request等接口
struct mmc_host_ops {
void (*post_req)(struct mmc_host *host, struct mmc_request *req,
int err);
void (*pre_req)(struct mmc_host *host, struct mmc_request *req,
bool is_first_req);
void (*request)(struct mmc_host *host, struct mmc_request *req);
int (*get_ro)(struct mmc_host *host);
int (*get_cd)(struct mmc_host *host);
void (*init_card)(struct mmc_host *host, struct mmc_card *card);
}
mmc_host
这个是mmc控制器结构,里面包含了控制器的回调接口
struct mmc_host {
struct device *parent;
struct device class_dev;
/* mmc控制需要实现的回调接口 */
const struct mmc_host_ops *ops;
}
mmc_alloc_host
struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{
struct mmc_host *host;
host = kzalloc(sizeof(struct mmc_host) + extra, GFP_KERNEL);
/* 使用延时工作队列调用core层的接口扫描mmc总线上的mmc器件,最终会调用mmc控制器的回调 */
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
return host;
}
mmc_add_host
int mmc_add_host(struct mmc_host *host)
{
int err;
err = device_add(&host->class_dev);
/* 启动mmc控制器,让其工作 */
mmc_start_host(host);
}
mmc_start_host
void mmc_start_host(struct mmc_host *host)
{
_mmc_detect_change(host, 0, false);
}
_mmc_detect_change
static void _mmc_detect_change(struct mmc_host *host, unsigned long delay,
bool cd_irq)
{
host->detect_change = 1;
/* 调度工作队列,开始侦测mmc总线上的器件 */
mmc_schedule_delayed_work(&host->detect, delay);
}
mmc_rescan
void mmc_rescan(struct work_struct *work)
{
struct mmc_host *host =
container_of(work, struct mmc_host, detect.work);
for (i = 0; i < ARRAY_SIZE(freqs); i++) {
if (!mmc_rescan_try_freq(host, max(freqs[i], host->f_min)))
break;
if (freqs[i] <= host->f_min)
break;
}
}
mmc_rescan_try_freq
static int mmc_rescan_try_freq(struct mmc_host *host, unsigned freq)
{
#ifdef MMC_STANDARD_PROBE
/* 调用对接card层的接口 */
if (!mmc_attach_mmc(host))
return 0;
#else
if ((host->restrict_caps & RESTRICT_CARD_TYPE_EMMC) &&
!mmc_attach_mmc(host))
return 0;
#endif
}
mmc_wait_for_req
这个接口实现对mmc控制器的访问
void mmc_wait_for_req(struct mmc_host *host, struct mmc_request *mrq)
{
__mmc_start_req(host, mrq);
mmc_wait_for_req_done(host, mrq);
}
__mmc_start_req
static int __mmc_start_req(struct mmc_host *host, struct mmc_request *mrq)
{
int err;
err = mmc_start_request(host, mrq);
}
mmc_start_request
static int mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
__mmc_start_request(host, mrq);
}
__mmc_start_request
static void __mmc_start_request(struct mmc_host *host, struct mmc_request *mrq)
{
if (mmc_is_io_op(mrq->cmd->opcode) && host->ops->card_busy) {
/* 调用host层mmc_host_ops的card_busy接口 */
while (host->ops->card_busy(host) && --tries)
mmc_delay(1);
}
/* 调用host层mmc_host_ops的request接口 */
host->ops->request(host, mrq);
}
mmc_attach_mmc
static const struct mmc_bus_ops mmc_ops = {
.remove = mmc_remove,
.detect = mmc_detect,
.suspend = mmc_suspend,
.resume = mmc_resume,
.runtime_suspend = mmc_runtime_suspend,
.runtime_resume = mmc_runtime_resume,
.alive = mmc_alive,
.shutdown = mmc_shutdown,
.reset = mmc_reset,
};
int mmc_attach_mmc(struct mmc_host *host)
{
int err;
mmc_attach_bus(host, &mmc_ops);
err = mmc_init_card(host, rocr, NULL);
err = mmc_add_card(host->card);
}
mmc_init_card
static int mmc_init_card(struct mmc_host *host, u32 ocr,
struct mmc_card *oldcard)
{
struct mmc_card *card;
int err;
u32 cid[4];
if (oldcard) {
if (memcmp(cid, oldcard->raw_cid, sizeof(cid)) != 0) {
}
card = oldcard;
} else {
card = mmc_alloc_card(host, &mmc_type);
card->ocr = ocr;
card->type = MMC_TYPE_MMC;
card->rca = 1;
memcpy(card->raw_cid, cid, sizeof(card->raw_cid));
}
/* 调用mmc控制器接口初始化emmc */
if (host->ops->init_card)
host->ops->init_card(host, card);
if (!oldcard) {
/*
* Fetch CSD from card.
*/
err = mmc_send_csd(card, card->raw_csd);
err = mmc_decode_csd(card);
err = mmc_decode_cid(card);
}
}
mmc_register_bus
这个函数向系统注册mmc总线,以便后面可以向总线添加mmc设备和注册mmc驱动
static struct bus_type mmc_bus_type = {
.name = "mmc",
.dev_groups = mmc_dev_groups,
.match = mmc_bus_match,
.uevent = mmc_bus_uevent,
.probe = mmc_bus_probe,
.remove = mmc_bus_remove,
.shutdown = mmc_bus_shutdown,
.pm = &mmc_bus_pm_ops,
};
int mmc_register_bus(void)
{
/* 向系统注册mmc总线 */
return bus_register(&mmc_bus_type);
}
mmc_add_card
int mmc_add_card(struct mmc_card *card)
{
int ret;
/* 最终把mmc设备添加到mmc总线上,并与总线上的驱动匹配 */
ret = device_add(&card->dev);
}
mmc_register_driver
int mmc_register_driver(struct mmc_driver *drv)
{
drv->drv.bus = &mmc_bus_type;
/* 最终把驱动注册到mmc总线上,如果驱动注册时间比设备添加时间迟,这里会做匹配操作 */
return driver_register(&drv->drv);
}
重点讲解card层块设备的实现
mmc_blk_init
static struct mmc_driver mmc_driver = {
.drv = {
.name = "mmcblk",
.pm = &mmc_blk_pm_ops,
},
.probe = mmc_blk_probe,
.remove = mmc_blk_remove,
.shutdown = mmc_blk_shutdown,
};
static int __init mmc_blk_init(void)
{
int res;
/* 注册为块设备 */
res = register_blkdev(MMC_BLOCK_MAJOR, "mmc");
/* 向mmc总线注册驱动 */
res = mmc_register_driver(&mmc_driver);
}
mmc_blk_probe
static int mmc_blk_probe(struct mmc_card *card)
{
struct mmc_blk_data *md, *part_md;
md = mmc_blk_alloc(card);
if (mmc_blk_alloc_parts(card, md))
goto out;
if (mmc_add_disk(md))
goto out;
list_for_each_entry(part_md, &md->part, part) {
if (mmc_add_disk(part_md))
goto out;
}
}
mmc_blk_alloc
static struct mmc_blk_data *mmc_blk_alloc(struct mmc_card *card)
{
sector_t size;
return mmc_blk_alloc_req(card, &card->dev, size, false, NULL,
MMC_BLK_DATA_AREA_MAIN);
}
mmc_blk_alloc_req
static const struct block_device_operations mmc_bdops = {
.open = mmc_blk_open,
.release = mmc_blk_release,
.getgeo = mmc_blk_getgeo,
.owner = THIS_MODULE,
.ioctl = mmc_blk_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = mmc_blk_compat_ioctl,
#endif
};
static struct mmc_blk_data *mmc_blk_alloc_req(struct mmc_card *card,
struct device *parent,
sector_t size,
bool default_ro,
const char *subname,
int area_type)
{
struct mmc_blk_data *md;
md = kzalloc(sizeof(struct mmc_blk_data), GFP_KERNEL);
md->disk = alloc_disk(perdev_minors);
/* 设置块设备的文件操作回调函数 */
md->disk->fops = &mmc_bdops;
md->disk->private_data = md;
}
mmc_blk_ioctl
重点分析一下mmc块设备驱动的ioctl如何通过core层最终调用host层接口实现对mmc设备的操作
static int mmc_blk_ioctl(struct block_device *bdev, fmode_t mode,
unsigned int cmd, unsigned long arg)
{
switch (cmd) {
case MMC_IOC_CMD:
/* 看这行 */
return mmc_blk_ioctl_cmd(bdev,
(struct mmc_ioc_cmd __user *)arg);
case MMC_IOC_MULTI_CMD:
return mmc_blk_ioctl_multi_cmd(bdev,
(struct mmc_ioc_multi_cmd __user *)arg);
default:
return -EINVAL;
}
}
mmc_blk_ioctl_cmd
static int mmc_blk_ioctl_cmd(struct block_device *bdev,
struct mmc_ioc_cmd __user *ic_ptr)
{
struct mmc_blk_ioc_data *idata;
struct mmc_blk_data *md;
struct mmc_card *card;
int err = 0, ioc_err = 0;
idata = mmc_blk_ioctl_copy_from_user(ic_ptr);
md = mmc_blk_get(bdev->bd_disk);
card = md->queue.card;
mmc_get_card(card);
/* 看这行 */
ioc_err = __mmc_blk_ioctl_cmd(card, md, idata);
mmc_put_card(card);
err = mmc_blk_ioctl_copy_to_user(ic_ptr, idata);
}
__mmc_blk_ioctl_cmd
static int __mmc_blk_ioctl_cmd(struct mmc_card *card, struct mmc_blk_data *md,
struct mmc_blk_ioc_data *idata)
{
struct mmc_request mrq = {NULL};
/* 这里调用了core层的接口 */
mmc_wait_for_req(card->host, &mrq);
}
在完全初始化完host驱动之后,通过调度延时工作队列,进入到设备扫描阶段,以便发现连接到host上的mmc设备