0
点赞
收藏
分享

微信扫一扫

【SemiDrive源码分析】【X9芯片驱动调试】13 - GPIO 配置方法



【SemiDrive源码分析】【X9芯片驱动调试】13 - GPIO 配置方法

  • ​​一、Android Linux 下 GPIO 配置方法(AP1 Domain - 6 * A55)​​
  • ​​1.1 Kernel DTS 配置 GPIO 方法​​
  • ​​1.1.1 初始化 GPIO 控制器​​
  • ​​1.1.1.1 打开 AP1 GPIO4控制器​​
  • ​​1.1.1.2 配置 gpio124 按键示例​​
  • ​​1.1.1.3 配置 gpio4 驱动 led 实例​​
  • ​​1.1.1.4 GPIO 相关操作函数​​
  • ​​1.2 Kernel 使用 GPIO 实例​​
  • ​​1.2.1 普通 gpio 操作实例​​
  • ​​1.2.2 中断 gpio 操作实例​​
  • ​​1.2.3 SD Card 中断 GPIO 分析​​
  • ​​1.3 devmem 调试 GPIO 方法​​
  • ​​1.3.1 基于 sysfs 接口访问 gpio​​
  • ​​1.3.2 基于 debugfs 操作 gpio节点​​
  • ​​1.3.3 devmem 调试说明​​
  • ​​二、SafetyOS 下 GPIO 配置方法(Safety Domain - Dual Core R5)​​
  • ​​2.1 SDToolBox GPIO 配置与烧录方法​​
  • ​​2.2 SafetyOS 下配置GPIO​​
  • ​​2.2.1 配置 PINMUX 为 GPIO​​
  • ​​2.2.2 配置 GPIO 输出​​
  • ​​2.2.3 配置 GPIO 输入​​
  • ​​2.3 SafetyOS 下配置 GPIO 中断实例​​
  • ​​2.4 SafetyOS 下配置 GPIO 调试方法​​
  • ​​2.4.1 get_pin_info​​
  • ​​2.4.2 操作寄存器​​
  • ​​三、QNX 下 GPIO 配置方法(AP2 Domain - 1 * A55)​​


本 SemiDrive源码分析 之 Yocto源码分析 系列文章汇总如下:

  1. 《​​【SemiDrive源码分析】【Yocto源码分析】01 - yocto/base目录源码分析(编译环境初始化流程)​​》
  2. 《​​【SemiDrive源码分析】【Yocto源码分析】02 - yocto/meta-openembedded目录源码分析​​》
  3. 《​​【SemiDrive源码分析】【Yocto源码分析】03 - yocto/meta-semidrive目录及Yocto Kernel编译过程分析(上)​​》
  4. 《​​【SemiDrive源码分析】【Yocto源码分析】04 - yocto/meta-semidrive目录及Yocto Kernel编译过程分析(下)​​》
  5. 《​​【SemiDrive源码分析】【Yocto源码分析】05 - 找一找Yocto Kernel编译过程中所有Task的源码在哪定义的呢?​​》
  6. 《​​【SemiDrive源码分析】【Yocto源码分析】06 - Kernel编译生成的Image.bin、Image_nobt.dtb、modules.tgz 这三个文件分别是如何生成的?​​》
  7. 《​​【SemiDrive源码分析】【Yocto源码分析】07 - core-image-base-x9h_ref_serdes.rootfs.ext4 文件系统是如何生成的​​》
  8. 《​​【SemiDrive源码分析】【X9芯片启动流程】08 - X9平台 lk 目录源码分析 之 目录介绍​​》
  9. 《​​【SemiDrive源码分析】【X9芯片启动流程】09 - X9平台系统启动流程分析​​》
  10. 《​​【SemiDrive源码分析】【X9芯片启动流程】10 - BareMetal_Suite目录R5 DIL.bin 引导程序源代码分析​​》
  11. 《​​【SemiDrive源码分析】【X9芯片启动流程】11 - freertos_safetyos目录Cortex-R5 DIL2.bin 引导程序源代码分析​​》
  12. 《​​【SemiDrive源码分析】【X9芯片启动流程】12 - freertos_safetyos目录Cortex-R5 DIL2.bin 之 sdm_display_init 显示初始化源码分析​​》
  13. 《​​【SemiDrive源码分析】【X9芯片驱动调试】13 - GPIO 配置方法​​》



在​​X9HP​​中提供了不同的​​domain​​,简单理解就是不同的相互隔离的​​CPU​​核,不同的​​Domain​​之间的​​GPIO​​控制器是不同的,

在​​X9HP​​上有三个​​Domain​​:

  1. ​AP1 (6*A55)​​​:使用的是​​GPIO4​​ 控制器
  2. ​AP2 (1*A55)​​​:使用的是​​GPIO5​​ 控制器
  3. ​Safety (双核R5)​​​:使用的是​​GPIO1​​ 控制器

除了​​GPIO​​控制器在不同的​​domain​​ 之间不同外,​​pin​​也是有各自不同的​​domain​​的,

但是当它们作为​​gpio​​使用时,可以通过设置挂靠到对应域下的​​GPIO​​控制器上来在对应的​​domain​​下使用。

具体 ​​pin​​ 的​​gpio​​控制器的设置可以通过​​PC​​工具的​​SDConfigTool​​来进行。


在​​linux​​ 下将一个​​pin​​当做​​gpio​​使用时,首先要确定它的​​domain​​是否是​​AP​​, 如果不是则需要进行修改;

