uboot移植Linux-SD驱动代码解析

news2025/1/12 2:56:18

一、uboot与linux驱动

1.1、uboot本身是裸机程序

(1)狭义的驱动概念是指:操作系统中用来具体操控硬件的代码叫驱动
广义的驱动概念是指:凡是操控硬件的代码都叫驱动
(2)裸机程序中是直接使用寄存器的物理地址来操控硬件的,操作系统中必须通过驱动来操控硬件。
这两个有什么区别?本质区别就是分层。

1.2、uboot的虚拟地址对硬件操作的影响

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

(2)uboot早期也是使用纯物理地址工作的,但是现在的uboot开启了MMU做了虚拟地址映射,因此要将驱动移植到uboot必须考虑真实操控的是不是同一个物理地址。
查uboot中的虚拟地址映射表,发现除了0x30000000-0x3FFFFFFF映射到了0xC0000000-0xCFFFFFFF之外,其余的虚拟地址空间全是原样映射的。
而我们驱动中主要是操控硬件寄存器,而S5PV210的SFR都在0xExxxxxx地址空间,因此驱动中不必考虑虚拟地址。

1.3、uboot借用(移植)了linux驱动

(1)linux驱动本身做了分文件的模块化设计。linux驱动本身和linux内核不是强耦合的,这是linux驱动可以被uboot借用(移植)的关键。
(2)linux驱动本身有更复杂的框架,实现了很多的附带功能,而uboot本质上只是个裸机程序,uboot移植linux驱动时只是借用了linux驱动的一部分而已。

二、iNand/SD驱动解析(从start_armboot中的mmc_initialize开始)

(1)驱动整体非常庞大,涉及很多个文件夹下的很多文件,函数更多,我们从start_armboot函数中的驱动初始化部分入手。
(2)mmc_initialize在start_armboot函数604行处调用,函数定义在:uboot/drivers/mmc/mmc.c中。
uboot移植的linux驱动都放在了uboot/drivers目录。

int mmc_initialize(bd_t *bis)
{
    struct mmc *mmc;
    int err;

    INIT_LIST_HEAD(&mmc_devices);
    //1.这句代码的含义是:初始化全局变量mmc_devices
    //2.初始化的值是:将mmc_devices这个结构体类型的变量中的next、prev指针均指向自己
    //3.mmc_devices的作用:是用来记录系统中所有已经注册的SD/iNand设备
    //4.举例:向系统中插入一个SD卡/iNand设备,则系统驱动就会向mmc_devices链表中增加一个节点表示这个设备。
    cur_dev_num = 0;
    //cur_dev_num的作用:记录当前正在操作的SD卡设备的设备号

    if (board_mmc_init(bis) < 0)
        cpu_mmc_init(bis);

#if defined(DEBUG_S3C_HSMMC)
    print_mmc_devices(',');
#endif

#ifdef CONFIG_CHECK_X210CV3
    mmc = find_mmc_device(1);//lqm
#else
    mmc = find_mmc_device(0);
#endif
    if (mmc) {
        err = mmc_init(mmc);
        if (err)
            err = mmc_init(mmc);
        if (err) {
            printf("Card init fail!\n");
            return err;
        }
    }
    printf("%ldMB\n", (mmc->capacity/(1024*1024/(1<<9))));
    return 0;
}

(3)mmc_initialize这个函数的作用就是初始化板上的MMC系统。
MMC系统的初始化应该包含这么两部分:
SoC里的SD/MMC控制器初始化(MMC系统时钟的初始化、GPIO初始化、SFR初始化)
SD卡/iNand 内部控制器的初始化。
//SD卡有很多种状态,获取到不同的命令,就会执行不同的操作,然后进入下一种状态;
//在SD卡中有个小型控制器,封装了读写SD卡的函数。
//这里的初始化SD卡就是为了 初始化SD卡的状态到合适。

(4)mmc_devices链表(全局变量),用来记录系统中所有已经注册的SD/iNand设备。所以向系统中插入一个SD卡/iNand设备,则系统驱动就会向mmc_devices链表中插入一个数据结构表示这个设备。

