详解sd协议以及裸机和u-boot中的sd卡驱动(4)

本文为该系列的第四篇。

3.3.3 核心层

在上文的协议层中,我们发现每个命令函数最终都会调用到mmc_send_cmd函数将命令发送出去,那本节就来讲解该函数的实现以及其他一些初始化过程中会用到的函数,最后探究相比于裸机驱动为什么会多出这个核心层。

3.3.3.1 mmc_send_cmd函数

代码如下:

 int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data)
 {
 return dm_mmc_send_cmd(mmc->dev, cmd, data);
 }

其中又调用了dm_mmc_send_cmd函数,代码如下:

 int dm_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
     struct mmc_data *data)
 {
 struct mmc *mmc = mmc_get_mmc_dev(dev);
 struct dm_mmc_ops *ops = mmc_get_ops(dev);
 int ret;
 
 mmmc_trace_before_send(mmc, cmd);
 if (ops->send_cmd)
 ret = ops->send_cmd(dev, cmd, data);
 else
 ret = -ENOSYS;
 mmmc_trace_after_send(mmc, cmd, ret);
 
 return ret;
 }

我们发现,该函数最终又调用了struct dm_mmc_ops *ops的send_cmd函数指针,关于struct dm_mmc_ops定义如下:

 struct dm_mmc_ops {
 /**
  * deferred_probe() - Some configurations that need to be deferred
  * to just before enumerating the device
  *
  * @dev:Device to init
  * @return 0 if Ok, -ve if error
  */
 int (*deferred_probe)(struct udevice *dev);
 /**
  * send_cmd() - Send a command to the MMC device
  *
  * @dev:Device to receive the command
  * @cmd:Command to send
  * @data:Additional data to send/receive
  * @return 0 if OK, -ve on error
  */
 int (*send_cmd)(struct udevice *dev, struct mmc_cmd *cmd,
 struct mmc_data *data);
 
 /**
  * set_ios() - Set the I/O speed/width for an MMC device
  *
  * @dev:Device to update
  * @return 0 if OK, -ve on error
  */
 int (*set_ios)(struct udevice *dev);
 
 /**
  * get_cd() - See whether a card is present
  *
  * @dev:Device to check
  * @return 0 if not present, 1 if present, -ve on error
  */
 int (*get_cd)(struct udevice *dev);
 
 /**
  * get_wp() - See whether a card has write-protect enabled
  *
  * @dev:Device to check
  * @return 0 if write-enabled, 1 if write-protected, -ve on error
  */
 int (*get_wp)(struct udevice *dev);
 
 #ifdef MMC_SUPPORTS_TUNING
 /**
  * execute_tuning() - Start the tuning process
  *
  * @dev:Device to start the tuning
  * @opcode:Command opcode to send
  * @return 0 if OK, -ve on error
  */
 int (*execute_tuning)(struct udevice *dev, uint opcode);
 #endif
 
 /**
  * wait_dat0() - wait until dat0 is in the target state
  *(CLK must be running during the wait)
  *
  * @dev:Device to check
  * @state:target state
  * @timeout_us:timeout in us
  * @return 0 if dat0 is in the target state, -ve on error
  */
 int (*wait_dat0)(struct udevice *dev, int state, int timeout_us);
 
 #if CONFIG_IS_ENABLED(MMC_HS400_ES_SUPPORT)
 /* set_enhanced_strobe() - set HS400 enhanced strobe */
 int (*set_enhanced_strobe)(struct udevice *dev);
 #endif
 
 /**
  * host_power_cycle - host specific tasks in power cycle sequence
  *     Called between mmc_power_off() and
  *     mmc_power_on()
  *
  * @dev:Device to check
  * @return 0 if not present, 1 if present, -ve on error
  */
 int (*host_power_cycle)(struct udevice *dev);
 
 /**
  * get_b_max - get maximum length of single transfer
  *       Called before reading blocks from the card,
  *       useful for system which have e.g. DMA limits
  *       on various memory ranges.
  *
  * @dev:Device to check
  * @dst:Destination buffer in memory
  * @blkcnt:Total number of blocks in this transfer
  * @return maximum number of blocks for this transfer
  */
 int (*get_b_max)(struct udevice *dev, void *dst, lbaint_t blkcnt);
 };

其实,这种调用函数指针的方式同样用在了其他一些用于初始化时的函数,比如下列函数:

3.3.3.2 mmc_set_ios函数

该函数的功能为设置sd控制器的时钟以及数据线宽度。

 int mmc_set_ios(struct mmc *mmc)
 {
 return dm_mmc_set_ios(mmc->dev);
 }
 
 int dm_mmc_set_ios(struct udevice *dev)
 {
 struct dm_mmc_ops *ops = mmc_get_ops(dev);
 
 if (!ops->set_ios)
 return -ENOSYS;
 return ops->set_ios(dev);
 }
3.3.3.3 mmc_getwp函数

该函数的功能为获取sd卡的写保护功能是否使能。

 int mmc_getwp(struct mmc *mmc)
 {
 return dm_mmc_get_wp(mmc->dev);
 }
 
 int dm_mmc_get_wp(struct udevice *dev)
 {
 struct dm_mmc_ops *ops = mmc_get_ops(dev);
 
 if (!ops->get_wp)
 return -ENOSYS;
 return ops->get_wp(dev);
 }
3.3.3.4 mmc_getcd函数

该函数的功能为探测sd卡是否插入。

 int mmc_getcd(struct mmc *mmc)
 {
 return dm_mmc_get_cd(mmc->dev);
 }
 
 int dm_mmc_get_cd(struct udevice *dev)
 {
 struct dm_mmc_ops *ops = mmc_get_ops(dev);
 
 if (!ops->get_cd)
 return -ENOSYS;
 return ops->get_cd(dev);
 }
3.3.3.5 增加核心层的原因

上述一系列函数都只是调用了struct dm_mmc_ops结构体中的函数指针,我们可以想一下,为什么要这样做呢?

我们要知道,u-boot需要支持不同的sd控制器,而每种sd控制器针对上述的一系列函数的实现均不同,这样,我们就需要在上文提到的协议层与对sd控制器的控制(也就是我们后面的驱动层的内容)之间增加一层核心层,作用便是解除协议层与驱动层之间的耦合。

当需要支持不同的sd控制器时,可以针对每个sd控制器分别实现一套上述的一系列函数,然后将每一套函数均赋值到自己的struct dm_mmc_ops结构体的各个函数指针中。当需要调用时,只需找到针对每个sd控制器的struct dm_mmc_ops结构体,然后调用其中的函数指针即可。

这里我们可以猜想到,驱动层的主要工作应该就是针对每个sd控制器,去实现具体的struct dm_mmc_ops结构体中的函数指针对应的函数,事实的确如此,接下来我们便开始进入驱动层的世界。

  • 发表于 2020-07-18 00:41
  • 阅读 ( 207 )
  • 分类:经验分享

0 条评论&回复

请先 登录 后评论
渐进
渐进

12 篇文章

作家榜 »

  1. 百问网-周老师 18 文章
  2. st_ashang 14 文章
  3. 渐进 12 文章
  4. zxq 11 文章
  5. helloworld 8 文章
  6. 谢工 5 文章
  7. Litchi_Zheng 5 文章
  8. 星星之火 5 文章