Linux MMC子系统分析(二)——Host分析
Linux MMC子系统分析(二)——Host分析
前言
通过前面对mmc子系统的模型分析,我们能够知道host是对应于硬件控制器的具体操作。本文将以sdhci-s3c.c为例对host进行简单的分析。
sdhci-s3c.c驱动代码的构成
实际上很多厂商的控制器代码都是基于sdhci.c这个驱动代码来实现的,不同的厂商调用其提供的函数实现自己驱动的个性化功能。下面将结合sdhci.c和sdhci-s3c.c来分析一个host的具体实现。
Host驱动的分析
通常分析一个驱动可以从它的init函数为入口进行分析,可是在sdhci-s3c.c文件中并没有相应的init函数,不过最驱动的最末端可以看见一个宏的使用
module_platform_driver(sdhci_s3c_driver)
这里通过使用替代了驱动中常见的init和exit函数,进一步查看这个宏:
define module_platform_driver(__platform_driver) \ module_driver(__platform_driver, platform_driver_register, \ platform_driver_unregister)
可以看见driver的register和unregister函数,使用这个宏可以和在init函数调用register以及在exit中调用unregister达到相同的效果。通过分析可以知道,这里是注册了一个platform driver,看一下sdhci_s3c_driver结构体中有哪些内容:
static struct platform_driver sdhci_s3c_driver = { .probe = sdhci_s3c_probe, .remove = sdhci_s3c_remove, .id_table = sdhci_s3c_driver_ids, .driver = { .name = "s3c-sdhci", //驱动名称 .of_match_table = of_match_ptr(sdhci_s3c_dt_match), //设备树匹配.pm = SDHCI_S3C_PMOPS, //电源管理相关 },
};
dt_match中的内容:
static const struct of_device_id sdhci_s3c_dt_match[] = { { .compatible = "samsung,s3c6410-sdhci", }, { .compatible = "samsung,exynos4210-sdhci", .data = (void *)EXYNOS4_SDHCI_DRV_DATA }, {},
};
当我们的设备树中有能够与dt_match的成员相匹配时,就会调用probe函数。这里以列举一下s3c64xx.dtsi中关于sdhci节点的内容:
sdhci0: sdhci@7c200000 {compatible = "samsung,s3c6410-sdhci";reg = <0x7c200000 0x100>;interrupt-parent = <&vic1>;interrupts = <24>;clock-names = "hsmmc", "mmc_busclk.0", "mmc_busclk.2";clocks = <&clocks HCLK_HSMMC0>, <&clocks HCLK_HSMMC0>,<&clocks SCLK_MMC0>;status = "disabled";
};
可以看见设备树中指定了该控制器的寄存器地址、中断以及时钟相关的内容,并没有指定其引脚等内容,因为这些内容需要根据不同板级来进行配置。设备树的节点,会被转换为相应的device,这里的sdhci0在会被转换为一个platform_device,当加载相应的驱动后便会进行匹配从而调用驱动中的probe函数。
sdhci_s3c_probe函数分析
probe函数中完成的主要工作就是申请并添加host、中断注册、以及设备树内容的解析工作;当然这其中还有一些标志位的设置等。这里去掉了很多关于标志位设置,只有一些个人认为和流程相关的函数内容,并对其中的函数进行相应的分析。
static int sdhci_s3c_probe(struct platform_device *pdev)
{host = sdhci_alloc_host(dev, sizeof(struct sdhci_s3c)); ......host->ops = &sdhci_s3c_ops; //这里的ops是提供给sdhci.c调用的,需要结合源码进行阅读......ret = mmc_of_parse(host->mmc); ret = sdhci_add_host(host);
}
1、很多芯片厂商使用的驱动都是结合sdhci.c来实现的,不同芯片厂商的功能设置操作可可能有所差异,因此sdhci.c就提供了一个统一的接来进行管理。这个ops中提供的函数在sdhci.c中的一些位置会进行调用。
host->ops = &sdhci_s3c_ops;
/*----------------------------------------------------------------------------*/
static struct sdhci_ops sdhci_s3c_ops = { .get_max_clock = sdhci_s3c_get_max_clk, //获取能够提供的最大的时钟频率 .set_clock = sdhci_s3c_set_clock, //设置sd卡时钟 .get_min_clock = sdhci_s3c_get_min_clock, .set_bus_width = sdhci_s3c_set_bus_width, //设置bus位宽 4 or 8bit.reset = sdhci_reset, .set_uhs_signaling = sdhci_set_uhs_signaling, //uhs 信号电平切换
};
/*-----------------------------------------------------------------------------*/
2、sdhci_alloc_host函数分析,此函数位于sdhci.c中
sdhci_alloc_host(dev, sizeof(struct sdhci_s3c))mmc = mmc_alloc_host(sizeof(struct sdhci_host) + priv_size, dev);host->mmc_host_ops = sdhci_ops; //硬件操作方法接口mmc->ops = &host->mmc_host_ops;
/*-------------------------------------------------------------------*/
//向core层提供操作方法 按照core提供的统一接口编写的驱动
static const struct mmc_host_ops sdhci_ops = {.request = sdhci_request,.post_req = sdhci_post_req,.pre_req = sdhci_pre_req,.set_ios = sdhci_set_ios,.get_cd = sdhci_get_cd,.get_ro = sdhci_get_ro,.hw_reset = sdhci_hw_reset,.enable_sdio_irq = sdhci_enable_sdio_irq,.start_signal_voltage_switch = sdhci_start_signal_voltage_switch,.prepare_hs400_tuning = sdhci_prepare_hs400_tuning,.execute_tuning = sdhci_execute_tuning,.select_drive_strength = sdhci_select_drive_strength,.card_event = sdhci_card_event,.card_busy = sdhci_card_busy,
};
/*-------------------------------------------------------------------*/
mmc_alloc_host函数
struct mmc_host *mmc_alloc_host(int extra, struct device *dev)
{.........mmc_gpio_alloc(host); //与SD卡检测 cd引脚相关的数据接口建立 spin_lock_init(&host->lock); init_waitqueue_head(&host->wq); INIT_DELAYED_WORK(&host->detect, mmc_rescan); //延迟工作队列 用于后续初始化完成后的SD卡的扫描检测..........
}
3、mmc_of_parse函数,core层对设备树内容的解析
int mmc_of_parse(struct mmc_host *host) //对设备树中的内容进行解析 列举一些关键内容
{/* "bus-width" is translated to MMC_CAP_*_BIT_DATA flags */ if (of_property_read_u32(np, "bus-width", &bus_width) < 0) { dev_dbg(host->parent, "\"bus-width\" property is missing, assuming 1 bibus_width = 1; } switch (bus_width) { //bus_width设置 case 8: host->caps |= MMC_CAP_8_BIT_DATA; /* Hosts capable of 8-bit transfers can also do 4 bits */case 4: host->caps |= MMC_CAP_4_BIT_DATA; break; case 1: break; default: dev_err(host->parent, "Invalid \"bus-width\" value %u!\n", bus_width); return -EINVAL; } of_property_read_u32(np, "max-frequency", &host->f_max); cd_cap_invert = of_property_read_bool(np, "cd-inverted"); // cd引脚翻转 通常无卡为低电平,如果硬件设计为无卡高电平 则可以在设备树中进行指定if (of_property_read_bool(np, "broken-cd")) // 在没有cd检测引脚的情况下,可以使用轮询方式进行检测,需要在设备树中指定host->caps |= MMC_CAP_NEEDS_POLL; ret = mmc_gpiod_request_cd(host, "cd", 0, true, //获取cd检测引脚的gpio相关信息0, &cd_gpio_invert); if (of_property_read_bool(np, "cap-sd-highspeed")) host->caps |= MMC_CAP_SD_HIGHSPEED; if (of_property_read_bool(np, "cap-mmc-highspeed")) host->caps |= MMC_CAP_MMC_HIGHSPEED; if (of_property_read_bool(np, "sd-uhs-sdr12")) //指定支持哪些速度等级的卡 host->caps |= MMC_CAP_UHS_SDR12; if (of_property_read_bool(np, "sd-uhs-sdr25")) //可结合源码进行详细了解 host->caps |= MMC_CAP_UHS_SDR25; if (of_property_read_bool(np, "sd-uhs-sdr50")) host->caps |= MMC_CAP_UHS_SDR50; if (of_property_read_bool(np, "sd-uhs-sdr104")) host->caps |= MMC_CAP_UHS_SDR104; if (of_property_read_bool(np, "sd-uhs-ddr50")) host->caps |= MMC_CAP_UHS_DDR50; if (of_property_read_bool(np, "cap-mmc-hw-reset")) //指定是否支持硬件复位
}
```c
data = pd.read_csv('https://labfile.oss.aliyuncs.com/courses/1283/adult.data.csv')
print(data.head())
4、sdhci_add_host
int sdhci_add_host(struct sdhci_host *host)
{...........sdhci_do_reset(host, SDHCI_RESET_ALL);tasklet_init(&host->finish_tasklet,sdhci_tasklet_finish, (unsigned long)host);setup_timer(&host->timer, sdhci_timeout_timer, (unsigned long)host); //设置每次操作的超时定时器init_waitqueue_head(&host->buf_ready_int);sdhci_init(host, 0); //初始化控制器..........mmc_add_host(mmc); //}int mmc_add_host(struct mmc_host *host)
{........device_add(&host->class_dev);mmc_start_host(host);.......
}
void mmc_start_host(struct mmc_host *host)
{ host->f_init = max(freqs[0], host->f_min);host->rescan_disable = 0;host->ios.power_mode = MMC_POWER_UNDEFINED;mmc_claim_host(host); //独占hostif (host->caps2 & MMC_CAP2_NO_PRESCAN_POWERUP)mmc_power_off(host);elsemmc_power_up(host, host->ocr_avail); mmc_release_host(host);mmc_gpiod_request_cd_irq(host); //注册 cd 检测引脚的中断 并设置中断函数 关于SD卡检测处理 后续会有单独说明_mmc_detect_change(host, 0, false); // 待前面的工作都完成后,就进行一次mmc的探测
}
总结
本文只针对mmc host驱动内容进行了简单的分析,在实际的开发过程一般进行控制控制器驱动开发的工作内容比较少,中除非是芯片设计原厂的驱动开发人员;但是作为芯片用户开发者,了解一下控制器驱动的整个流程会有助于我们对整个系统的理解和错误的调试。
Linux MMC子系统分析(二)——Host分析相关推荐
- Linux中断子系统(二)中断控制器GIC驱动分析
Linux中断子系统(二)中断控制器GIC驱动分析 备注: 1. Kernel版本:5.4 2. 使用工具:Source Insight 4.0 3. 参考博客: Linux中断子系统(一 ...
- LINUX MMC 子系统分析之五 MMC driver模块分析
前面我们介绍了MMC 子系统驱动模型.mmc host模块,本篇主要介绍MMC Driver模块,在前面几篇文章中,我们已经说明mmc 子系统已实现mmc driver,即针对所有的mmc card( ...
- linux中断子系统(基于imx6ul arm32分析)
0.说明 本文主要针对linux内核中断整个框架进行梳理,针对的是armv7架构,硬件平台是imx6ul,基于arm GIC控制器来分析. GIC是arm公司设计使用的中断控制器,全称Global I ...
- linux mmc 子系统,linux2.6.28块设备mmc_sd卡mmc子系统核心初始化
参考http://blog.csdn.net/wavemcu/article/details/7366852 // / /// MMC/SD设备驱动代码在Linux源码中的位置/linux-2.6.3 ...
- Linux V4L2子系统-Video设备框架分析(二)
1.概述 在V4L2子系统中,Video设备是一个字符设备,设备节点为/dev/videoX,主设备号为81,次设备号范围为0-63.在用户空间,应用可以通过open/close/ioctl/mmap ...
- linux usb 子系统(二)- host driver
了解usb host driver. 1.USB Subsystem Framework The following chart shows the framework of the USB su ...
- Linux MTD子系统 _从模型分析到Flash驱动模板
MTD(Memory Technology Device)即常说的Flash等使用存储芯片的存储设备,MTD子系统对应的是块设备驱动框架中的设备驱动层,可以说,MTD就是针对Flash设备设计的标准化 ...
- Linux时间子系统之二:表示时间的单位和结构【转】
本文转载自:http://blog.csdn.net/droidphone/article/details/7979295 版权声明:本文为博主原创文章,未经博主允许不得转载. 目录(?)[+] 人们 ...
- Linux V4L2子系统分析(一)
1.概述 Linux系统上的Video设备多种多样,如通过Camera Host控制器接口连接的摄像头,通过USB总线连接的摄像头等.为了兼容更多的硬件,Linux内核抽象了V4L2(Video fo ...
最新文章
- 面试官:请简述一下vue-cli命令行工具,你能自己手写一个吗?
- 。。。。。。想不通的ADO.NET。
- boost::hana::unpack用法的测试程序
- ORACLE的所有字段类型
- 沙漠上不小心挖了个洞,让这个地狱之门般的巨坑,燃烧了50年
- flowable实现流程回退功能
- 存储过程与函数oracle
- (素材源码) 猫猫学IOS(十二)UI之UITableView学习(上)LOL英雄联盟练习
- java读写文件操作
- .net 中的DllImport
- 如何使用 SpringCloud 搭建服务注册中心?
- 蚂蚁金服总监杨冰:金融科技公司为什么要拥抱开源? | 穿山甲专访
- D7000、60D、K5、E5的详细对比评价(转)_我是亲民_新浪博客
- python开发一个PC屏幕监控软件(2000块的道德底线)
- nacos 2.0 Scanner SubTypesScanner was not configured
- 软件测试之因果图分析法
- 荣耀智慧屏x1和小米4a哪个好?
- Overcoming Language Priors in VQA via Decomposed Linguistic Representations阅读笔记
- 2022-2028年中国祭祀用品行业市场竞争状况及发展趋向分析报告
- 迈德威视相机C#调用例程
热门文章
- 使用Auto.js庖丁对Pro Snapshot快照加密的解密打包教程
- Quartus II 13.1安装时出现的问题
- VBS脚本学习:遍历XML文档
- 三进制 四进制计算机原理,基因编码为何选择了“四进制”,而不是计算机系统的“二进制”?...
- Gson:GitHub 标星 18K 的 JSON 解析器,Google 出品的 Java JSON 解析器,强烈推荐!
- 诈金花游戏单机版 附开源地址
- 一年读完100本书(1/100)《微习惯》2021-01-18
- elasticsearch删除过期数据
- Netty快速学习1-基础知识回顾
- Type-c接口及其协议介绍