2.1、cpu_mmc_init(mmc_initialize函数1191行处调用)

(1)函数定义在:uboot/cpu/s5pc11x/cpu.c中。实质是通过调用3个函数来完成的。

int cpu_mmc_init(bd_t *bis)
{
#ifdef CONFIG_S3C_HSMMC
    setup_hsmmc_clock();
    setup_hsmmc_cfg_gpio();
    return smdk_s3c_hsmmc_init();
#else
    return 0;
#endif
}

2.1.1、setup_hsmmc_clock

(1)函数定义在:uboot/cpu/s5pc11x/setup_hsmmc.c中。看名字函数是用来初始化SoC中MMC控制器的时钟部分的。


void setup_hsmmc_clock(void)
{
    u32 tmp;
    u32 clock;
    u32 i;

    /* MMC0 clock src = SCLKMPLL */
    //CLK_SRC4低4位设置为0110,意思是设置MMC0通道的源时钟为SCLKMPLL
    tmp = CLK_SRC4_REG & ~(0x0000000f);
    CLK_SRC4_REG = tmp | 0x00000006;

    /* MMC0 clock div */
    tmp = CLK_DIV4_REG & ~(0x0000000f);//CLK_DIV4di4位清0
    clock = get_MPLL_CLK()/1000000;//MPLL_CLK = 667MHz,clock = 667
    for(i=0; i<0xf; i++)
    {
        if((clock / (i+1)) <= 50) { 667/14 < 50, i+1 = 14, i = 13 = (1101)
            CLK_DIV4_REG = tmp | i<<0;    //对MPLL_CLK(667M)进行14分频得到SCLKMPLL(47M)
            break;
        }
    }


#ifdef USE_MMC2
    /* MMC2 clock src = SCLKMPLL */
    tmp = CLK_SRC4_REG & ~(0x00000f00);
    CLK_SRC4_REG = tmp | 0x00000600;

    /* MMC2 clock div */
    tmp = CLK_DIV4_REG & ~(0x00000f00);
    CLK_DIV4_REG = tmp | i<<8;
#endif


}

2.1.2、setup_hsmmc_cfg_gpio

(1)函数定义在:uboot/cpu/s5pc11x/setup_hsmmc.c中。看名字函数是用来配置SoC中MMC控制器相关的GPIO的。

2.1.3、smdk_s3c_hsmmc_init

(1)函数定义在:uboot/drivers/mmc/s3c_hsmmc.c中。函数内部通过宏定义USE_MMCx来决定是否调用s3c_hsmmc_initialize来进行具体的初始化操作。

int smdk_s3c_hsmmc_init(void)
{
    int err;

#ifdef USE_MMC0            //x210使用这里
    err = s3c_hsmmc_initialize(0);
    if(err)
        return err;
#endif

#ifdef USE_MMC1
    err = s3c_hsmmc_initialize(1);
    if(err)
        return err;
#endif    

#ifdef USE_MMC2           //x210使用这里
    err = s3c_hsmmc_initialize(2);
    if(err)
        return err;
#endif    

#ifdef USE_MMC3
    err = s3c_hsmmc_initialize(3);
    if(err)
        return err;
#endif
    return -1;
}

2.1.3.1、s3c_hsmmc_initialize

(1)函数定义在:uboot/drivers/mmc/s3c_hsmmc.c中。

