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

一、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文件中也能说过去。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

实时聊天如何做,让客户眼前一亮(一)

网站上的实时聊天功能应该非常有用&#xff0c;因为它允许客户支持立即帮助用户。在线实时聊天可以快速轻松地访问客户服务部门&#xff0c;而它也代表着企业的门面。 让我们讨论一下如何利用SaleSmartly&#xff08;ss客服&#xff09;在网站中的实时聊天视图如何提供出色的实…

纯前端JS实现文件上传解析渲染页面

AI真的能代替前端吗&#xff1f; 回答&#xff1a;不会完全代替 能用吗&#xff1f;复制到项目中只会报错 爆红 ……他完全不能理解你需要什么JavaScript&#xff08;简称JS&#xff09;是一种轻量级的脚本语言&#xff0c;主要用于在Web页面上添加交互行为。它由三个不同的…

项目环境配置、不知晓问题自己搜索后得到的解答

目录 Anolis OS龙蜥操作系统 Kernel Selection 4.18.0(RHCK) Compatible with RHEL (kernel-4.18.0) 4.19.91(ANCK) Support Anolis OS verified platform (kernel-4.19.91) 这两个内核选择哪个比较好呢&#xff1f; 我的C盘有些满&#xff0c;我该如何删除一些我需要的东西…

家用洗地机哪个好用?家用洗地机分享

洗地机是一种代表现代化清洁的设备&#xff0c;它具有高效、环保、经济、智能等多种特点。洗地机可以为您提供先进的清洁技术和设备&#xff0c;为您的清洁工作提供有力的支持。洗地机可以适应不同场所和建筑物的需求&#xff0c;提高工作效率和卫生形象。因此&#xff0c;选择…

logstash介绍和使用-ELK文章2

官方 Logstash 是免费且开放的服务器端数据处理管道&#xff0c;能够从多个来源采集数据&#xff0c;转换数据&#xff0c;然后将数据发送到您最喜欢的“存储库”中。 下载和文档&#xff1a;https://www.elastic.co/cn/logstash/ docker部署&#xff1a;https://hub.docker.…

Linux中信号的基础知识

信号的概念 Linux操作系统中&#xff0c;信号是一种进程间通信&#xff08;Inter-Process Communication, IPC&#xff09;机制&#xff0c;用于向其他进程发送通知或指示&#xff0c;通常是为了通知特定事件的发生&#xff0c;如程序终止、用户按下特定按键等。信号提供了一种…

在字节跳动做了6年软件测试,4月无情被辞,想给划水的兄弟提个醒

先简单交代一下背景吧&#xff0c;某不知名 985 的本硕&#xff0c;17 年毕业加入字节&#xff0c;以“人员优化”的名义无情被裁员&#xff0c;之后跳槽到了有赞&#xff0c;一直从事软件测试的工作。之前没有实习经历&#xff0c;算是6年的工作经验吧。 这6年之间完成了一次…

浅谈springboot启动过程

1. 知识回顾 为了后文方便&#xff0c;我们先来回顾一下spring的一些核心概念。 spring最核心的功能无非是ioc容器&#xff0c;这个容器里管理着各种bean。ioc容器反映在java类上就是spring的核心类ApplicationContext。ApplicationContext有众多的子接口和子类&#xff0c;不…

文旅数字人出圈不断,文旅数字人宣传片制作赋能数字文旅新业态

在文旅产业发展中 数字人的应用越来越广泛 文旅借助数字人浪潮 把传统文化与科技相融合 以Z世代年轻群体所喜爱的方式 推动文旅数字化发展 文旅数字人应用场景——数字人文旅宣传片 可以让数字人作为文旅宣传片主角&#xff0c;串联当地代表性的历史文化地标、现代都市场景…

sensor的感光原理

文章内容来自网络&#xff0c;联系我可以删掉。 目录 CMOS sensor上有什么&#xff1f; 不同像素对应的图像质量&#xff1a; 像点感光原理&#xff1a; Bayer格式变换成RGB格式&#xff1a; CMOS sensor上有什么&#xff1f; CMOS sensor 通常由像敏单元阵列、行驱动器、…

第10章:数据处理增删改