大部分​​pin​​都是​​mux​​ 类型即可以当做不同的功能,而一小部分 ​​pin​​ 是专用功能,所以 ​​pin​​ 本身涉及到 ​​pin​​ 功能、驱动强度、向上拉、方向、以及电平等设置;所有这些相关的内容都是通过PC工具进行配置。

配置完成后会生成对应的配置 ​​bin​​ 文件,将此文件烧录到对应的分区 ​​system_config​​,在系统启动过程中会用到。

而如果 ​​pin​​ 本身是设置 ok的,则不需要更改此部分内容。

在 ​​linux​​​内核下​​gpio​​​ 的配置使用,比如​​gpio​​​驱动的​​led​​​,你需要根据你的原理图在第一步中查找具体的​​gpio​​​号并填入到​​dts​​​,而后驱动进行解析并使用。不建议直接在驱动中通过​​gpio​​​ 号来操作而不经过​​dts​​ 设置解析的过程,因为一旦出问题,查找起来比较麻烦。



经过 ​​SDConfigTool​​ 配置后会生成对应的配置文件 ​​system_config.bin​​,这个部分需要烧录到 ​​system_config​​分区,​​safety domain​​ 在系统启动过程中分读取此分区的配置信息并将其写入寄存器生效。

当​​Linux​​内核运行起来后之前的配置信息已经生效,所以内核初始化过程中主要就是解析​​dts​​ 中设置的 ​​gpio​​ 信息并进行初始化设置,之后就根据各个进程需求在驱动中得到调用。



一、Android Linux 下 GPIO 配置方法(AP1 Domain - 6 * A55)

1.1 Kernel DTS 配置 GPIO 方法

1.1.1 初始化 GPIO 控制器

不同的​​domain​​使用不同的​​gpio​​控制器,这些控制器的​​dts​​配置定义,

因为是​​AP1​​的代码,所以我只留下​​GPIO4​​的配置,其他的​​GPIO3​​和​​GPIO5​​省略不动,如下:

# android\kernel\arch\arm64\boot\dts\semidrive\x9.dtsi
gpio3: gpio@30410000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "semidrive,sdrv-gpio";
reg = <0x0 0x30410000 0x0 0x10000>;
status = "disabled";
// ......省略......
};

gpio4: gpio@30420000 { // Android AP1 对应的GPIO控制器
#address-cells = <1>;
#size-cells = <0>;
compatible = "semidrive,sdrv-gpio";
reg = <0x0 0x30420000 0x0 0x10000>; // gpio4 寄存器地址
status = "disabled";

port4a: gpio-controller@0 {
compatible = "semidrive,sdrv-gpio-port";
gpio-controller;// 此项为GPIO控制器,内核驱动初始化过程中会生成对应的struct gpio_chip结构体
gpio-ranges = <&pinctrl 0 0 32>; //控制的GPIO范围,从gpio0 到 gpio31,共32个gpio
#gpio-cells = <2>;
nr-gpios = <32>; // 当前组包含的GPIO数量 32
reg = <0>; // 当前组的编号为 port0
interrupt-controller; // 中断控制器,初始化时会生成struct irq_chip
#interrupt-cells = <2>; // dts 使用 int 时需要配置的单元数
interrupts = <GIC_SPI 236 IRQ_TYPE_LEVEL_HIGH>; //中断类:型软件中断,中断号:236,触发类型:高电平中断
};

port4b: gpio-controller@1 {
compatible = "semidrive,sdrv-gpio-port";
gpio-controller;
gpio-ranges = <&pinctrl 0 32 32>; //控制的GPIO范围,从gpio32 到 gpio63,共32个gpio
#gpio-cells = <2>;
nr-gpios = <32>; // 当前组包含的GPIO数量 32
reg = <1>; // 当前组的编号为 port1
interrupt-controller; // 中断控制器,初始化时会生成struct irq_chip
#interrupt-cells = <2>;
interrupts = <GIC_SPI 237 IRQ_TYPE_LEVEL_HIGH>;//中断类:型软件中断,中断号:237,触发类型:高电
};

port4c: gpio-controller@2 {
compatible = "semidrive,sdrv-gpio-port";
gpio-controller;
gpio-ranges = <&pinctrl 0 64 32>; //控制的GPIO范围,从gpio64 到 gpio95,共32个gpio
#gpio-cells = <2>;
nr-gpios = <32>; // 当前组包含的GPIO数量 32
reg = <2>; // 当前组的编号为 port2
interrupt-controller; // 中断控制器,初始化时会生成struct irq_chip
#interrupt-cells = <2>;
interrupts = <GIC_SPI 238 IRQ_TYPE_LEVEL_HIGH>;//中断类:型软件中断,中断号:238,触发类型:高电
};
port4d: gpio-controller@3 {
compatible = "semidrive,sdrv-gpio-port";
gpio-controller;
gpio-ranges = <&pinctrl 0 96 32>; //控制的GPIO范围,从gpio96 到 gpio127,共32个gpio
#gpio-cells = <2>;
nr-gpios = <32>; // 当前组包含的GPIO数量 32
reg = <3>; // 当前组的编号为 port3
interrupt-controller; // 中断控制器,初始化时会生成struct irq_chip
#interrupt-cells = <2>;
interrupts = <GIC_SPI 239 IRQ_TYPE_LEVEL_HIGH>;//中断类:型软件中断,中断号:239,触发类型:高电
};

port4e: gpio-controller@4 {
compatible = "semidrive,sdrv-gpio-port";
gpio-controller;
gpio-ranges = <&pinctrl 0 128 32>; //控制的GPIO范围,从gpio128 到 gpio159,共32个gpio
#gpio-cells = <2>;
nr-gpios = <32>; // 当前组包含的GPIO数量 32
reg = <4>; // 当前组的编号为 port4
interrupt-controller; // 中断控制器,初始化时会生成struct irq_chip
#interrupt-cells = <2>;
interrupts = <GIC_SPI 240 IRQ_TYPE_LEVEL_HIGH>;//中断类:型软件中断,中断号:240,触发类型:高电
};
};