static int s3c_hsmmc_initialize(int channel)
{
    struct mmc *mmc;
    //struct mmc封装了SD卡所有相关的信息和操作函数,和C++中使用类来面向对象很像。

    mmc = &mmc_channel[channel];

    sprintf(mmc->name, "S3C_HSMMC%d", channel);
    mmc->priv = &mmc_host[channel];
    mmc->send_cmd = s3c_hsmmc_send_command;
    mmc->set_ios = s3c_hsmmc_set_ios;
    mmc->init = s3c_hsmmc_init;

    mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
    mmc->host_caps = MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS;
#if defined(USE_MMC0_8BIT) || defined(USE_MMC2_8BIT)
    mmc->host_caps |= MMC_MODE_8BIT;
#endif

    mmc->f_min = 400000;
    mmc->f_max = 52000000;

    mmc_host[channel].clock = 0;

    switch(channel) {
    case 0:
        mmc_host[channel].ioaddr = (void *)ELFIN_HSMMC_0_BASE;
        break;
    case 1:
        mmc_host[channel].ioaddr = (void *)ELFIN_HSMMC_1_BASE;
        break;
    case 2:
        mmc_host[channel].ioaddr = (void *)ELFIN_HSMMC_2_BASE;
        break;
#ifdef USE_MMC3
    case 3:
        mmc_host[channel].ioaddr = (void *)ELFIN_HSMMC_3_BASE;
        break;
#endif
    default:
        printk("mmc err: not supported channel %d\n", channel);
    }
    
    return mmc_register(mmc);
}

(2)定义一个指向struct mmc类型的指针,指向mmc_channel数组中的元素,mmc_channel数组中的每个元素都是struct mmc类型的对象,之后填充这个指针指向的对象的成员和成员函数,最后调用mmc_register函数来向驱动框架注册这个mmc设备驱动。

int mmc_register(struct mmc *mmc)
{
    /* Setup the universal parts of the block interface just once */
    mmc->block_dev.if_type = IF_TYPE_MMC;
    mmc->block_dev.dev = cur_dev_num++;
    mmc->block_dev.removable = 1;
    mmc->block_dev.block_read = mmc_bread;
    mmc->block_dev.block_write = mmc_bwrite;

    INIT_LIST_HEAD(&mmc->link);

    list_add_tail(&mmc->link, &mmc_devices);//将该设备插入内核链表

    return 0;
}

(3)mmc_register功能是进行mmc设备的注册,注册方法就是将当前这个mmc插入到mmc_devices这个内核链表中去。

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

(5)至此cpu_mmc_init函数分析完成。完成了以下工作:
SoC里的MMC控制器初始化(MMC系统时钟的初始化、SFR初始化、GPIO初始化)

2.2、find_mmc_device

(1)函数定义在:uboot/drivers/mmc/mmc.c中。

struct mmc *find_mmc_device(int dev_num)
{
    struct mmc *m;
    struct list_head *entry;

    list_for_each(entry, &mmc_devices) {
        m = list_entry(entry, struct mmc, link);

        if (m->block_dev.dev == dev_num)
            return m;
	 }

    printf("MMC Device %d not found\n", dev_num);

    return NULL;
}

(2)这个函数的作用就是通过mmc设备号在内核链表中查找对应的mmc设备(struct mmc的对象,根据上面2.1.3.1.(4)分析系统中有2个mmc设备,编号分别是0和2)。函数工作原理就是遍历mmc_devices链表,去依次寻找注册了的mmc设备,然后对比设备编号,如果相同则就说明找到了设备。找到后调用mmc_init函数来初始化这个设备。

2.3、mmc_init

(1)函数定义在:drivers/mmc/mmc.c中。

(2)这个函数应该是要进行mmc卡的初始化了(前面已经完成了SoC端控制器的初始化)

(3)函数的调用关系为:

mmc_init                    //发送若干个命令码初始化SD卡内部的控制器
	mmc_go_idle            //命令1
		mmc_send_cmd        //发送命令1
	mmc_send_if_cond        //命令2
		mmc_send_cmd        //发送命令2
	······

可以看出,mmc_init函数内部就是依次通过向mmc卡发送命令码(CMD0、CMD2那些)来初始化SD卡/iNand内部的控制器,以达到初始化SD卡的目的。

三、总结

(1)至此整个MMC系统初始化结束。
uboot中的iNand/SD驱动总体的调用关系:
(2)整个MMC系统初始化分为2大部分:SoC这一端的MMC控制器的初始化,SD卡这一端卡本身的初始化。前一部分主要是在cpu_mmc_init函数中完成,后一部分主要是在mmc_init函数中完成。

