电脑基础 · 2023年3月21日

总结篇--知识准备

一、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"来进行匹配的