一、pinctrl 和gpio子系统
pinctrl子系统
借助pinctrl子系统来设置一个PIN的复用(用作什么功能和电气属性)
pinctrl_test: testgrp {
fsl,pins = <
//下面这句话是描述这个pin的设置信息,也就是需要设置成什么样子
MX6UL_PAD_GPIO1_IO00__GPIO1_IO00 0x10B0 /*config 是具体设置值*/
>;
}
MX6UL_PAD_UART1_RTS_B__GPIO1_IO19 0x0090 0x031C 0x0000 0x5 0x0
< mux_reg conf_reg input_reg mux_mode input_val>
0x0090 0x031C 0x0000 0x5 0x0
IOMUXC父节点首地址0x020e0000,因此UART1_RTS_B这个PIN的mux寄存器地址 就是:0x020e0000+0x0090=0x020e 0090。
conf_reg:0x020e0000+0x031C=0x020e 031C,这个寄存器就是UART1_RTS_B的电气属性配置寄存器。
input_reg,便宜为0,表示UART1_RTS_B这个PIN没有input功能。
mux_mode:5表示复用为GPIO1_IO19,将其写入0x020e 0090
input_val:就是写入input_reg寄存器的值。
GPIO子系统
使用gpio子系统来控制gpio,控制gpio的输入输出
在设备树中添加设备节点
gpioled {
#address-cells = <1>;
#size-cells = <1>;
compatible = "atkalpha-gpioled";
pinctrl-names = "default";
// pinctrl-0 属性设置 LED 灯所使用的 PIN 对应的 pinctrl 节点
pinctrl-0 = <&pinctrl_led>;
下面这个与gpio子系统有关
//led-gpio 属性指定了 LED 灯所使用的 GPIO,在这里就是 GPIO1这一组第四个引脚,低电平有效(也急速GPIO1d第三个引脚)
led-gpio = <&gpio1 3 GPIO_ACTIVE_LOW>;
status = "okay";
};
二、设备树
设备树就是将关于硬件配置信息的文件独立出去,驱动程序中只留下关于硬件的操作
修改设备树
1、添加pinctrl节点
2、添加设备节点
三、平台总线框架
主机驱动:一般负责对具体设备进行硬件级别的操作,然后向外提交api函数接口,主机驱动一般都是开发板厂家写好的
设备驱动:所有的设备驱动都可以调用主机驱动的api函数去实现与外界交互功能
当我们向系统注册一个驱动的时候,总线就会在右侧的设备中查找,看看是有没有设备与之匹配的设备,如果有的话就将二者联系起来,同样的,当向系统中注册一个设备的时候,总线就会在左侧的驱动中查找有没有与之匹配的设备
驱动与设备分离
我们知道设备驱动的分离,并且引出了总线、驱动、设备的模型,比如i2c、spi、usb等总线。但是在soc中有些外设没有这个概念,但是我们有想使用这个模型,Linux提出了platform这虚拟总线,于是就对应的platfor_driver和platform_device
当我们向系统注册一个驱动的时候,总线就会在右侧的设备中查找,看看有没有与之匹配
的设备,如果有的话就将两者联系起来。同样的,当向系统中注册一个设备的时候,总线就会在左侧的驱动中查找看有没有与之匹配的设备,有的话也联系起来。Linux 内核中大量的驱动程序都采用总线、驱动和设备模式,我们一会要重点讲解的 platform 驱动就是这一思想下的产物。
1、platform总线
Linux系统内核用bus_type结构体表示总线
bus_type结构体中 platform_match函数负责驱动与设备的匹配
struct bus_type platform_bus_type = {
.name = "platform",
.dev_groups = platform_dev_groups,
.match = platform_match,
.uevent = platform_uevent,
.pm = &platform_dev_pm_ops,
}
2、platform驱动
struct platform_driver {
int (*probe)(struct platform_device *);
int (*remove)(struct platform_device *);
void (*shutdown)(struct platform_device *);
int (*suspend)(struct platform_device *, pm_message_t state);
int (*resume)(struct platform_device *);
struct device_driver driver; //这个结构体变量中包含了设备树匹配的of_match_tableha函数
const struct platform_device_id *id_table; //无设备树匹配
bool prevent_deferred_probe;
}
第二行: probe 函数,当驱动与设备匹配成功以后 probe 函数就会执行,probe函数就是负 责注册驱动设备到内核的哪些东西
第 七 行 driver 成员,该结构体中包含了设备树匹配的of_match_table函数
第八行 :id_table 用在与无设备树匹配
注意:在无设备树的时候platform_driver name 和 platfor_device compliate属性 对应就可以匹配上
struct device_driver
{
const struct of_device_id *of_match_table; //设备树匹配
}
驱动入口函数里面调用的platform_driver_register函数向Linux内核注册一个platform驱动
驱动卸载函数里面调用platform_driver_unregister函数卸载platform驱动
编写platform驱动需要的一些东西
0、寄存器地址定义、因为这里是用地址映射,用虚拟地址进行操作 //传统字符设备驱动
1、设备结构体
2、设备具体操作函数
3、字符设备驱动操作集(file_operations)
4、platform驱动的probe函数,驱动与设备匹配后此函数就会执行(注册字符设备驱动,初始化设备(寄存器地址映射、设备))
5、remove()(卸载字符设备驱动,取消寄存器地址映射)
6、匹配列表(如果使用设备树的话通过此匹配表进行驱动匹配)
7、platform平台驱动结构体(其中包含name(其中name移动要和设备字段相对应),匹配列表,probe和remove)
8、驱动模块的加载/卸载
三、设备树
platform_devices和设备里面具体的节点功能是一样的,所以说如果有设备树,就没必要整个platform_devices。在编写基于设备树的platform驱动我们需要注意以下几点:
1.在设备树中创建设备节点
2.编写platform要注意兼容属性
这里需要注意,我们就是用
设备节点的compatible属性:“atkalpha-gpioled”和platform驱动的of_match_table属性表中compatible属性:”atkalpha-gpioled"来进行匹配的