(3)整个初始化完成后去使用sd卡/iNand时,操作方法和mmc_init函数中初始化SD卡的操作方式一样。读写sd卡时也是通过总线向SD卡发送命令、读取/写入数据来完成的。

(4)顺着mmc_init函数中发送命令的操作追下去,到了mmc_send_cmd函数处就断了,真正的向SD卡发送命令的硬件操作的函数找不到。

(5)struct mmc结构体是关键。两部分初始化之间用使用mmc结构体来联系,初始化完了后对mmc卡的常规读写操作也是通过mmc结构体来链接的。

四、linux驱动前奏

4.1、struct mmc

(1)驱动的设计中有一个关键数据结构,每个驱动都对应着一个数据结构。譬如MMC驱动的结构体就是struct mmc,这个结构体中包含一些变量和一些函数指针,变量用来记录SD卡相关的属性,函数指针用来记录SD卡相关的操作方法。这些变量和函数指针加起来就构成了驱动。驱动就被抽象为这个结构体。

(2)一个驱动工作时主要就分两部分(以iNand/SD驱动为例子):
驱动构建(构建一个struct mmc对象然后填充它)、驱动运行时(调用这个对象里的函数指针和变量)

4.2、分离思想—面向对象

(1)分离思想就是说在驱动中将操作方法和数据分开,然后封装在一个结构体中。

(2)操作方法就是函数,数据就是变量。所谓操作方法和数据分离的意思就是:在不同的地方来存储和管理驱动的操作方法和变量,这样的优势就是驱动便于移植。

4.3、分层思想

(1)分层思想是指一整个的驱动分为好多个层次。驱动分为很多个源文件,放在很多个文件夹中。

(2)以mmc驱动为例来分析各个文件的作用:
uboot/drivers/mmc/mmc.c:本文件的主要内容是和MMC卡操作有关的方法,譬如MMC卡设置复位状态的mmc_go_idle、卡读写数据等。但是本文件中并没有具体的硬件寄存器操作函数,操作最终指向的是struct mmc结构体中的函数指针send_cmd,这些函数指针是在驱动构建的时候和真正硬件操作函数挂勾的(真正的硬件操作函数在别的文件中)。

uboot/drivers/mmc/s3c_hsmmc.c:本文件中主要内容是SoC内部MMC控制器的硬件寄存器操作方法,真正的寄存器操作就在这里,譬如向SD卡发送命令的函数(s3c_hsmmc_send_command),譬如和SD卡读写数据的函数(s3c_hsmmc_set_ios),这些函数就是具体操作硬件的函数,也就是mmc.c中函数指针最终指向的函数。这些函数在mmc驱动初始化构建时(cpu_mmc_init-> smdk_s3c_hsmmc_init-> s3c_hsmmc_initialize函数中)和struct mmc对象挂勾起来备用。

分析:mmc.c和s3c_hsmmc.c构成了一个分层,mmc.c中调用了s3c_hsmmc.中的函数,所以mmc.c在上层,s3c_hsmmc.c在下层。这两个分层后我们发现mmc.c中不涉及具体硬件的操作,s3c_hsmmc.c中不涉及驱动工作时的时序操作(发送命令的先后顺序)。因此移植的时候就有好处:譬如我们要把这一套mmc驱动移植到别的SoC上,mmc.c就不用动,s3c_hsmmc.c动就可以了;譬如SoC没变但是SD卡升级了,这时候只需要更换mmc.c,不需要更换s3c_hsmmc.c即可。

cpu/s5pc110/下面还有一个setup_hsmmc.c,也和MMC驱动有关。但是这些代码为什么不能放到drivers目录下去,而要放到cpu目录下去?因为这里面的2个函数(setup_hsmmc_clock和setup_hsmmc_cfg_gpio)都是和SoC内部有关的初始化函数,这两个函数不能放到drivers目录下去。实际上如果非把这两个函数放在uboot/drivers/mmc/s3c_hsmmc.c文件中也能说过去。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/502159.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

最新版千帆直播网站系统PHP完整版源码(PC+WAP在线观看视频)附安装教程

