热门标签 | 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中也能凑合说得过去。


推荐阅读
  • C++字符字符串处理及字符集编码方案
    本文介绍了C++中字符字符串处理的问题,并详细解释了字符集编码方案,包括UNICODE、Windows apps采用的UTF-16编码、ASCII、SBCS和DBCS编码方案。同时说明了ANSI C标准和Windows中的字符/字符串数据类型实现。文章还提到了在编译时需要定义UNICODE宏以支持unicode编码,否则将使用windows code page编译。最后,给出了相关的头文件和数据类型定义。 ... [详细]
  • Nginx使用AWStats日志分析的步骤及注意事项
    本文介绍了在Centos7操作系统上使用Nginx和AWStats进行日志分析的步骤和注意事项。通过AWStats可以统计网站的访问量、IP地址、操作系统、浏览器等信息,并提供精确到每月、每日、每小时的数据。在部署AWStats之前需要确认服务器上已经安装了Perl环境,并进行DNS解析。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • Linux重启网络命令实例及关机和重启示例教程
    本文介绍了Linux系统中重启网络命令的实例,以及使用不同方式关机和重启系统的示例教程。包括使用图形界面和控制台访问系统的方法,以及使用shutdown命令进行系统关机和重启的句法和用法。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • 本文讨论了在openwrt-17.01版本中,mt7628设备上初始化启动时eth0的mac地址总是随机生成的问题。每次随机生成的eth0的mac地址都会写到/sys/class/net/eth0/address目录下,而openwrt-17.01原版的SDK会根据随机生成的eth0的mac地址再生成eth0.1、eth0.2等,生成后的mac地址会保存在/etc/config/network下。 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • Windows7 64位系统安装PLSQL Developer的步骤和注意事项
    本文介绍了在Windows7 64位系统上安装PLSQL Developer的步骤和注意事项。首先下载并安装PLSQL Developer,注意不要安装在默认目录下。然后下载Windows 32位的oracle instant client,并解压到指定路径。最后,按照自己的喜好对解压后的文件进行命名和压缩。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • Python正则表达式学习记录及常用方法
    本文记录了学习Python正则表达式的过程,介绍了re模块的常用方法re.search,并解释了rawstring的作用。正则表达式是一种方便检查字符串匹配模式的工具,通过本文的学习可以掌握Python中使用正则表达式的基本方法。 ... [详细]
  • imx6ull开发板驱动MT7601U无线网卡的方法和步骤详解
    本文详细介绍了在imx6ull开发板上驱动MT7601U无线网卡的方法和步骤。首先介绍了开发环境和硬件平台,然后说明了MT7601U驱动已经集成在linux内核的linux-4.x.x/drivers/net/wireless/mediatek/mt7601u文件中。接着介绍了移植mt7601u驱动的过程,包括编译内核和配置设备驱动。最后,列举了关键词和相关信息供读者参考。 ... [详细]
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 服务器上的操作系统有哪些,如何选择适合的操作系统?
    本文介绍了服务器上常见的操作系统,包括系统盘镜像、数据盘镜像和整机镜像的数量。同时,还介绍了共享镜像的限制和使用方法。此外,还提供了关于华为云服务的帮助中心,其中包括产品简介、价格说明、购买指南、用户指南、API参考、最佳实践、常见问题和视频帮助等技术文档。对于裸金属服务器的远程登录,本文介绍了使用密钥对登录的方法,并提供了部分操作系统配置示例。最后,还提到了SUSE云耀云服务器的特点和快速搭建方法。 ... [详细]
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社区 版权所有