一、概述
本文章主要通过源代码分析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_