最新版千帆直播网站PHP完整版源码&#xff0c;PCWAP在线观看视频直播系统 安装方法&#xff1a; 1、导入数据库文件 zhibo.sql 2、修改数据库配置文件 有多处包含UC配置; 根目录&#xff1a;config.inc.php – config.php 其他路径&#xff1a; Conf/config.php Admin/C…

JVM(三):JVM命令与参数

JVM命令与参数 文章目录 JVM命令与参数JVM参数标准参数-X 参数-XX参数其他参数说明常用参数的意义 常用命令jpsjinfojstatjstackjmap 常用工具jconsolejvisualvm内存分析工具 MATGC日志分析工具内存分析工具 MATGC日志分析工具 经过前面的各种分析学习&#xff0c;我们知道了关…

淦,服务器被人传了后门木马。。。

「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 今天很暴躁&#xff0c;因为睡眠被打扰了。 一个朋友大半夜打我电话&#xff0c;说她云服…

ThingsBoard 接入摄像头方案

0、上图 废话不多说,先给大家来个效果图: 1、概述 最近,我在群里看到有很多兄弟向我咨询摄像头接入到tb的方案,这个就是找对人了,后续我会截图我当初做的东西,其实这个很简单,而且我这种方法是最好的,下面给大家一一道来。 我总结了下面几种情况,其实关键在于摄像头…

QML画布元素

在早些时候的Qt4中加入QML时&#xff0c;一些开发者讨论如何在QtQuick中绘制一个圆形。类似圆形的问题&#xff0c;一些开发者也对于其它的形状的支持进行了讨论。在QtQuick中没有圆形&#xff0c;只有矩形。在Qt4中&#xff0c;如果你需要一个除了矩形外的形状&#xff0c;你需…

浅谈电力物联网在智能配电系统应用

摘要&#xff1a; 在社会经济和科学技术不断发展中&#xff0c;配电网实现了角色转变&#xff0c;传统的单向供电服务形式已经被双向能流服务形式取代&#xff0c;社会多样化的用电需求也得以有效满足。随着物联网技术的发展&#xff0c;泛在电力物联网开始应用于当今的电力系…

使用【SD-WEBUI】插件生成单张图包含多个人物:分区域的提示词

文章目录 &#xff08;零&#xff09;前言&#xff08;一&#xff09;潜变量成对&#xff08;Latent Couple&#xff09;&#xff08;1.1&#xff09;可自组LoRA&#xff08;Composable LoRA&#xff09; &#xff08;二&#xff09;分区扩散&#xff08;Multi Diffusion&#…

@Configuration(proxyBeanMethods = false) 解析

又是美好的一天呀~ 个人博客地址&#xff1a; huanghong.top 往下看看~ Configuration(proxyBeanMethods false) 解析proxyBeanMethods分析总结 Configuration(proxyBeanMethods false) 解析 最近看一些源码的时候&#xff0c;发现很多Configuration配置类上Configuration(p…

Mysql 学习(九)多表连接原理

连接介绍 为了更加方便的介绍一下连接&#xff0c;我们先创建两个表格 t1 和 t2 CREATE TABLE t1 (m1 int, n1 char(1));CREATE TABLE t2 (m2 int, n2 char(1));INSERT INTO t1 VALUES(1, a), (2, b), (3, c);INSERT INTO t2 VALUES(2, b), (3, c), (4, d);连接的本质是将各个…

GitHub上的AutoGPT神秘的面纱

最近一直在说AutoGPT&#xff0c;有很多的视频介绍&#xff0c;但是本着收藏等于学会的原则&#xff0c;收藏一堆一直没看。 这里用一句话说明白&#xff1a;AutoGPT就是一个用Python套装的壳子&#xff0c;用来更省事的调用OpenAI的API。&#xff08;如果你不明白API&#xf…

WiFi(Wireless Fidelity)基础(二)

