Pinctrl学习

这一篇2月就TODO了,因为之前一直没怎么用到,所以拖到现在。

因为我之前一直用树莓派或者esp32这些,感觉GPIO这些就是直接用就完了,所以Pinctrl的概念一直不明白,最近因为主动请缨申请了点屏幕,要用到这个,所以再看看。这里不写原理了,只写用法。

设备树

&pinctrl {
    uart1_pins: uart1 {
        pinmux = <0x18 0x1C>;
        bias-disable;
    };
};

&uart1 {
    pinctrl-names = "default";
    pinctrl-0 = <0x20 0x24>;
    status = "okay";
};

在这个示例中,uart1_pins 定义了 UART1 的引脚配置,指定了 PINMUX_UART1_TX 和 PINMUX_UART1_RX 的引脚复用,同时禁用了引脚的上拉或下拉偏置。uart1 节点引用了这些引脚配置,并将其应用到 UART1 设备。

再给一个详细的例子

引脚控制器节点:

pinctrl: pinctrl@44e10800 {
    compatible = "ti,omap4-pinctrl";
    reg = <0x44e10800 0x100>;
    #address-cells = <1>;
    #size-cells = <0>;
    pinctrl-single,register-width = <32>;
    pinctrl-single,function-mask = <0x7f>;
};

compatible 属性指定了引脚控制器的类型。
reg 属性指定了引脚控制器的寄存器地址和大小。这里的0x100表示0x00到0xff,一个寄存器占4个字节,所以一共差不多64个寄存器。
#address-cells 和 #size-cells 属性定义了子节点的地址和大小单元。
pinctrl-single,register-width 和 pinctrl-single,function-mask 是特定引脚控制器的属性,用于配置引脚复用寄存器的宽度和功能掩码。

所以可以看出,这里的pinctrl支持的是一大组寄存器。

定义引脚复用和配置

&pinctrl {
    uart1_pins: uart1_pins {
        pinmux = <0x18 0x1C>;
        bias-disable;
        drive-strength = <2>;
    };
    
    i2c1_pins: i2c1_pins {
        pinmux = <0x20 0x24>;
        bias-pull-up;
    };
};

uart1_pins 和 i2c1_pins 是两个引脚配置节点。
pinmux 属性指定了引脚的复用设置(如 UART1 TX 和 RX,引脚具体值由 SoC 数据手册定义)。
bias-disable 和 bias-pull-up 是引脚配置属性,用于配置引脚的上拉/下拉电阻。
drive-strength 属性配置了引脚驱动强度。

这部分的配置不同厂商可能差别很大。

在设备节点中引用引脚配置

&uart1 {
    pinctrl-names = "default";
    pinctrl-0 = <&uart1_pins>;
    status = "okay";
};

&i2c1 {
    pinctrl-names = "default";
    pinctrl-0 = <&i2c1_pins>;
    status = "okay";
};

pinctrl-names 属性指定了引脚配置的名称。
pinctrl-0 属性引用了定义的引脚配置节点(如 uart1_pins 和 i2c1_pins)。
status 属性设置为 "okay",表示设备处于启用状态。

驱动代码

#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/i2c.h>