一、插入数据 CREATE TABLE emp1 (id int(11) ,name varchar(15) ,hire_date date ,salary double(10,2) )1.添加一条数据 ①没有指明添加的字段&#xff0c;一定按照顺序添加 insert into emp1 values(1,wang,2000-4-4,5900)②指明添加的字段&#xff08;推荐&#xff09;…

【CSS3】CSS3 属性选择器 ( CSS3 简介 | 属性选择器 | 属性选择器权重 )

文章目录 一、CSS3 简介二、CSS3 属性选择器权重三、CSS3 属性选择器 一、CSS3 简介 CSS3 是在 CSS2 基础上进行扩展后的样式 ; 在 移动端 对 CSS3 的支持 要比 PC 端支持的更好 , 建议在移动端开发时 , 多使用 CSS3 ; PC 端老版本浏览器不支持 CSS3 , 尤其是 IE 9 及以下的版…

Qt5.9学习笔记-事件(五) 事件调试和排查

⭐️我叫忆_恒心&#xff0c;一名喜欢书写博客的在读研究生&#x1f468;‍&#x1f393;。 如果觉得本文能帮到您&#xff0c;麻烦点个赞&#x1f44d;呗&#xff01; 近期会不断在专栏里进行更新讲解博客~~~ 有什么问题的小伙伴 欢迎留言提问欧&#xff0c;喜欢的小伙伴给个三…

【超详细】【YOLOV8使用说明】一套框架解决CV的5大任务:目标检测、分割、姿势估计、跟踪和分类任务【含源码】

目录 1.简介2.环境安装2.1安装torch相关库2.2 获取yolov8最新版本&#xff0c;并安装依赖 3. 如何使用模型用于各种CV任务3.1 目标检测任务实现检测图片代码检测视频代码 3.2 分割任务实现分割图片代码分割视频代码 3.3 追踪任务3.4 姿态检测任务姿态检测&#xff08;图片&…

数据结构之“树”——二叉树、红黑树、B树、B+树、B*树

这篇文章主要简单总结下二叉树、红黑树、B树、B树、B*树的基本结构和原理。 一、二叉树 二叉树就是度不超过2的树(每个结点最多有两个子结点)。 二叉树是有序树&#xff08;二叉排序树&#xff09;&#xff0c;若将其左右子树颠倒&#xff0c;则成为另一棵不同的二叉树。 二叉…

php+vue+mysql医院医护人员医生排班系统

本医护人员排班系统管理员&#xff0c;医护。管理员功能有个人中心&#xff0c;医院信息管理&#xff0c;医护信息管理&#xff0c;医护类型管理&#xff0c;排班信息管理&#xff0c;排班类型管理&#xff0c;科室信息管理&#xff0c;投诉信息管理。医护人员可以修改自己的个…

「二线豪华」或成历史,理想反超沃尔沃再树「里程碑」

今年的上海车展&#xff0c;除了占据C位的新能源汽车&#xff0c;还有传统车企。 上海车展开幕前&#xff0c;沃尔沃汽车大中华区销售公司总裁钦培吉在新车发布会上直言&#xff1a;“新势力会的&#xff0c;我们三年就学会了&#xff1b;我们会的&#xff0c;新势力十年都学不…

Android安装apk出现 “安装包无效”或“安装包不兼容”的解决方案

Android 安装apk出现“安装包无效”或“安装包不兼容”解决方案 1. 问题出现2. 配置 build.gradle3. 生成Signed APK 1. 问题出现 使用Android Studio安装apk到手机一切正常&#xff0c;但是分享出去出现安装apk出现“安装包无效”或“安装包不兼容”问题 这种情况需要我们设…

4 IK分词器

4 IK分词器 4.1测试分词器 在添加文档时会进行分词&#xff0c;索引中存放的就是一个一个的词&#xff08;term&#xff09;&#xff0c;当你去搜索时就是拿关键字去匹配词&#xff0c;最终 找到词关联的文档。 测试当前索引库使用的分词器&#xff1a; post 发送&#xff…

【分布式理论】聊一下 ACID、BASE、CAP、FLP

分布式理论基础 今天我们来聊一下分布式相关基础理论基础&#xff0c;上一篇文章中&#xff0c;我描述了一下分布式系统的纲&#xff0c;但是想要入手学习分布式系统设计&#xff0c;其实需要先从基本理论开始。而知名的ACID、BASE、CAP、FLP都是相关的理论基础。 ACID ACID…