目录 一、基本介绍&#xff08;Introduction&#xff09; 二、进化发展&#xff08;Evolution&#xff09; 三、PHY帧&#xff08;&#xff08;PHY Frame &#xff09; 四、MAC帧&#xff08;MAC Frame &#xff09; 五、协议&#xff08;Protocol&#xff09; 六、安全&#x…

ByteBuffer的讲解和使用

1.它其实就是一个数据读取或者写入的一个缓冲区 2.基本的操作步骤&#xff1a; 向buffer写入数据&#xff0c;例如调用channel.read(buffer)调用flip()切换至读模式从buffer读取数据&#xff0c;例如调用buffer.get()调用clear()或者compact()切换至写模式重复以上步骤 3.内部…

企业商务租车为工作提供便利

在当代的忙碌生活中&#xff0c;我们总会遇到各种各样的烦恼。最突出的是企业在商务工作中&#xff0c;常常会因为各种原因而导致耽误时间&#xff0c;如火急火燎的去谈生意&#xff0c;却遇到了堵车的现象&#xff0c;或者车辆出现问题而导致耽误时间&#xff0c;而广州商务租…

ICV:中国车载超声波雷达市场规模预计2024年可达20亿美元

近年来&#xff0c;由于市场对车辆先进安全功能的需求的增加&#xff0c;汽车超声波传感器市场一直保持稳步增长。ICV估计&#xff0c;车载超声波传感器全球市场预计在2022年至2027年之间以11.5&#xff05;的复合年增长率增长&#xff0c;这种增长是由越来越多的高级驾驶辅助系…

python 开发 1 之 拷贝文件

目录 一、需求&#xff1a; 二、python拷贝分析 1、需要的库&#xff0c;及源路径、目标路径定义 2、定义的拷贝数组 3、自定义拷贝函数 1&#xff09; 如果目标路径不存在时&#xff0c;先创建目标路径 2&#xff09;遍历元组数组中的文件 3&#xff09;如果源文件或目…

【JavaEE初阶】多线程进阶(六)JUC 线程安全的集合类

文章目录 JUC&#xff08;java.util.concurrent&#xff09;的常见类Callable接口相关面试题 ReentrantLock(可重入锁)原子类信号量SemaphoreCountDownLatch 线程安全的集合类多线程环境使用 ArrayList多线程使用队列多线程使用哈希表&#xff08;重点&#xff09;相关面试题 J…

2.sql server数据表的管理(实验报告)

目录 一﹑实验目的 二﹑实验平台 三﹑实验内容和步骤 四﹑命令(代码)清单 五﹑运行结果 一﹑实验目的 掌握使用SQL Server管理平台和Transact-SQL语句Create table和Alter table创建和修改表的方法&#xff1b;掌握在SQL Server管理平台中对表进行插入、修改和删除数据操作…

哪种蓝牙耳机戴着最舒服?久戴不痛的蓝牙耳机推荐

很多喜欢跑步或通勤的时候带着耳机听音乐&#xff0c;而现在无线耳机市场规模扩大之后&#xff0c;也开始走向更加细分的市场&#xff0c;以满足越来越不同的差异化需求&#xff0c;但是佩戴的舒适度是很多人关注的&#xff0c;下面整理了几款佩戴舒适度高的蓝牙耳机&#xff0…

国考省考结构化面试:组织管理题,调研题,宣传题,活动题,整治题

国考省考结构化面试&#xff1a;组织管理题&#xff0c;调研题&#xff0c;宣传题&#xff0c;活动题&#xff0c;整治题 2022找工作是学历、能力和运气的超强结合体! 公务员特招重点就是专业技能&#xff0c;附带行测和申论&#xff0c;而常规国考省考最重要的还是申论和行测…

基于python语言dlib库和opencv库的视频眨眼检测

功能说明&#xff1a; 基于python编程语言&#xff0c;使用dlib 和opencv开发的视频眨眼检测。 环境&#xff1a; * python 3.6.8 * opencv 3.4.2.16 * dlib 19.7.0 原理&#xff1a; 1.使用opencv-python读取处理视频图像 2.使用线程机制处理人脸检测关键点 3.根…