插曲:
因为使用的平台是telechips的tcc803x,其芯片用户手册描述寄存器都是四字节寻址的,但是在代码驱动中用的很可能是单字节寻址,咋一看,有可能有的地址在芯片手册上没有或者感觉写错了,其实不是,这个需要注意一下。
简单流程:
mmc host主控器注册完成之后,会分别生成一个底层硬件相关的主控制设备(struct)sdhci_host和通用抽象的主控制器设备(struct)mmc_host,当检测到有sd卡等mmc从设备插入时,mmc host主控制器会向sd卡等mmc从设备发起会话,sd卡等从设备作出相应的应答。mmc host主控制建立会话的机制是先通过通用抽象的(struct)mmc_host主设备的通用操作集(struct)mmc_host_ops,再由通用操作集(struct)mmc_host_ops进一步调用底层硬件相关的操作集(struct)sdhci_ops实现主从设备的通信的。
转载:linux MMC framework(2) - sdhci host driver_Hacker_Albert的博客-CSDN博客
注册mmc主控制器:
static int sdhci_tcc_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
const struct sdhci_tcc_soc_data *soc_data;
struct sdhci_host *host;
struct sdhci_pltfm_host *pltfm_host;
struct sdhci_tcc *tcc = NULL;
match = of_match_device(sdhci_tcc_of_match_table, &pdev->dev);
soc_data = match->data;
host = sdhci_pltfm_init(pdev, soc_data->pdata, sizeof(*tcc));
pltfm_host = sdhci_priv(host);
tcc = sdhci_pltfm_priv(pltfm_host);
sdhci_add_host(host);
}
sdhci_tcc_of_match_table表:
static const struct of_device_id sdhci_tcc_of_match_table[] = {
{ .compatible = "telechips,tcc803x-sdhci,module-only", .data = &soc_data_tcc803x},
{}
};
表里的soc_data_tcc803x结构:
static const struct sdhci_tcc_soc_data soc_data_tcc803x = {
.pdata = &sdhci_tcc803x_pdata,
.parse_channel_configs = sdhci_tcc803x_parse_channel_configs,
.set_channel_configs = sdhci_tcc803x_set_channel_configs,
.set_core_clock = sdhci_tcc803x_set_core_clock,
.sdhci_tcc_quirks = 0,
};
sdhci_tcc803x_pdata平台数据结构:
static const struct sdhci_pltfm_data sdhci_tcc803x_pdata = {
.ops = &sdhci_tcc803x_ops,
.quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN,
.quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
SDHCI_QUIRK2_STOP_WITH_TC,
};
sdhci_tcc803x_ops操作集:
static const struct sdhci_ops sdhci_tcc803x_ops = {
.get_max_clock = sdhci_tcc803x_clk_get_max_clock,
.set_clock = sdhci_tcc_set_clock,
.set_bus_width = sdhci_set_bus_width,
.reset = sdhci_tcc_reset,
.hw_reset = sdhci_tcc_hw_reset,
.set_uhs_signaling = sdhci_set_uhs_signaling,
.get_ro = sdhci_tcc_get_ro,
};
sdhci_ops与主控制硬件打交道,所以是再host主控制器驱动中实现的。
重新回到probe函数,看sdhci_pltfm_init函数:
struct sdhci_host *sdhci_pltfm_init(struct platform_device *pdev,
const struct sdhci_pltfm_data *pdata,
size_t priv_size)
{
struct sdhci_host *host;
//分配一个struct sdhci_host结构空间,附带分配一个struct sdhci_pltfm_host结构空间
//和一个priv_size大小的空间
host = sdhci_alloc_host(&pdev->dev,sizeof(struct sdhci_pltfm_host) + priv_size);
if (pdata && pdata->ops)
host->ops = pdata->ops; //对应上面定义的struct sdhci_ops sdhci_tcc803x_ops变量
return host;
}
sdhci_alloc_host函数:
struct sdhci_host *sdhci_alloc_host(struct device *dev,
size_t priv_size)
{
struct mmc_host *mmc;
struct sdhci_host *host;
//本来是分配struct sdci_host结构体空间的,这里先分配struct mmc_host
//结构体空间,附带分配着sdhci_host结构体空间,以及sdhci_host结构体附带
//的其它空间大小
mmc = mmc_alloc_host(sizeof(struct sdhci_host) + priv_size, dev);
host = mmc_priv(mmc);
host->mmc = mmc;
host->mmc_host_ops = sdhci_ops;
mmc->ops = &host->mmc_host_ops;
return host;
}
mmc_priv函数:
static inline void *mmc_priv(struct mmc_host *host)
{
return (void *)host->private;
}
mmc_host结构体:
struct mmc_host {
struct device *parent;
struct device class_dev;
const struct mmc_host_ops *ops;
unsigned int f_min;
unsigned int f_max;
unsigned int f_init;
......
unsigned long private[0] ____cacheline_aligned;
}
回到probe函数,看sdhci_priv函数:
static inline void *sdhci_priv(struct sdhci_host *host)
{
return host->private;
}
sdhci_host结构:
struct sdhci_host {
/* Data set by hardware interface driver */
const char *hw_name; /* Hardware bus name */
const struct sdhci_ops *ops; /* Low level hw interface */
/* Internal data */
struct mmc_host *mmc; /* MMC structure */
struct mmc_host_ops mmc_host_ops; /* MMC host ops */
......
unsigned long private[0] ____cacheline_aligned;
}
回到probe函数,看sdhci_add_host函数:
int sdhci_add_host(struct sdhci_host *host)
{
sdhci_setup_host(host);
__sdhci_add_host(host);
return 0;
}