gpio5: gpio@30430000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "semidrive,sdrv-gpio";
reg = <0x0 0x30430000 0x0 0x10000>;
status = "disabled";
// ......省略......
};



1.1.1.1 打开 AP1 GPIO4控制器

在​​dts​​​中也搜到只有​​dts4​​在使用:

# android\kernel\arch\arm64\boot\dts\semidrive\x9_high-plus_ref_native_ivi_serdes_8g.dts
&gpio4 {
//pinctrl-names = "default";
//pinctrl-0 = <&pinctrl_gpiotouch_lvds &pinctrl_gpiotouch_mipi>;
status = "okay";
};



1.1.1.2 配置 gpio124 按键示例

gpio-keys {
status = "okay";
compatible = "gpio-keys";
up {
label = "GPIO Key Power";
linux,code = <KEY_POWER>;
gpios = <&port4d 28 GPIO_ACTIVE_LOW>; //此gpio为4d组内偏移编号为28(124 - 96) 的gpio 即 gpio124,低有效
};
};

以 ​​gpio124​​ 配置为按键为例,​​gpio124​​ 是属于 ​​port4d​​ 中的 ​​gpio​​,它的起始编号为 ​​96​​,偏移为 ​​28​​。

包含三个部分,第一部分是组名即​​4d​​组,中间的则是组内偏移编号,最后一个是有效性设置.


1.1.1.3 配置 gpio4 驱动 led 实例

leds {
status = "okay";
compatible = "gpio-leds";
led0: heartbeat {
label = "heartbeat";
default-state = "on";
linux,default-trigger = "heartbeat";
gpios = <&port4a 4 GPIO_ACTIVE_LOW>; //此gpio为4a组内偏移编号为4 (0+4) 的gpio 即 gpio4,低有效
};
};

所谓的有效电平即设备处于工作 ​​active​​ 状态的电平级别,比如按键按下的电平,​​led​​ 灯亮的电平。

1: 输出的情况: 以 ​​gpiod_set_value​​ 为例

​GPIO_ACTIVE_HIGH​​: 代码逻辑写 ​​1​​,输出高电平;代码逻辑写 ​​0​​,输出低电平。

​GPIO_ACTIVE_LOW​​: 代码逻辑写 ​​0​​,输出高电平;代码逻辑写 ​​1​​,输出低电平。

2: 输入的情况: 以 ​​gpiod_get_value​​ 为例

​GPIO_ACTIVE_HIGH​​: 外部输入为高电平,返回 ​​1​​,否则返回 ​​0​​。

​GPIO_ACTIVE_LOW​​: 外部输入为低电平,返回 ​​1​​,否则返回 ​​0​​。


1.1.1.4 GPIO 相关操作函数

  1. ​struct gpio_chip​

    ​gpio​​ 控制器抽象结构体,比如​​GPIO4​​ 控制器里面有五组,那么则有五个控制器。

    划分的依据简单的理解为每一组都可以控制 ​​0-31​​ 范围的 ​​gpio​​,而且识别访问可以通过简单的”偏移” 来进行

  2. ​struct gpio_desc​

    ​gpio​​ 描述符,即描述具体某个​​ gpio​​ 的信息,包括 ​​name​​、​​label ​​以及相关的​​flag​​ 等

  3. ​struct gpio_device​

    ​gpio chip​​ 的内部状态存储结构,主要保存大部分的运行时数据,比如其中包含了 ​​struct gpio_desc​​ 数组,即存储此 ​​chip​​ 中的各个 ​​gpio​​ 的描述符,在初始化时构建。


目前​​ linux​​​ 内核提供了两套 ​​gpio​​ 操作接口,

  • ​legacy​​​ 接口通过​​gpio number​​​ 去操作​​gpio​​​ 函数以​​gpio_*​​ 作为前缀,
  • ​new​​​ 接口通过​​struct gpio_desc​​​ 去操作​​gpio​​​, 函数以​​gpiod_*​​​ 作为前缀,目前内核推荐使用​​new​​​ 接口去操作​​gpio​


  1. 通过解析​​DTS​​​ 申请​​gpio​
// 从设备的子节点获取一个 gpio 描述符
static inline struct gpio_desc *devm_fwnode_get_gpiod_from_child
(struct device *dev, const char *con_id, struct fwnode_handle *child,
enum gpiod_flags flags, const char *label)


// 根据功能字符串 con_id 来获取 gpio,
// 即 dts 中设置的 xxx-gpio=<...> 或者 xxx-gpios=<...>的属性
// 则可以通过将 con_id 设置为”xxx” 来获取对应的 struct gpio_desc,如果此处设置为 NULL,则直接使用 gpio 或者 gpios
struct gpio_desc *__must_check devm_gpiod_get(struct device *dev, const char *con_id, enum gpiod_flags flags)

