基于RK3399的Linux驱动开发 -- EMMC驱动框架

文章目录

  • 一、概述
  • 二、host层
    • 1、dw_mci框架
      • `dw_mci_drv_data`
      • `dw_mci_dma_ops`
      • `dw_mci`
      • `dw_mci_pltfm_register`
      • `dw_mci_probe`
      • `dw_mci_init_slot`
      • `dw_mci_init_dma`
    • 2、dw_mci-rockchip驱动
      • `dw_mci_rockchip_probe`
  • 三、core层
    • 1、对接host层的接口
      • `mmc_host_ops`
      • `mmc_host`
      • `mmc_alloc_host`
      • `mmc_add_host`
    • 2、core层接口
      • `mmc_start_host`
      • `_mmc_detect_change`
      • `mmc_rescan`
      • `mmc_rescan_try_freq`
      • `mmc_wait_for_req`
      • `__mmc_start_req`
      • `mmc_start_request`
      • `__mmc_start_request`
    • 2、对接card层的接口
      • `mmc_attach_mmc`
      • `mmc_init_card`
    • 4、mmc总线接口
      • `mmc_register_bus`
      • `mmc_add_card`
      • `mmc_register_driver`
  • 四、card层
      • `mmc_blk_init`
      • `mmc_blk_probe`
      • `mmc_blk_alloc`
      • `mmc_blk_alloc_req`
      • `mmc_blk_ioctl`
      • `mmc_blk_ioctl_cmd`
      • `__mmc_blk_ioctl_cmd`
  • 五、时序图
    • 1、mmc块设备驱动初始化
    • 2、mmc host驱动初始化
    • 3、mmc设备扫描并添加
    • 4、mmc块设备注册
    • 5、mmc块设备操作

一、概述

本文章主要通过源代码分析EMMC的驱动框架,解释如何注册一个host驱动、并通过card层的块设备驱动接口和core层相关接口访问host驱动接口,进而访问emmc器件。

二、host层

1、dw_mci框架

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

2、dw_mci-rockchip驱动

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);
}

三、core层

1、对接host层的接口

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);
}

2、core层接口

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);
}

2、对接card层的接口

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);
	}
}

4、mmc总线接口

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层

重点讲解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);
}

五、时序图

1、mmc块设备驱动初始化

card/block genhd core/bus register_blkdev 注册块设备驱动 mmc_register_driver 注册到mmc总线 card/block genhd core/bus

2、mmc host驱动初始化

dw_mmc_rockchip platform dw_mmc_pltfm dw_mmc core/host platform_driver_register 注册platform 驱动,跟dts节点匹 配 dw_mci_pltfm_register dts节点匹配后, 注册到dw_mci 框架,实现完整的驱 动 dw_mci_probe 解析dts,初始 化时钟,中断服务 等 dw_mci_init_slot 设置操作host的回 调 mmc_alloc_host 设置延时工作队 列以便调用mm c_rescan mmc_add_host 调度延时工作队列调用 mmc_rescan dw_mmc_rockchip platform dw_mmc_pltfm dw_mmc core/host

3、mmc设备扫描并添加

在完全初始化完host驱动之后,通过调度延时工作队列,进入到设备扫描阶段,以便发现连接到host上的mmc设备

core/host core/core core/mmc dw_mmc core/bus mmc_rescan 通过延时工 作队列触发 mmc_rescan_try_freq mmc_attach_mmc mmc_init_card 初始化mmc card dw_mci_init_card 回调mmc_host_op s的init_card方法 mmc_add_card 添加设备到mm c总线,这里会 触发总线的驱动 与设备匹配操作 core/host core/core core/mmc dw_mmc core/bus

4、mmc块设备注册

card/block genhd mmc_blk_alloc/mmc_blk_alloc_parts 根据mmc设 备扫描结果分 配块设备文件入口 mmc_add_disk add_disk 向通用文件系 统添加磁盘, 设置文件操作指针 card/block genhd

5、mmc块设备操作

block/ioctl card/block core/core dw_mmc blkdev_ioctl mmc_blk_ioctl 回调block_dev ice_operati ons指针的ioctl方法 mmc_blk_ioctl_cmd __mmc_blk_ioctl_cmd mmc_wait_for_req mmc_wait_for_req_done __mmc_start_request dw_mci_request 回调mmc_ho st_ops指针 的request方法 block/ioctl card/block core/core dw_mmc

你可能感兴趣的:(Linux驱动开发)