Linux的移植(2)
SMP多核启动及CPU热插拔驱动
多核启动
linux中对ARM芯片而言,在bootrom代码中,每个CPU都会识别自身的ID,如果ID是0,则引导Bootloader和Linux内核执行,如果不是0,则Bootrom一般在上电时将自身置于WFI或WFE状态,并等待CPU0给其发CPU核间中断或时间来唤醒它。一个典型的多核Linux启动流程如下。
CPU0唤醒其它CPU的动作在内核中被封装为一个smp_operations结构体,对于ARM而言,它定义于arch/arm/include/asm/smp.h中。
1 | struct smp_operations { |
而linux中硬件相关的代码通过实现smp_operations接口来对启动过程的底层实现进行填充,其实现类似下面代码所示。
1 | bool __init vexpress_smp_init_ops(void) |
如上代码的SMP唤醒流程如下所示:
CPU热插拔
CPU热插拔的实现也是与芯片密切相关的。对VEXPRESS而言,其实现了smp_operations的cpu_die成员来实现的CPU热插拔。xxx_cpu_die()会在进行CPUn的拔除操作时将CPUn投入低功耗的WFI状态,其代码如下所示。
1 | static inline void versatile_immitation_enter_lowpower(unsigned int actrl_mask) |
其睡眠与wfi(),当CPUn再次在线的时候,又会因为CPU0给它发出的IPI而从wfi()函数返回继续执行,醒来后就做“versatile_cpu_release == cpu_logical_map(cpu)”的判断,以确定该次醒来是否是由CPU0进行的一次正常唤醒。
GPIO与pinctrl
在drivers/gpio下实现了通用的基于gpiolib的GPIO驱动,其中定义了一个通用的描述底层GPIO控制器的gpio_chip结构,并要求具体的SoC实现gpio_chip结构体的成员函数,最后通过gpiochip_add()注册gpio_chip。但在GPIO兼有多种功能且需要复杂配置的情况下,GPIO驱动部分往往一到drivers/pinctrl下与pinmux一起实现。
在许多SoC内部都包含pin控制器,通过pin控制器的寄存器,我们可以配置一个或多个引脚的功能和特性。而pinctrl驱动则是内核定义的用于pin控制器操作的软件接口。其功能实现主要包含:
- 枚举且命名pin控制器可控制的所有引脚
- 提供引脚复用的能力
- 提供配置引脚的能力,如驱动能力、上拉下拉、开漏等
pinctrl和引脚
在pinctrl驱动中,我们需要定义引脚。假设一个PGA封装的芯片引脚布局如下所示。
则在pinctrl驱动初始化的时候,需要像pinctrl子系统注册一个pinctrl_desc描述符,该描述符的pins成员中包含所有引脚的列表,这在pinctrl初始化时填充。
引脚组(Pin Group)
在pinctrl子系统中,支持将一组引脚绑定为同一功能。如{0,8,16,24}这组引脚为SPI功能,{24,25}这组引脚为I2C功能。在驱动中,要实现这种分组只需实现pinctrl_ops相关的接口,并将其填充到pinctr_desc中去。示例可参考drivers/pinctrl/pinctrl-pic32.c
1 | static const struct pinctrl_ops pic32_pinctrl_ops = { |
引脚配置
设备驱动有时需要配置引脚,比如可能把引脚设置为高阻或者三态,或通过某阻值将引脚上拉/下拉以确保默认状态下引脚的电平状态。如下某设备驱动中将某引脚上拉:
1 | #include <linux/pinctrl/consumer.h> |
PLATFORM_X_PULL_UP由特定pinctrl驱动定义,在驱动中需要完成这些配置所需要的回调函数,即pinctrl_desc的confops成员函数。
pinctrl子系统与GPIO子系统交互
pinctrl驱动所覆盖的引脚可同时作为GPIO用,内核的GPIO子系统和pinctrl子系统是并行工作的,但有时需要交叉映射,在这种时候,pinctrl驱动需要告知pinctrl子系统核心层GPIO与底层pinctrl驱动所管理的引脚之间的映射关系。
比如pinctrl驱动定义的32~47号引脚与gpio_chip实例chip_a的GPIO对应,64~71号引脚与gpio_chip实例chip_b的GPIO对应,则其映射关系为:
1 | chip a: |
对于这种映射关系在内核中可以使用pinctrl_gpio_range结构体来进行描述,而在pinctrl驱动中,则通过pinctrl_add_gpio_range()接口向pinctrl子系统核心层注册这种映射关系。
而在基于gpiolib的GPIO驱动中,GPIO的申请与释放实质上也是通过pinctrl子系统来完成的,因为其中管理着两种不同驱动间GPIO资源在软件上的映射关系。
引脚复用
一个特定的功能总是要求由一组引脚来完成,一组引脚的数量可以是一个或多个。正如前面对引脚组的描述。
如果I2C功能由{A5, B5}引脚组成,而在定义引脚描述的pinctrl_pin_desc结构体实例的时候,将它们的序号定义为{24,25};而SPI功能由{A8,A7,A6,A5}和{G4,G3,G2,G1},即{0,8,16,24}和{38,46,54,62}两组引脚完成。
据此,功能和引脚组的组合就可以决定一组引脚在系统中的作用,因此在设置某组引脚的作用时,pinctrl核心层会将功能的序号与引脚组的序号传递给底层pinctrl驱动中的相关回调函数。
在特定pinctrl驱动中pinmux相关代码主要处理如何使能/禁止某一{功能,引脚组}的组合,例如,当spi0设备申请pinctrl0的fspi0功能和gspi0引脚组以便将gspi0引脚组配置为SPI接口时,相关的回调函数被组织进一个pinmux_ops结构体中,而该结构体的实例最终成为pinctrl_desc的pmxops成员。
具体的pinctrl、使用引脚的设备、功能、引脚组的映射关系,可以在板文件中通过定义pinctrl_map结构体实例来实现。当然这种映射关系最好是在设备树中通过节点的属性进行,具体属性依赖于具体pinctrl驱动的实现,在pinctrl驱动中通过pinctrl_ops结构体的dt_node_to_map()成员函数读出属性建立映射表。
转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 yxhlfx@163.com
文章标题:Linux的移植(2)
本文作者:红尘追风
发布时间:2016-12-16, 20:41:10
原始链接:http://www.micernel.com/2016/12/16/Linux%E7%9A%84%E7%A7%BB%E6%A4%8D(2)/版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。