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


推荐阅读
  • 本文详细介绍了如何搭建一个高可用的MongoDB集群,包括环境准备、用户配置、目录创建、MongoDB安装、配置文件设置、集群组件部署等步骤。特别关注分片、读写分离及负载均衡的实现。 ... [详细]
  • CentOS下ProFTPD的安装与配置指南
    本文详细介绍在CentOS操作系统上安装和配置ProFTPD服务的方法,包括基本配置、安全设置及高级功能的启用。 ... [详细]
  • 本文详细介绍了如何在 Ubuntu 14.04 系统上搭建仅使用 CPU 的 Caffe 深度学习框架,包括环境准备、依赖安装及编译过程。 ... [详细]
  • 为何Compose与Swarm之后仍有Kubernetes的诞生?
    探讨在已有Compose和Swarm的情况下,Kubernetes是如何以其独特的设计理念和技术优势脱颖而出,成为容器编排领域的领航者。 ... [详细]
  • spring boot使用jetty无法启动 ... [详细]
  • 从理想主义者的内心深处萌发的技术信仰,推动了云原生技术在全球范围内的快速发展。本文将带你深入了解阿里巴巴在开源领域的贡献与成就。 ... [详细]
  • 解决Win10 1709版本文件共享安全警告问题
    每当Windows 10发布新版本时,由于兼容性问题往往会出现各种故障。近期,一些用户在升级至1709版本后遇到了无法访问共享文件夹的问题,系统提示‘文件共享不安全,无法连接’。本文将提供多种解决方案,帮助您轻松解决这一难题。 ... [详细]
  • MITM(中间人攻击)原理及防范初探(二)
    上一篇文章MITM(中间人攻击)原理及防范初探(一)给大家介绍了利用ettercap进行arp欺骗及劫持明文口令,后来我发现好友rootoorotor的文章介绍比我写的更透彻,所以基础利用大家可以参看 ... [详细]
  • Fiddler 安装与配置指南
    本文详细介绍了Fiddler的安装步骤及配置方法,旨在帮助用户顺利抓取用户Token。文章还涵盖了一些常见问题的解决方案,以确保安装过程顺利。 ... [详细]
  • 本文探讨了如何在PHP与MySQL环境中实现高效的分页查询,包括基本的分页实现、性能优化技巧以及高级的分页策略。 ... [详细]
  • Docker安全策略与管理
    本文探讨了Docker的安全挑战、核心安全特性及其管理策略,旨在帮助读者深入理解Docker安全机制,并提供实用的安全管理建议。 ... [详细]
  • 本文详细介绍了 `org.apache.tinkerpop.gremlin.structure.VertexProperty` 类中的 `key()` 方法,并提供了多个实际应用的代码示例。通过这些示例,读者可以更好地理解该方法在图数据库操作中的具体用途。 ... [详细]
  • 洛谷 P4009 汽车加油行驶问题 解析
    探讨了经典算法题目——汽车加油行驶问题,通过网络流和费用流的视角,深入解析了该问题的解决方案。本文将详细阐述如何利用最短路径算法解决这一问题,并提供详细的代码实现。 ... [详细]
  • 本文详细介绍了在Linux操作系统上安装和部署MySQL数据库的过程,包括必要的环境准备、安装步骤、配置优化及安全设置等内容。 ... [详细]
  • 深入解析WebP图片格式及其应用
    随着互联网技术的发展,无论是PC端还是移动端,图片数据流量占据了很大比重。尤其在高分辨率屏幕普及的背景下,如何在保证图片质量的同时减少文件大小,成为了亟待解决的问题。本文将详细介绍Google推出的WebP图片格式,探讨其在实际项目中的应用及优化策略。 ... [详细]
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社区 版权所有