// flags: 用于指定 gpio 默认的方向及电平,可被设置为如下值:
- GPIOD_ASIS // 不对 gpio 设置默认值
- GPIOD_IN // gpio 默认设置为输入
- GPIOD_OUT_LOW // gpio 默认设置为输出且输出低电平
- GPIOD_OUT_HIGH // gpio 默认设置为输出且输出高电平
  1. 设置​​gpio​​ 方向
int gpiod_direction_input(struct gpio_desc *desc);
int gpiod_direction_output(struct gpio_desc *desc, int value);
  1. 设置输出电平
void gpiod_set_value(struct gpio_desc *desc, , int value);
  1. 读取输入电平
int gpiod_get_value(const struct gpio_desc *desc);
  1. 释放​​gpio​​​ 如果申请 ​​gpio​​ 的接口包含 ​​devm​​ 前缀,在驱动卸载 或 加载失败时,​​gpio​​ 将自动被释放,
    对于这类场景,不需要显式调用该函数释放 ​​gpio​​ 资源. 如果正常运行过程中需主动释放该​​gpio ​
void devm_gpiod_put(struct device *dev, struct gpio_desc *desc)
  1. 获取​​GPIO​​ 对应的中断号
int gpiod_to_irq(const struct gpio_desc *desc);

更多接 口见: ​​kernel/include/linux/gpio/consumer.h​




1.2 Kernel 使用 GPIO 实例

1.2.1 普通 gpio 操作实例

  1. 配置DTS
node {
...
reset-gpios = <&gpioctlr 43 GPIO_ACTIVE_HIGH>;
}
  1. 代码操作
#include <linux/gpio/consumer.h>

struct gpio_desc *reset;
int status;

reset = devm_gpiod_get(&pdev->dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(reset)) {
err = PTR_ERR(reset);
return err;
}

gpiod_set_value(reset, 1); // DTS 中 flag 被设置为 GPIO_ACTIVE_HIGH,输出高电平
gpiod_set_value(reset, 0); // DTS 中 flag 被设置为 GPIO_ACTIVE_HIGH,输出低电平

gpiod_direction_input(reset) // 将 GPIO 设置为输入

// DTS 中 flag 被设置为 GPIO_ACTIVE_HIGH,如果读到的是高电平,返回 1,如果读到的是低电平,返回 0
status = gpiod_get_value(reset)



1.2.2 中断 gpio 操作实例

触摸屏 中断​​GPIO ​​以​​ds941_ds948_gt9xx​​ 为例,​​DTS​​配置如下:

有关 《​​tca9539芯片手册​​》可以上​​TI​​官网下载

&i2c11 {
status = "okay";
tp_gpio: gpio@76 {
compatible = "ti,tca9539";
reg = <0x76>;
#gpio-cells = <2>;
gpio-controller;
};
};
&i2c15 {
status = "disabled";
sync_enable = <1>;
serdes_gt9xx_16_j35:touch@5d {
compatible = "sdrv,ds941_ds948_gt9xx";
irq-gpios = <&port4e 16 0>; /*io96*/ // 配置 GPIO(128 + 16) = gpio144 中断引脚
reset-gpios = <&tp_gpio 1 0>; /*index 1*/ // 使用tca9539扩展的GPIO作为中断引脚
reg = <0x5d>;
dev_type = <0>; /* 0: main device, 1: aux device */
addr_ds941 = <0x0c>;
addr_ds948 = <0x2c>;
irq_channel = <3>;
reset_channel = <2>;
};
serdes_gt9xx_16_j35a:touch@14 {
compatible = "sdrv,ds941_ds948_gt9xx";
irq-gpios = <&port4e 17 0>; /*io97*/ //配置 GPIO(128 + 17) = gpio145 中断引脚
reset-gpios = <&tp_gpio 2 0>; /*index 2*/
reg = <0x14>;
dev_type = <1>; /* 0: main device, 1: aux device */
addr_ds941 = <0x0d>;
addr_ds948 = <0x3c>;
irq_channel = <3>;
reset_channel = <2>;
};
};

代码操作如下:

可以看出,在​​ds941_ds948_goodix.c​​ 中只是对dts中的内容进行了解析,而申请中断这些事则是由​​devm_request_threaded_irq​​进行统一封装了。

# kernel\drivers\input\touchscreen\ds941_ds948_goodix.c

#define GOODIX_GPIO_INT_NAME "irq"
#define GOODIX_GPIO_RST_NAME "reset"

static int goodix_request_irq(struct goodix_ts_data *ts)
{
return devm_request_threaded_irq(&ts->client->dev, ts->client->irq,
NULL, goodix_ts_irq_handler, ts->irq_flags, ts->client->name, ts);
}

static int goodix_get_gpio_config(struct goodix_ts_data *ts)
{
int error;
struct device *dev;
struct gpio_desc *gpiod;
dev = &ts->client->dev;

/* Get the interrupt GPIO pin number */
gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_INT_NAME, GPIOD_IN); // 解析 gpio144
ts->gpiod_int = gpiod;

/* Get the reset line GPIO pin number */
gpiod = devm_gpiod_get_optional(dev, GOODIX_GPIO_RST_NAME, GPIOD_IN); // 解析 reset引脚,使用tca9539扩展的GPIO作为中断引脚
ts->gpiod_rst = gpiod;
return 0;
}

