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

一、概述

本文章主要通过源代码分析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_
  • 4
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值