static const struct of_device_id my_i2c_of_match[] = {
    { .compatible = "myvendor,my-i2c-device", },
    { /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, my_i2c_of_match);

static int my_i2c_probe(struct platform_device *pdev)
{
    struct device_node *np = pdev->dev.of_node;

    // 获取和配置引脚
    struct pinctrl *pinctrl = devm_pinctrl_get(&pdev->dev);
    struct pinctrl_state *state;

    if (IS_ERR(pinctrl)) {
        dev_err(&pdev->dev, "Failed to get pinctrl\n");
        return PTR_ERR(pinctrl);
    }

    state = pinctrl_lookup_state(pinctrl, "default");
    if (IS_ERR(state)) {
        dev_err(&pdev->dev, "Failed to lookup pinctrl state\n");
        return PTR_ERR(state);
    }

    pinctrl_select_state(pinctrl, state);

    // 其他初始化代码...
    
    return 0;
}

static int my_i2c_remove(struct platform_device *pdev)
{
    // 清理代码
    return 0;
}

static struct platform_driver my_i2c_driver = {
    .driver = {
        .name = "my_i2c_driver",
        .of_match_table = my_i2c_of_match,
    },
    .probe = my_i2c_probe,
    .remove = my_i2c_remove,
};

module_platform_driver(my_i2c_driver);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Author");
MODULE_DESCRIPTION("My I2C Driver");

在这个示例中,驱动程序在 probe 函数中获取 pinctrl 句柄,查找 default 状态,并将其应用到设备。这样,设备的引脚配置将在设备初始化时自动设置。

使用代码

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <linux/i2c-dev.h>

int main(int argc, char *argv[])
{
    int file;
    int adapter_nr = 1; /* usually i2c-1 */
    char filename[20];
    int addr = 0x50; /* The I2C address of the device */

    snprintf(filename, 19, "/dev/i2c-%d", adapter_nr);
    file = open(filename, O_RDWR);
    if (file < 0) {
        perror("Failed to open the i2c bus");
        exit(1);
    }

    if (ioctl(file, I2C_SLAVE, addr) < 0) {
        perror("Failed to acquire bus access and/or talk to slave");
        exit(1);
    }

    // Write to the I2C device
    char buffer[2] = {0x00, 0xA5}; // Example: register 0x00, value 0xA5
    if (write(file, buffer, 2) != 2) {
        perror("Failed to write to the i2c device");
    } else {
        printf("Data written successfully\n");
    }

    // Read from the I2C device
    buffer[0] = 0x00; // Register to read from
    if (write(file, buffer, 1) != 1) {
        perror("Failed to write to the i2c device");
    } else {
        if (read(file, buffer, 1) != 1) {
            perror("Failed to read from the i2c device");
        } else {
            printf("Data read from the device: 0x%02x\n", buffer[0]);
        }
    }

    close(file);
    return 0;
}

pinctrl 是 Linux 内核中的一个公共子系统模块,用于管理和配置 SoC(System on Chip)上的引脚。它提供了一种统一的方式来配置引脚的功能和属性,如 GPIO、I2C、SPI 等。

pinctrl 子系统的代码位置

pinctrl 子系统的代码主要位于 Linux 内核源码树的以下目录中:

  • 核心代码drivers/pinctrl/
  • 设备树支持Documentation/devicetree/bindings/pinctrl/
  • 头文件include/linux/pinctrl/

I2C的位置可以通过dmesg | grep i2c看到。

小结

Pinctrl主要就是配置了引脚,用了哪些管脚?这些管脚复用为什么功能?这些管脚的初始状态是什么。其实就是为了简化移植而来的,如果整个功能从A板移植到B板,那么驱动代码不用动,使用代码不用动,设备树要动,但是不多,就一点点,就是那个pinmux。重新定义一下引脚就可以了。pinmux其实也就是引脚复用的意思。。

所以我看看pinmux就好。。。

再说一个,有Pinctrl和没有Pinctrl区别在哪呢?

/ {
    // 定义引脚控制器
    pinctrl: pinctrl@fd510000 {
        compatible = "qcom,tlmm";
        reg = <0xfd510000 0x1000>;
        gpio-controller;
        #gpio-cells = <2>;
        pinctrl-names = "default";
        pinctrl-0 = <&tlmm_default>;
    };
    // 定义引脚配置
    tlmm_default: pinmux {
        pinctrl-single,pins = <
            0x10 0x1  // 引脚 16 配置为功能 1
            0x20 0x2  // 引脚 32 配置为功能 2
        >;
    };
    // 使用引脚配置
    uart0: serial@78B0000 {
        compatible = "qcom,msm-uart";
        reg = <0x78B0000 0x1000>;
        pinctrl-names = "default";
        pinctrl-0 = <&uart0_pins>;
        uart0_pins: pinmux {
            pinctrl-single,pins = <
                0x30 0x1  // 引脚 48 配置为 UART 功能
                0x31 0x1  // 引脚 49 配置为 UART 功能
            >;
        };
    };
};

没有

/ {
    // 没有引脚控制器和引脚配置节点
    uart0: serial@78B0000 {
        compatible = "qcom,msm-uart";
        reg = <0x78B0000 0x1000>;
        // 没有 pinctrl-names 和 pinctrl-0 属性
    };
};

很多功能就在驱动代码中:

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#define UART0_BASE 0x78B0000
#define UART0_PIN_CONFIG 0x1234  // 假设的引脚配置寄存器地址
static int uart0_probe(struct platform_device *pdev)
{
    void __iomem *base;
    base = ioremap(UART0_BASE, 0x1000);
    if (!base)
        return -ENOMEM;
    // 硬编码引脚配置
    writel(UART0_PIN_CONFIG, base + 0x00);
    // 其他初始化代码
    ...
    return 0;
}
static int uart0_remove(struct platform_device *pdev)
{
    // 清理代码
    ...
    return 0;
}
static const struct of_device_id uart0_of_match[] = {
    { .compatible = "qcom,msm-uart", },
    {},
};
MODULE_DEVICE_TABLE(of, uart0_of_match);
static struct platform_driver uart0_driver = {
    .probe = uart0_probe,
    .remove = uart0_remove,
    .driver = {
        .name = "uart0",
        .of_match_table = uart0_of_match,
    },
};
module_platform_driver(uart0_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Author");
MODULE_DESCRIPTION("UART0 Driver without pinctrl");

参考:

https://blog.csdn.net/qq_33141353/category_11633447.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值