static int goodix_ts_probe(struct i2c_client *client,const struct i2c_device_id *id){
error = goodix_get_gpio_config(ts);

// 解析 941 i2c地址: 0x0c
error = of_property_read_u32(client->dev.of_node, "addr_ds941", &val); ts->addr_ds941 = (u8)val;
// 解析 948 i2c地址: 0x2c
error = of_property_read_u32(client->dev.of_node, "addr_ds948", &val); ts->addr_ds948 = (u8)val;
// 解析 irq_channel: 3
error = of_property_read_u32(client->dev.of_node, "irq_channel", &val); ts->irq_channel = val;
// 解析 reset_channel: 2
error = of_property_read_u32(client->dev.of_node, "reset_channel", &val); ts->reset_channel = val;

error = of_property_read_u32(client->dev.of_node, "dev_type", &ts->dev_type);
error = of_property_read_u32(client->dev.of_node, "port_type", &ts->port_type);
}

static const struct of_device_id goodix_of_match[] = {
{ .compatible = "sdrv,ds941_ds948_gt9xx" },
{ }
};
MODULE_DEVICE_TABLE(of, goodix_of_match);

static struct i2c_driver goodix_ts_driver = {
.probe = goodix_ts_probe,
.remove = goodix_ts_remove,
.id_table = goodix_ts_id,
.driver = {
.name = "Semidrive DS941 DS948 Goodix-TS",
.acpi_match_table = ACPI_PTR(goodix_acpi_match),
.of_match_table = of_match_ptr(goodix_of_match),
.pm = &goodix_pm_ops,
},
};
module_i2c_driver(goodix_ts_driver);

我们进入 ​​devm_request_threaded_irq​​ 函数中看一下:

# kernel/irq/devres.c
int devm_request_threaded_irq(struct device *dev, unsigned int irq,
irq_handler_t handler, irq_handler_t thread_fn, unsigned long irqflags, const char *devname, void *dev_id)
{
struct irq_devres *dr;
int rc;
dr = devres_alloc(devm_irq_release, sizeof(struct irq_devres),GFP_KERNEL);

devname = dev_name(dev);

rc = request_threaded_irq(irq, handler, thread_fn, irqflags, devname, dev_id);

dr->irq = irq;
dr->dev_id = dev_id;
devres_add(dev, dr);
return 0;
}



1.2.3 SD Card 中断 GPIO 分析

​SD Card​​​ 中断​​GPIO​​​配置如下,同样 我也不知道 ​​ext_gpio74​​​ 说明的是哪一个​​GPIO​​引脚,等后面到板子打印看下。

# android\kernel\arch\arm64\boot\dts\semidrive\x9.dtsi
sdhci3: sdhci@341a0000 {
compatible = "snps,dwcmshc-sdhci";
reg = <0 0x341a0000 0 0x10000>;
interrupts = <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>;
sdrv,scr_signals_ddr = <SCR_SEC__mshc3_ddr_mode>;
vmmc-supply = <&vmmc_3v3>;
vqmmc-supply = <&vqmmc_3v3>;
status = "disabled";
};

# android\kernel\arch\arm64\boot\dts\semidrive\x9_high-plus_ref_native_ivi_serdes_8g.dts
&i2c11 {
status = "okay";
ext_gpio74: gpio@74 {
compatible = "ti,tca9539"; // 使用tca9539扩展的GPIO作为中断引脚
reg = <0x74>;
#gpio-cells = <2>;
gpio-controller;
};
&sdhci3 {
#clock-cells = <1>;
clocks = <&EMMC3>;
clock-names = "core";
max-frequency = <25000000>;
cd-gpios = <&ext_gpio74 14 GPIO_ACTIVE_LOW>; // 使用tca9539扩展的GPIO作为中断引脚
bus-width = <4>;
no-1-8-v;
no-mmc;
no-sdio;
disable-wp;
keep-power-in-suspend;
status = "okay";
};

我们进代码找下 它对应的源码在哪:​​grep -rsn "snps,dwcmshc-sdhci" .​

kernel/drivers/mmc/host/sdhci-of-dwcmshc.c:605:     { .compatible = "snps,dwcmshc-sdhci" },

kernel/drivers/mmc/host/mmci.c:1743: ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL);

其代码如下:

# buildsystem/android/kernel/drivers/mmc/host/mmci.c
static int mmci_probe(struct amba_device *dev, const struct amba_id *id)
{
if (!np) {
ret = mmc_gpiod_request_cd(mmc, "cd", 0, false, 0, NULL);
if (ret < 0) {
if (ret == -EPROBE_DEFER)
goto clk_disable;
else if (gpio_is_valid(plat->gpio_cd)) {
ret = mmc_gpio_request_cd(mmc, plat->gpio_cd, 0);
}
}
}
ret = devm_request_irq(&dev->dev, dev->irq[0], mmci_irq, IRQF_SHARED, DRIVER_NAME " (cmd)", host);
if (!dev->irq[1])
host->singleirq = true;
else {
ret = devm_request_irq(&dev->dev, dev->irq[1], mmci_pio_irq, IRQF_SHARED, DRIVER_NAME " (pio)", host);
}
writel(MCI_IRQENABLE, host->base + MMCIMASK0);
}



1.3 devmem 调试 GPIO 方法

1.3.1 基于 sysfs 接口访问 gpio

  1. 获取 ​​GPIO​​对应的​​Number​

    ​gpio Number​​ 与 ​​gpio​​ 硬件编号相同,那我们通过 ​​SDConfigtool​​ 获取 ​​gpio​​ 硬件编号(也即 ​​gpio Number​​),

    例如下图 ​​GPIO_C13 pin​​ 用作 ​​gpio​​ 功能时,其 ​​gpio number 为 61​

  2. ​GPIO​​ 操作

    通过命令导出 ​​gpio61​​: ​​echo 61 > export​

    进入 ​​gpio61​​ 目录执行 ​​gpio​​相关操作:

    【SemiDrive源码分析】【X9芯片驱动调试】13 - GPIO 配置方法_单片机

    此功能通常用于验证 ​​gpio​​ 输入/输出值是否正确,即输入模式则 ​​cat value​​ 可以得到输入值,

    而输出模式 ​​echo 1​​ 或者 ​​echo 0​​ 到 ​​value​​ 则输出高或者低等


