基于宋宝华《Linux设备驱动开发详解-基于最新的Linux4.0内核》。
电源管理的唯一目标是 省电 。
CMOS电路中的功耗与电压的平方成正比,与频率成正比:P∝fV^2
图中,有两处频率变换的地方:
CPUFreq的核心层位于drivers/cpufrq/cpufreq.c:
每个SOC的具体CPUFreq驱动实例只需要实现电压、频率表,以及从硬件层面完成这些变化。
SOC CPUFreq驱动只是设定了CPU的频率参数,以及提供了设置频率的途径。
一个SOC的CPUFreq驱动实例(drivers/cpufreq/s3c64xx-cpufreq.c):
...
static struct s3c64xx_dvfs s3c64xx_dvfs_table[] = {
[0] = { 1000000, 1150000 },
[1] = { 1050000, 1150000 },
[2] = { 1100000, 1150000 },
[3] = { 1200000, 1350000 },
[4] = { 1300000, 1350000 },
};
static struct cpufreq_frequency_table s3c64xx_freq_table[] = {
{ 0, 0, 66000 },
{ 0, 0, 100000 },
{ 0, 0, 133000 },
{ 0, 1, 200000 },
{ 0, 1, 222000 },
{ 0, 1, 266000 },
{ 0, 2, 333000 },
{ 0, 2, 400000 },
{ 0, 2, 532000 },
{ 0, 2, 533000 },
{ 0, 3, 667000 },
{ 0, 4, 800000 },
{ 0, 0, CPUFREQ_TABLE_END },
};
static int s3c64xx_cpufreq_set_target(struct cpufreq_policy *policy,
unsigned int index)
{
...
ret = regulator_set_voltage(vddarm,
dvfs->vddarm_min,
dvfs->vddarm_max);
...
ret = clk_set_rate(policy->clk, new_freq * 1000);
...
}
#ifdef CONFIG_REGULATOR
static void s3c64xx_cpufreq_config_regulator(void)
{
...
cpufreq_for_each_valid_entry(freq, s3c64xx_freq_table) {
dvfs = &s3c64xx_dvfs_table[freq->driver_data];
found = 0;
for (i = 0; i < count; i++) {
v = regulator_list_voltage(vddarm, i);
if (v >= dvfs->vddarm_min && v <= dvfs->vddarm_max)
found = 1;
}
...
}
#endif
static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy)
{
...
policy->clk = clk_get(NULL, "armclk");
...
s3c64xx_cpufreq_config_regulator();
...
regulator_put(vddarm);
clk_put(policy->clk);
...
}
static struct cpufreq_driver s3c64xx_cpufreq_driver = {
.flags = CPUFREQ_NEED_INITIAL_FREQ_CHECK,
.verify = cpufreq_generic_frequency_table_verify,
.target_index = s3c64xx_cpufreq_set_target,
.get = cpufreq_generic_get,
.init = s3c64xx_cpufreq_driver_init,
.name = "s3c",
};
static int __init s3c64xx_cpufreq_init(void)
{
return cpufreq_register_driver(&s3c64xx_cpufreq_driver);
}
module_init(s3c64xx_cpufreq_init);
究竟频率依据哪种标准,进行何种变化,完全有CPUFreq的策略(policy)决定:
用户空间可通过/sys/devices/system/cpu/cpux/cpufreq节点来设置CPUFreq(采用userspace策略),则运行如下命令:
\# echo userspace > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
\# echo 700000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed
CPUFreq子系统主要会发出两种通知(notifier):
CPUFREQ_NOTIFY:所有注册的notifier都会被告知新的策略已经被设置
CPUFREQ_POSTCHANGE:已经完成变更
除了CPU以外,一些非CPU设备也支持多个操作频率和电压,存在多个OPP。Linux 3.2之后的内核也支持针对这种非CPU设备的DVFS,该套子系统为DEVFreq,位于drivers/devfreq目录。
某个domain所支持的<频率,电压>对的集合被称为Operating Performance Point,缩写为OPP。
一个OPP实例:
static struct omap_opp_def __initdata omap44xx_opp_def_list[]={
/* MPU OPP1 - OPP50 */
OPP_INITIALIZER("mpu", true, 300000000, OMAP4430_VDD_MPU_OPP50_UV),
...
}
...
int __init omap4_opp_init(void)
{
...
r = omap_init_opp_table(omap44xx_opp_def_list,
ARRAY_SIZE(omap44xx_opp_def_list));
...
}
device_initcall(omap4_opp_init);
int __init omap_init_opp_table(struct omap_opp_def *opp_def, u32 opp_def_size)
{
...
for(i = 0; i< opp_def_size;i++,opp_def++){
...
r = opp_add(dev, opp_def->freq, opp_def->u_volt);
...
}
return 0;
}
下面两个API分别用于获取与某OPP对应的电压和频率:
unsigned long opp_get_voltage(struct opp *opp);
unsigned long opp_get_freq(struct opp *opp);
当某个CPUFreq驱动想将CPU设置为某一频率的时候,它可能会同时设置电压,其代码流程为:
soc_switch_to_freq_voltage(freq)
{
/* do thing */
rcu_read_lock();
opp = opp_find_freq_ceil(dev, &freq);
v = opp_get_voltage(opp);
rcu_read_unlock();
if(v)
regulator_set_voltage(..,v);
/* do other things */
}
big.LITTLE架构的设计旨在为适当的作业分配恰当的处理器。
:
基于宋宝华《Linux设备驱动开发详解-基于最新的Linux4.0内核》。
Linux设备驱动会以内核模块的形式出现,因此,学会编写Linux内核模块编程是学习Linux设备驱动的先决条件。
为了避免编译后的内核尺寸过大,有些功能不会被编译进内核。
当需要使用这些功能的时候,相应的代码被动态的加载到内核。这种机制被称为模块(Module):
一个Linux模块主要由以下几个部分组成:
/*
* 模块说明
*
*/
static int __init xxx_init(void)
{
}
module_init(xxx_init);
static int __exit xxx_exit(void)
{
}
module_exit(xxx_exit);
MODULE_AUTHOR("XXX");
MODULE_LICENSE("XXX");
...
编译后,生成xxx.ko目标文件。
命令 | 描述 |
---|---|
insmod ./xxx.ko | 加载模块 |
modprob ./xxx.ko | 加载模块及该模块依赖的其他模块 |
modprob -r ./xxx.ko | 卸载模块及该模块依赖的其他模块 |
lsmod | 获得系统中已经加载的所有模块及模块间的依赖关系(读取/proc/modules文件);已经加载的模块信息位于/sys/module目录 |
modinfo <模块名>模块名> | 获得模块的信息 |
基于宋宝华《Linux设备驱动开发详解-基于最新的Linux4.0内核》。
在编译内核时,需要配置内核,可以使用下面命令中的一个:
例:
$ make ARCH=arm menuconfig
$ make ARCH=arm xxx_defconfig /* arch/arm/configs/xxx_defconfig文件包含了许多电路板的默认配置 */
编译内核和模块的方法是:
$ make ARCH=arm zImage
$ make ARCH=arm modules
执行上述命令的结果:
在源代码的根目录下,会得到未压缩的内核映像vmlinux,内核符合表文件System.map;
在arch/arm/boot/目录下,会得到压缩的内核映像zImage;
在内核各对应目录,会得到选中的内核模块;
Linux内核的配置系统由以下3部分组成:
使用make config、make menuconfig等命令后,会生成一个.config配置文件,记录哪些部分被编译入内核,哪些部分被编译为内核模块。
运行make menuconfig等时,配置工具首先分析与体系结构对应的/arch/xxx/Kconfig文件(xxx即为传入的ARCH参数),/arch/xxx/Kconfig文件中除本身包含以下与体系结构相关的配置项与配置菜单以外,还通过source语句引入了一系列Kconfig文件,而这些Kconfig又可能再次通过source引入下一层的Kconfig,配置工具依据Kconfig包含的菜单和条目就可以描绘出一个菜单分层结构。
在Linux内核中增加程序需要完成一项3项工作:
Kconfig文件中的配置项:
config XXXX
tristate “xxxxxxxxx”
dependson YYYY
default n
---help---
nnnnnnnn
基于宋宝华《Linux设备驱动开发详解-基于最新的Linux4.0内核》。
驱动工程师必须按照相应的架构设计驱动。
操作系统 | 驱动特点 |
---|---|
无 | 每一种设备驱动都会定义一个软件模块,包含.h文件和.c文件. .h文件 - 定义该设备驱动的数据结构并声明外部函数。 .c文件 - 进行驱动的具体实现。 |
有 | 需要将驱动融入内核。 设计面向操作系统内核的接口,该接口有操作系统规定。 |
操作系统的作用: 多任务并发;提供内存管理机制(让每个进程都可以独立的访问4GB的内存空间)。
操作系统通过给驱动制造麻烦来达到给上层应用提供便利的目的。
Linux驱动针对的对象是存储器和外设,分为3个基础大类:
Linux设备驱动需要的基础:硬件基础、c语言基础、Linux内核基础、多任务并发控制和同步基础。
有效的帮助文档:Linux内核源代码中包含的Documention目录。
Linux源代码阅读交叉索引:
NOR Flash的特点是 可芯片内执行(eXecute In Place, XIP),程序可以直接在NOR内运行。
NAND Flash以块方式进行访问,不支持芯片内执行。
DRAM以电荷形式进行存储,数据存储在电容器中,由于电容器会因漏电而出现电荷丢失,所以DRAM器件需要定期刷新。
与SDRAM相比,DDR SDRAM同时利用了时钟脉冲的上升沿和下降沿传输数据,因此,在时钟频率不变的情况下,数据传输频率加倍。
USB传输速度:
USB 2.0的bulk模式只支持1个数据流。
USB 3.0增加的Bulk Streams模式,可以支持多个数据流,每个数据流被分配一个Stream ID(SID),每个SID与一个主机缓冲区对应。
以太网MAC和PHY之间采用MII(媒体独立接口)连接。
以太网隔离变压器是处于以太网PHY与连接器之间的磁性组件,作用:信号传输,阻抗匹配,波形修复,信号杂波抑制,高电压隔离。
SD/SDIO的传输模式:
CPLD由完全可编程的与或门阵列以及宏单元构成。“与或”阵列完成组合逻辑功能,触发器完成时序逻辑功能。
FPGA基于LUT(查找表)工艺。查找表本质上是一片RAM。
芯片手册阅读:
Linux内核是一个演变而不是一个设计。一直朝着3个方向发展:
Linux 2.6以后,总线、设备、驱动三者之间因为一定的联系性而实现对设备的控制。
Linux 2.6以后,内核模块从.o变为.ko。
Linux内核源代码:
内核一般要做到drivers与arch的软件架构分离,驱动中不包含板级信息,让驱动跨平台。同时,内核的通用部分(如kernel,fs,ipc,net等)则与具体的硬件(arch和drivers)剥离。
Linux内核的组成:
绝大多数进程(以及进程中的多个进程)是由用户空间的应用创建的。
内核空间和用户空间的具体界限是可以调整的,在内核配置选项Kernel Features—>Memory split,可以设置界限为2GB或者3GB。
Linux内核的内存管理底层的Buddy算法,用于管理每个页的占用情况,内核空间的slab以及用户空间的C库的二次管理。
Kswapd(交换进程)是Linux中用于页面回收的内核线程,采用最近最少使用(LRU)算法进行内存回收。
在Linux中网络接口可分为网络协议和网络驱动程序。
ARM处理器的7种工作模式:
在AutoCAD中进行设计丝印,然后在AI中打开。
环境:
dwg (viewres,2000) -> 输出wmf -> ai
文件,输出,图源文件,选中地址,确定,鼠标会变成框的形式,选中你要转的图,回车或是空格。
使用Viewres命令重生成一下文件,让线条圆滑。
AutoCAD默认的精度是1000,范围值是1-20000。
精度太小线条不够圆滑,精度太高,生成的曲线导入CDR或AI后会不连续,一个封闭的曲线会被分割成几段,重新使它们成为可用的封闭的曲线会花很多时间。
经过试验,精度设置成2000,生成的曲线不仅封闭的,而且也足够圆滑。
在Adobe Illustrator中就可以打开ai文件了。
注意:如果是字体,需要检查,可能需要精细调节和编辑。