热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

uboot硬件驱动

uboot与linux驱动1.uboot本身是裸机程序(1)在裸机中本来是没有驱动概念的(狭义的驱动概念是指在操作系统中用来具体操控硬

uboot与linux驱动

1.uboot本身是裸机程序

(1)在裸机中本来是没有驱动概念的(狭义的驱动概念是指在操作系统中用来具体操控硬件的那部分代码叫驱动)。

(2)裸机程序是直接操控硬件的,操作系统必须通过驱动来操控硬件。这两个有什么区别?本质区别就是分层。

2.uboot虚拟地址对硬件操作的影响

(1)操作系统(指的是linux)下MMU肯定是开启的,也就是说linux驱动中使用的都是虚拟地址,而纯裸机程序中根本不会开MMU,全部使用的是物理地址。这是裸机和操作系统中操控硬件的一个重要区别。

(2)uboot早期也是工作在纯物理地址下,但是现在的uboot开启了MMU,做了虚拟地址映射,因此做驱动时必须考虑到这一点。

通过查看uboot中的虚拟地址映射表,发现除了0x30000000~0x3FFFFFFF映射到0xC0000000~0xCFFFFFFF之外,其余的地址空间全部是原样映射,而在驱动中主要是操控硬件寄存器,而s5pv210的SFR都在0xEXXXXXXX地址空间,因此在驱动不需要考虑虚拟地址。

3.uboot借用(移植)linux驱动

(1)linux驱动本身做了模块化设计,linux驱动本身和linux内核不是强耦合的,这是linux驱动可以被uboot借用(移植)的关键。

(2)uboot移植了linux驱动源代码,uboot是从源代码级别去移植linux驱动的,这就是linux系统的开源性。

(3)uboot中的硬件驱动比linux简单,linux驱动本身有复杂的框架,需要实现更多的附带功能,而uboot本质上只是个裸机程序,uboot移植linux驱动时,只是借用了linux驱动的一部分而已。


iNand/SD卡驱动

1.从start_armboot开始

(1)驱动整体比较庞大,涉及很多文件夹下的很多文件,函数很多,贸然插入根本不知道看哪里,学习时必须有时序。因此这里的iNand/SD卡驱动从start_armboot开始。

(2)在start_armboot函数调用mmc_initialize函数实现mmc的初始化,该函数在lib_arm/board.c的603行被调用。

2.mmc_initialize

代码:1177 ~ 1208行(drivers/mmc/mmc.c)

int mmc_initialize(bd_t *bis)
{struct mmc *mmc;int err;INIT_LIST_HEAD(&mmc_devices);cur_dev_num &#61; 0;if (board_mmc_init(bis) <0)cpu_mmc_init(bis);#if defined(DEBUG_S3C_HSMMC)print_mmc_devices(&#39;,&#39;);
#endif#ifdef CONFIG_CHECK_X210CV3mmc &#61; find_mmc_device(1);//lqm
#elsemmc &#61; find_mmc_device(0);
#endifif (mmc) {err &#61; mmc_init(mmc);if (err)err &#61; mmc_init(mmc);if (err) {printf("Card init fail!\n");return err;}}printf("%ldMB\n", (mmc->capacity/(1024*1024/(1<<9))));return 0;
}

&#xff08;1&#xff09;从名字可以看出&#xff0c;这个函数的作用就是初始化开发板上的MMC系统。MMC系统的初始化应该包含这几部分&#xff1a;SoC中的MMC控制器初始化&#xff08;MMC系统时钟的初始化、SFR初始化&#xff09;、SoC中的MMC相关的GPIO的初始化、iNand/SD卡芯片的初始化。

3.mmc_devices

&#xff08;1&#xff09;mmc_devices是一个链表&#xff08;全局变量&#xff09;&#xff0c;用来记录系统中所有已注册的iNand/SD设备。所以向系统中插入一个iNand/SD设备&#xff0c;则系统驱动就会向mmc_devices链表中插入一个数据结构表示这个设备。

4.cpu_mmc_init

代码&#xff1a;232 ~ 241行&#xff08;cpu/s5pc11x/cpu.c&#xff09;

/** Initializes on-chip MMC controllers.* to override, implement board_mmc_init()*/
int cpu_mmc_init(bd_t *bis)
{
#ifdef CONFIG_S3C_HSMMCsetup_hsmmc_clock();setup_hsmmc_cfg_gpio();return smdk_s3c_hsmmc_init();
#elsereturn 0;
#endif
}

&#xff08;1&#xff09;该函数调用了3个函数&#xff1a;setup_hsmmc_clock、setup_hsmmc_cfg_gpio、smdk_s3c_hsmmc_init。

&#xff08;2&#xff09;setup_hsmmc_clock函数&#xff1a;位于cpu/s5pc11x/setup_hsmmc.c中&#xff0c;从名字上可以看出该函数是用来初始化SoC中MMC控制器的时钟部分的。

&#xff08;3&#xff09;setup_hsmmc_cfg_gpio函数&#xff1a;位于cpu/s5pc11x/setup_hsmmc.c中&#xff0c;从名字上可以该函数是用来配置SoC中MMC相关的GPIO的。

&#xff08;4&#xff09;smdk_s3c_hsmmc_init函数&#xff1a;位于drivers/mmc/s3c_hsmmc.c中&#xff0c;在它的内部通过宏定义USE_MMCx来决定是否调用s3c_hsmmc_initialize函数来进行具体的初始化操作。

&#xff08;5&#xff09;s3c_hsmmc_initialize函数&#xff1a;位于drivers/mmc/s3c_hsmmc.c中&#xff0c;代码如下所示&#xff1a;

static int s3c_hsmmc_initialize(int channel)
{struct mmc *mmc;mmc &#61; &mmc_channel[channel];sprintf(mmc->name, "S3C_HSMMC%d", channel);mmc->priv &#61; &mmc_host[channel];mmc->send_cmd &#61; s3c_hsmmc_send_command;mmc->set_ios &#61; s3c_hsmmc_set_ios;mmc->init &#61; s3c_hsmmc_init;mmc->voltages &#61; MMC_VDD_32_33 | MMC_VDD_33_34;mmc->host_caps &#61; MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS;
#if defined(USE_MMC0_8BIT) || defined(USE_MMC2_8BIT)mmc->host_caps |&#61; MMC_MODE_8BIT;
#endifmmc->f_min &#61; 400000;mmc->f_max &#61; 52000000;mmc_host[channel].clock &#61; 0;switch(channel) {case 0:mmc_host[channel].ioaddr &#61; (void *)ELFIN_HSMMC_0_BASE;break;case 1:mmc_host[channel].ioaddr &#61; (void *)ELFIN_HSMMC_1_BASE;break;case 2:mmc_host[channel].ioaddr &#61; (void *)ELFIN_HSMMC_2_BASE;break;
#ifdef USE_MMC3case 3:mmc_host[channel].ioaddr &#61; (void *)ELFIN_HSMMC_3_BASE;break;
#endifdefault:printk("mmc err: not supported channel %d\n", channel);}return mmc_register(mmc);
}

函数解析&#xff1a;

第1点&#xff0c;定义并且实例化一个struct mmc类型的对象&#xff08;定义了一个指针&#xff0c;并且给指针指向有意义的内存&#xff0c;或者给指针分配内存&#xff09;&#xff0c;然后填充它的各种成员&#xff0c;最后调用mmc_register函数来向驱动框架注册这个mmc设备驱动。

第2点&#xff0c;mmc_register功能是进行mmc设备的注册&#xff0c;注册方法其实就是将当前这个struct mmc使用链表连接到mmc_devices这个全局变量中去。

&#xff08;6&#xff09;在x210_sd.h中定义了USE_MMC0和USE_MMC2&#xff0c;因此在uboot初始化时&#xff0c;会调用2次s3c_hsmmc_initialize函数&#xff0c;传递参数分别是0和2&#xff0c;因此在调用完成之后&#xff0c;系统中会注册上2个mmc设备&#xff0c;表示当前系统中有2个mmc通道在工作。

5.find_mmc_device

代码&#xff1a;63 ~ 78行&#xff08;drivers/mmc/mmc.c&#xff09;

struct mmc *find_mmc_device(int dev_num)
{struct mmc *m;struct list_head *entry;list_for_each(entry, &mmc_devices) {m &#61; list_entry(entry, struct mmc, link);if (m->block_dev.dev &#61;&#61; dev_num)return m;}printf("MMC Device %d not found\n", dev_num);return NULL;
}

&#xff08;1&#xff09;这个函数其实就是通过mmc设备编号在系统中查找对应的mmc设备&#xff08;struct mmc的对象&#xff0c;根据上面的分析可知&#xff0c;系统中有2个&#xff0c;分别是0和2&#xff09;

&#xff08;2&#xff09;函数的工作原理就是通过遍历mmc_devices链表&#xff0c;依次寻找系统中注册的mmc设备&#xff0c;然后对比设备编号和我们当前要查找的设备编号&#xff0c;如果相同&#xff0c;就表示找到要找的设备。找到了之后&#xff0c;调用mmc_init函数来初始化它。

6.mmc_init

代码&#xff1a;1112 ~ 1144行&#xff08;drivers/mmc/mmc.c&#xff09;

int mmc_init(struct mmc *host)
{int err;err &#61; host->init(host);if (err)return err;/* Reset the Card */err &#61; mmc_go_idle(host);if (err)return err;/* Test for SD version 2 */err &#61; mmc_send_if_cond(host);/* Now try to get the SD card&#39;s operating condition */err &#61; mmc_send_app_op_cond(host);/* If the command timed out, we check for an MMC card */if (err &#61;&#61; TIMEOUT) {err &#61; mmc_send_op_cond(host);if (err)return UNUSABLE_ERR;} elseif (err)return UNUSABLE_ERR;return mmc_startup(host);
}

&#xff08;1&#xff09;该函数的功能是初始化mmc卡。

&#xff08;2&#xff09;函数的调用关系为&#xff1a;

mmc_init
        mmc_go_idle
                mmc_send_cmd
        mmc_send_if_cond
                mmc_send_cmd
······

从以上的分析可以看出&#xff0c;mmc_init函数内部就是依次通过向mmc卡发送命令码&#xff08;CMD0、CMD2...&#xff09;来初始化iNand/SD卡内部的控制器&#xff0c;以达到初始化SD卡的目的。


总结

1.整个MMC的初始化分为2部分&#xff1a;SoC这一端的MMC控制器的初始化、SD卡这一端的本身的初始化。前一部分主要是在cpu_mmc_init函数中完成&#xff0c;后一部分主要是在mmc_init函数中完成。

2.整个初始化完成后去使用iNand/SD卡时&#xff0c;操作方法和mmc_init函数中初始化SD卡的的操作一样。读写SD卡时也是通过总线向SD卡发送命令、读取/写入数据来完成的。

3.顺着操作追下去&#xff0c;到了mmc_send_cmd函数处就断了&#xff0c;真正的向SD卡发送命令的硬件操作的函数找不到。这就是学习驱动的麻烦之处。

4.struct mmc结构体是关键&#xff0c;两部分初始化之间是用mmc结构体来链接的&#xff0c;初始化完了之后对mmc卡的常规读写操作也是通过mmc结构体来链接的。


驱动结构体分析

1.数据结构

&#xff08;1&#xff09;驱动设计中有一个关键数据结构&#xff0c;例如MMC驱动的结构体struct mmc&#xff0c;这些结构体中包含一些变量和一些函数指针&#xff0c;变量用来记录驱动相关的一些属性&#xff0c;函数指针用来记录驱动相关的一些操作方法&#xff0c;这些变量和函数指针加起来就构成了驱动&#xff0c;驱动就被抽象为这个结构体。

&#xff08;2&#xff09;驱动工作的时候主要分为两部分&#xff1a;驱动构建&#xff08;构建一个struct mmc&#xff0c;然后填充它&#xff09;、驱动运行&#xff08;调用结构体中变量和函数指针&#xff09;

2.分离思想

&#xff08;1&#xff09;分离思想就是说在驱动中将操作方法和数据分开。

&#xff08;2&#xff09;操作方法就是函数&#xff0c;数据就是变量。所谓操作方法和数据分离的意思是&#xff1a;在不同的地方来存储和管理驱动的操作方法和变量&#xff0c;这样的优势就是驱动便于移植。

3.分层思想

&#xff08;1&#xff09;分层思想是指一个整体的驱动分为好多个层次&#xff0c;简单理解就是驱动分为很多个源文件&#xff0c;放在很多个文件夹中。例如本课程讲的mmc的驱动设计到drivers/mmc下面的2个文件和cpu/s5pc11x下面的几个文件。

&#xff08;2&#xff09;以mmc驱动为例来分析各个文件的作用&#xff1a;

drivers/mmc/mmc.c&#xff1a;本文件的主要内容是和MMC卡操作有关的方法&#xff0c;例如MMC卡设置为空闲状态、卡读写数据等&#xff0c;但是本文件中并没有具体的硬件操作函数&#xff0c;操作最终指向的是struct mmc结构体中的函数指针&#xff0c;这些函数指针是在驱动构建的时候和真正的硬件操作函数挂接的&#xff08;真正的硬件操作函数在别的文件中&#xff09;

drivers/mmc/s3c_hsmmc.c&#xff1a;本文件是SoC内部MMC控制器的硬件操作方法&#xff0c;例如向SD卡发送命令的函数&#xff08;s3c_hsmmc_send_command&#xff09;、SD卡读写数据的函数&#xff08;s3c_hsmmc_set_ios&#xff09;等&#xff0c;这些函数就是具体操作硬件的函数&#xff0c;也就是mmc.c中需要的那些硬件操作函数。这些函数在mmc驱动初始化构建时&#xff08;s3c_hsmmc_initialize函数中&#xff09;和struct mmc挂接起来备用。

分析&#xff1a;mmc.c和s3c_hsmmc.c构成了一个分层&#xff0c;mmc.c中调用了s3c_hsmmc.c中的函数&#xff0c;所以mmc.c在上层&#xff0c;s3c_hsmmc.c在下层。有这个分成后&#xff0c;可以发现mmc.c中不涉及具体硬件的操作&#xff0c;s3c_hsmmc.c中不涉及时序操作。因此移植的时候就有好处&#xff1a;例如要把这一套mmc驱动移植到别的SoC上的&#xff0c;那mmc.c就不用动&#xff0c;s3c_hsmmc.c动就可以了&#xff1b;又例如SoC没变&#xff0c;但是SD卡升级了&#xff0c;这时候只需要更换mmc.c&#xff0c;不需要更换s3c_hsmmc.c。

&#xff08;3&#xff09;cpu/s5pc11x下还有一个setup_hsmmc.c&#xff0c;也和MMC驱动有关&#xff0c;但是这些代码为什么不放到drivers目录下&#xff0c;而是放到cpu目录下&#xff1f;因为这里面的两个函数&#xff08;setup_hsmmc_clock和setup_hsmmc_cfg_gpio&#xff09;都是和SoC有关的初始化函数&#xff0c;这两个函数不能放到drivers目录下&#xff0c;实际上如果非把这两个函数放在drivers/mmc/s3c_hsmmc.c中也能凑合说得过去。


推荐阅读
  • 深入解析Redis内存对象模型
    本文详细介绍了Redis内存对象模型的关键知识点,包括内存统计、内存分配、数据存储细节及优化策略。通过实际案例和专业分析,帮助读者全面理解Redis内存管理机制。 ... [详细]
  • 本文详细介绍了优化DB2数据库性能的多种方法,涵盖统计信息更新、缓冲池调整、日志缓冲区配置、应用程序堆大小设置、排序堆参数调整、代理程序管理、锁机制优化、活动应用程序限制、页清除程序配置、I/O服务器数量设定以及编入组提交数调整等方面。通过这些技术手段,可以显著提升数据库的运行效率和响应速度。 ... [详细]
  • 本文详细介绍了Grand Central Dispatch (GCD) 的核心概念和使用方法,探讨了任务队列、同步与异步执行以及常见的死锁问题。通过具体示例和代码片段,帮助开发者更好地理解和应用GCD进行多线程开发。 ... [详细]
  • ElasticSearch 集群监控与优化
    本文详细介绍了如何有效地监控 ElasticSearch 集群,涵盖了关键性能指标、集群健康状况、统计信息以及内存和垃圾回收的监控方法。 ... [详细]
  • 本文详细介绍如何在Linux系统中配置SSH密钥对,以实现从一台主机到另一台主机的无密码登录。内容涵盖密钥对生成、公钥分发及权限设置等关键步骤。 ... [详细]
  • 在网站制作中随时可用的10个 HTML5 代码片段
    HTML很容易写,但创建网页时,您经常需要重复做同样的任务,如创建表单。在这篇文章中,我收集了10个超有用的HTML代码片段,有HTML5启动模板、空白图片、打电话和发短信、自动完 ... [详细]
  • 访问一个网页的全过程
    准备:DHCPUDPIP和以太网启动主机,用一根以太网电缆连接到学校的以太网交换机,交换机又与学校的路由器相连.学校的这台路由器与一个ISP链接,此ISP(Intern ... [详细]
  • 本文探讨了如何通过一系列技术手段提升Spring Boot项目的并发处理能力,解决生产环境中因慢请求导致的系统性能下降问题。 ... [详细]
  • 本文深入探讨了 Delphi 中类对象成员的核心概念,包括 System 单元的基础知识、TObject 类的定义及其方法、TClass 的作用以及对象的消息处理机制。文章不仅解释了这些概念的基本原理,还提供了丰富的补充和专业解答,帮助读者全面理解 Delphi 的面向对象编程。 ... [详细]
  • [Vue.js 3.0] Guide – Scaling Up – State Management
    [Vue.js 3.0] Guide – Scaling Up – State Management ... [详细]
  • PHP 过滤器详解
    本文深入探讨了 PHP 中的过滤器机制,包括常见的 $_SERVER 变量、filter_has_var() 函数、filter_id() 函数、filter_input() 函数及其数组形式、filter_list() 函数以及 filter_var() 和其数组形式。同时,详细介绍了各种过滤器的用途和用法。 ... [详细]
  • 本文详细探讨了HTML表单中GET和POST请求的区别,包括它们的工作原理、数据传输方式、安全性及适用场景。同时,通过实例展示了如何在Servlet中处理这两种请求。 ... [详细]
  • 本文详细介绍了Python中函数的基本概念,包括函数的定义与调用、文档注释、参数传递(形参与实参)、返回值以及函数嵌套。通过具体示例和解释,帮助读者掌握函数在编程中的应用。 ... [详细]
  • 采用IKE方式建立IPsec安全隧道
    一、【组网和实验环境】按如上的接口ip先作配置,再作ipsec的相关配置,配置文本见文章最后本文实验采用的交换机是H3C模拟器,下载地址如 ... [详细]
  • 主板IO用W83627THG,用VC如何取得CPU温度,系统温度,CPU风扇转速,VBat的电压. ... [详细]
author-avatar
手机用户2602903715
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有