要导出某​​gpio​​,它必须是未被驱动 ​​request​​ 的状态,
即如果有驱动在使用并​​request​​了则会失败。
如果对应的​​pin​​被配置为其他功能(非 ​​gpio​​),导出 ​​gpio​​ 操作将无效,
也就是只有在 ​​pin ​​被配置为 ​​gpio​​ 的情况下,通过 ​​sysfs​​ 去操作 ​​gpio​​ 才有意义




1.3.2 基于 debugfs 操作 gpio节点

​debugfs​​​ 下相关的节点则可以看到关于 ​​gpio​​ 总体的信息。

  1. 一共有五个​​gpio chip​​​,每个占用​​32​​​ 个​​gpio​​​,号码从​​0​​​ 到​​159​​。
  2. 各个组中已经申请了的​​ gpio​​​ 的信息,包括​​gpio​​号、名字、输入输出以及高低、是否用作了中断等

【SemiDrive源码分析】【X9芯片驱动调试】13 - GPIO 配置方法_ios_02


1.3.3 devmem 调试说明

在 ​​busybox ​​的杂项工具中提供了 ​​devmem​​ 工具,它可以在应用层进行物理地址的读写访问,

实际就是对​​/dev/mem​​ 操作来完成

Usage: devmem ADDRESS [WIDTH [VALUE]]
Read/write from physical
ADDRESS Address to act upon :需要进行读写访问的物理地址
WIDTH Width (8/16/...) :访问数据类型
VALUE Data to be writte :如果是读操作省略;如果是写操作,表示需要写入的数据

基本测试用法
# devmem 0x44e07134 16
0xFFEF
# devmem 0x44e07134 32
0xFFFFFFEF
# devmem 0x44e07134 8
0xE
  • 寄存器部分说明

    【SemiDrive源码分析】【X9芯片驱动调试】13 - GPIO 配置方法_stm32_03

    其中 ​​m​​ 表示对应的 ​​gpio​​ 组编号,​​GPIO4​​ 一共有五个组,即​​0-4​​,得到的 ​​Addr ​​即是偏移值,

    再加上控制器本身的​​reg​​值则得到最终的物理内存地址。一个寄存器 ​​32 ​​位,刚好对应组中的​​32​​ 个 ​​gpio​

  • ​port4a gpio4​​ 操作实例

    首先进行地址的计算

    ​gpio4​​ 控制器地址: ​​0x30420000​

    ​dir​​ 方向寄存器地址: ​​0x30420000 + 0x2000 + (0*0x10) = 0x30422000​

    ​out​​ 输出寄存器地址:​​0x30420000 + 0x2400 + (0*0x10) = 0x3​

    【SemiDrive源码分析】【X9芯片驱动调试】13 - GPIO 配置方法_stm32_04

    方向寄存器结果: 其中第四位是 ​​1​​ 即表示输出模式。

    ​0x08000010 = 1000 0000 0000 0000 0000 0001 0000​

    输出寄存器结果: 其中第四位时 1 时 0 即设置时高时低来达到一个 heartbeat 的效果

    ​0x08000010 = 1000 0000 0000 0000 0000 0001 0000​

    ​0x08000000 = 1000 0000 0000 0000 0000 0000 0000​


二、SafetyOS 下 GPIO 配置方法(Safety Domain - Dual Core R5)

2.1 SDToolBox GPIO 配置与烧录方法

​SDToolBox​​ 工具路径位于: ​​buildsystem\res\tools\release\SDToolBox​

项目GPIO配置文件位于:​​buildsystem\rtos\lk_boot\chipcfg\generate\x9_high-plus​

【SemiDrive源码分析】【X9芯片驱动调试】13 - GPIO 配置方法_ios_05

进入​​SDConfigTool​​ 工具,打开项目文件:

【SemiDrive源码分析】【X9芯片驱动调试】13 - GPIO 配置方法_stm32_06

完整界面截图如下,根据 硬件原理图做相关的配置:

【SemiDrive源码分析】【X9芯片驱动调试】13 - GPIO 配置方法_单片机_07

配置完后,分别点如下三个​​Save​​ 按钮分别保存 ​​prj、bin、conf​​文件。

生成的 ​​system_config.bin​​文件,这个部分需要烧录到 ​​system_config ​​分区

【SemiDrive源码分析】【X9芯片驱动调试】13 - GPIO 配置方法_单片机_08


2.2 SafetyOS 下配置GPIO

2.2.1 配置 PINMUX 为 GPIO

在 ​​Safety Domain​​ 中,可以通过代码配置 ​​pinmux​​ 为​​GPIO​​,该配置方法会覆盖​​PC​​工具配的​​system config​​中的配置。

​pinmux​​ 建议在 ​​system config​​ 中配置便于同一查看,此处只提供方法,不建议在代码中操作。

  1. 获取​​GPIO​​​ 操作的​​handle​
bool hal_dio_creat_handle(void **handle, uint32_t dio_res_glb_idx)
# handle: 获取的句柄
# dio_res_glb_idx: 传入 g_iomuxc_res.res_id[0
  1. 设置​​PINMUX​
int hal_port_set_pin_mode(void *handle, const Port_PinType pin, const Port_PinModeType mode)

# void *handle: 获取的操作 GPIO 的句柄

# const Port_PinType pin: 设置的 pin,
//使用的 GPIO 编号,在${freertos}\chipdev\port\sd_port\inc\port_cfg_def.h 文件中查找自己需要使用的。

# const Port_PinModeType mode: 设置 pin 的模式, Port_PinModeType
方向:
PORT_PAD_IS__OUT 输出
PORT_PAD_IS__IN 输入
上下拉状态
PORT_PIN_IN_NO_PULL 悬空
PORT_PIN_IN_PULL_UP 上拉
PORT_PIN_IN_PULL_DOWN 下拉
功能
PORT_PIN_MODE_GPIO 引脚功能配置为 GPIO
  1. 释放​​GPIO​​​ 操作的​​handle​
bool hal_port_release_handle(void **handle)
参数说明:
void **handle: 释放操作 GPIO 的句
  1. 以​​GPIO D8​​​ 为例,配置​​GPIO pinmux​​,代码例子:
#include <hal_port.h>
#include <hal_dio.h>

const Port_PinType PIN_GPIO_D8_M0_GPIO_D8 = PortConf_PIN_GPIO_D8;

const Port_PinModeType MODE_GPIO_D8_M0_GPIO_D8 = {
((uint32_t)PORT_PAD_POE__DISABLE | PORT_PAD_IS__OUT | PORT_PAD_SR__FAST |
PORT_PAD_DS__MID1 | PORT_PIN_IN_NO_PULL), ((uint32_t)PORT_PIN_MUX_FV__MIN | PORT_PIN_MUX_FIN__MIN |
PORT_PIN_OUT_PUSHPULL | PORT_PIN_MODE_GPIO), };

extern const domain_res_t g_iomuxc_res;
void backlight_enable(void)
{
static void *some_port_handle;
bool ret;
/* Port setup */
// 申请 与配置GPIO
ret = hal_port_creat_handle(&some_port_handle, g_iomuxc_res.res_id[0]);
ret = hal_port_set_pin_mode(some_port_handle, PIN_GPIO_D8_M0_GPIO_D8, MODE_GPIO_D8_M0_GPIO_D8);
......
// 使用完后释放GPIO
hal_port_release_handle(&some_port_handle);
}



2.2.2 配置 GPIO 输出

Pinmux 配置成 GPIO 输出后,在 Safety 代码中可以操作 GPIO 输出高电平

操作步骤:

  1. 获取 GPIO 操作 handle
bool hal_dio_creat_handle(void **handle, uint32_t dio_res_glb_idx)
# handle: 获取的句柄
# dio_res_glb_idx: 传入 g_iomuxc_res.res_id[0]
  1. 控制 GPIO 输出高低电平
void hal_dio_write_channel(void *handle, const Dio_ChannelType ChannelId, const Dio_LevelType Level)

void *handle 获取的操作 GPIO 的句柄
const Dio_ChannelType ChannelId: 操作的 GPIO
const Dio_LevelType Level: 输出高低电平;1:高电平,0:低电平
  1. 释放获取的 GPIO handle: ​​bool hal_dio_release_handle(void **handle)​
  2. 以 GPIO D8 为例配置 GPIO 输出高低
#include <hal_port.h>
#include <hal_dio.h>
const Port_PinType PIN_GPIO_D8_M0_GPIO_D8 = PortConf_PIN_GPIO_D8;
extern const domain_res_t g_gpio_res;

void backlight_enable(void)
{
static void *some_dio_handle;
bool ret;

/* Dio setup */
ret = hal_dio_creat_handle(&some_dio_handle, g_gpio_res.res_id[0]);
hal_dio_write_channel(some_dio_handle, PIN_GPIO_D8_M0_GPIO_D8, 1);
.....

hal_dio_release_handle(&some_dio_handle);
}



2.2.3 配置 GPIO 输入

Pinmux 配置成 GPIO 输入后,在 Safety 代码中可以获取 GPIO 输入的状态。

  1. 获取 GPIO 操作 handle
bool hal_dio_creat_handle(void **handle, uint32_t dio_res_glb_idx)

参数说明:
void **handle: 获取的句柄
uint32_t dio_res_glb_idx: 传入 g_iomuxc_res.res_id[0]
  1. 读取 GPIO 输入的高低电平
Dio_LevelType hal_dio_read_channel(void *handle, const Dio_ChannelType ChannelId, const Dio_LevelType Level)

参数说明
void *handle 获取的操作 GPIO 的句柄
const Dio_ChannelType ChannelId: 读取的 GPIO

返回值:Dio_LevelType 读取的电平
  1. 释放获取的 GPIO handle: ​​bool hal_dio_release_handle(void **handle)​
  2. 以 GPIO D8 为例配置 GPIO 获取输入状态,代码
#include <hal_port.h>
#include <hal_dio.h>

const Port_PinType PIN_GPIO_D8_M0_GPIO_D8 = PortConf_PIN_GPIO_D8;
extern const domain_res_t g_gpio_res;

void backlight_enable(void)
{
static void *some_dio_handle;
bool ret;
Dio_LevelType dio_value;
/* Dio setup */
ret = hal_dio_creat_handle(&some_dio_handle, g_gpio_res.res_id[0]);
dio_value = hal_dio_read_channel(some_dio_handle, PIN_GPIO_D8_M0_GPIO_D8);
........
hal_dio_release_handle(&some_dio_handle);
}



2.3 SafetyOS 下配置 GPIO 中断实例

​Pinmux​​​ 配置成 ​​GPIO​​​ 输入后,​​Safety​​​ 下 ​​GPIO​

  1. 注册 GPIO 中断
int register_gpio_int_handler(uint16_t ChannelId, int irqflag, int_handler handler, void *arg)

参数说明:
uint16_t ChannelId: 使用的 GPIO
int irqflag: 中断触发方式
#define IRQ_TYPE_NONE DIO_IRQ_TYPE_NONE
#define IRQ_TYPE_EDGE_RISING DIO_IRQ_TYPE_EDGE_RISING
#define IRQ_TYPE_EDGE_FALLING DIO_IRQ_TYPE_EDGE_FALLING
#define IRQ_TYPE_EDGE_BOTH DIO_IRQ_TYPE_EDGE_BOTH
#define IRQ_TYPE_LEVEL_HIGH DIO_IRQ_TYPE_LEVEL_HIGH
#define IRQ_TYPE_LEVEL_LOW DIO_IRQ_TYPE_LEVEL_LOW
int_handler handler: 中断处理函数
void *arg: 中断触发时出入到中断处理函数的参数
  1. 使能 GPIO 中断
int unmask_gpio_interrupt(uint16_t ChannelId)
参数说明:
uint16_t ChannelId: 使用的 GPI
  1. 失能 GPIO 中断
int mask_gpio_interrupt(uint16_t ChannelId)

参数说明:
uint16_t ChannelId: 使用的 GPI
  1. 以 GPIO D8 为例配置 GPIO 中断触发
#include <hal_port.h>
#include <hal_dio.h>

static int lt9611_work_func(void *arg)
{
while (1) {
event_wait(&event_hdmi_hpd);
//do something
unmask_gpio_interrupt(PortConf_PIN_GPIO_D8);
}
return 0;
}
lt9611_thread = thread_create("lt9611", lt9611_work_func, NULL, DEFAULT_PRIORITY, DEFAULT_STACK_SIZE);
thread_detach_and_resume(lt9611_thread);


static enum handler_return d8_irq_handler(void *arg)
{
mask_gpio_interrupt(PortConf_PIN_GPIO_D8);
event_signal(&event_hdmi_hpd, false);
return INT_RESCHEDULE;
}

event_init(&event_hdmi_hpd, false, EVENT_FLAG_AUTOUNSIGNAL);
register_gpio_int_handler(PortConf_PIN_GPIO_D8, IRQ_TYPE_EDGE_FALLING, d8_irq_handler, NULL);

。。。。。。。。。

unmask_gpio_interrupt(PortConf_PIN_GPIO_D8);



2.4 SafetyOS 下配置 GPIO 调试方法

2.4.1 get_pin_info

​Safety ​​下可以通过此命令获取 9 系列芯片所有 PIN 的状态。

进入 ​​safety Command terminal​​​,输入​​ help​​​,查看是否有​​ get_pin_info​​​;若存在即可在​​Command terminal​​中使用。

使用方法 ​​get_pin_info​​ 或 ​​get_pin_info [pin_index];​​不加 ​​pin_index​​ 参数会打印所有 ​​PIN​​ 的状态;

加 ​​pin_index​​ 获取指定 ​​PIN ​​的状态。

以获取 ​​GPIO_A4​​​ 的状态举例,并说明 ​​get_pin_info 28​

# get_pin_info 28
pin[28], pad addr: 0xfc521000, value: 0x1011
mux addr: 0xfc59c000, value: 0x0
gpio addr: 0xf0420040, value: 0x4001
pin[28] is set to GPIO, info as following:
GPIO controller: GPIO4
Direction: output
Level: Low
Pull Down/Up: Pull Down
PAD Mode: Push-Pull
Registers values as following:
PAD config value 0x1011
Mux config value 0x0
GPIO4 controller config value 0x4001

【SemiDrive源码分析】【X9芯片驱动调试】13 - GPIO 配置方法_ios_09

2.4.2 操作寄存器

以 GPIO_A4 为例

  1. 首先进行地址的计算

    ​gpio4​​ 控制器地址: ​​0xf0420000​

    ​dir ​​方向寄存器地址: ​​0xf0420000 + 0x2000 + (0*0x10) = 0xf0422000​

    ​out​​ 输出寄存器地址: ​​0xf0420000 + 0x2400 + (0*0x10) = 0xf0422400​

  2. 通过 ​​Safety​​ 下的命令 ​​dw​​ 查看
dw 使用方法:
dw : display memory in words
dw [reg] [length]
mw 使用方法:
mw : modify word of memory
mw [reg]
# dw 0xf0422000 1
0xf0422000: 08000010
# dw 0xf0422400
0xf0422400: 08000000
# dw 0xf0422400
0xf0422400: 08000010

方向寄存器结果: 其中第四位是 1 即表示输出模式。

​0x08000010 = 1000 0000 0000 0000 0000 0001 0000​

输出寄存器结果: 其中第四位时 1 时 0 即设置时高时低来达到一个 ​​heartbeat​​ 的效果

​0x08000010 = 1000 0000 0000 0000 0000 0001 0000​

​0x08000000 = 1000 0000 0000 0000 0000 0000 0000​


三、QNX 下 GPIO 配置方法(AP2 Domain - 1 * A55)

目前 没有最新的QNX 代码,等后续 QNX 代码到了后再分析后更新吧。



举报

相关推荐

0 条评论