RK3568驱动指南|第十一篇 pinctrl子系统-第120章 pinctrl子系统的引入

瑞芯微RK3568芯片是一款定位中高端的通用型SOC,采用22nm制程工艺,搭载一颗四核Cortex-A55处理器和Mali G52 2EE 图形处理器。RK3568 支持4K 解码和 1080P 编码,支持SATA/PCIE/USB3.0 外围接口。RK3568内置独立NPU,可用于轻量级人工智能应用。RK3568 支持安卓 11 和 linux 系统,主要面向物联网网关、NVR 存储、工控平板、工业检测、工控盒、卡拉 OK、云终端、车载中控等行业。


【公众号】迅为电子

【粉丝群】824412014(加群获取驱动文档+例程)

【视频观看】嵌入式学习之Linux驱动(第十一期_pinctrl子系统_全新升级)_基于RK3568

【购买链接】迅为RK3568开发板瑞芯微Linux安卓鸿蒙ARM核心板人工智能AI主板


第十一篇pinctrl子系统

第120章 pinctrl子系统的引入

Linux中的pinctrl子系统(Pin Control Subsystem)是一个用于管理和配置通用输入/输出(GPIO)引脚的框架。它提供了一种标准化的方法,以在Linux内核中对GPIO引脚进行配置、分配和控制,从而适应不同的硬件平台和设备。

pinctrl子系统也符合Linux内核的设备模型规范,所以pinctrl子系统同样可以根据设备模型规范分为设备、驱动、总线和类四个部分,本章节将从设备和驱动两个部分来引入pinctrl子系统。

120.1 pinctrl 设备树

在前面设备树相关的章节中已经对pinctrl节点的编写和使用进行了讲解,设备树的pinctrl可以分为客户端和服务端两个部分,在pinctrl客户端可以指定引脚描述、引脚组描述和配置描述,以满足其特定的功能和需求,不同厂商在客户端内容的编写格式是相同的。服务端是指提供GPIO引脚配置的pinctrl设备树节点,它是描述GPIO引脚配置和使用规则的节点,定义了一组GPIO引脚的配置选项,以及这些选项对应的引脚功能和电气特性。

接下来对rk3568的pinctrl设备树进行详细的讲解。首先在rk3568.dtsi设备树根节点下找到pinctrl节点,具体内容如下所示:

   pinctrl: pinctrl {
        compatible = "rockchip,rk3568-pinctrl";
        rockchip,grf = <&grf>;
        rockchip,pmu = <&pmugrf>;
        #address-cells = <2>;
        #size-cells = <2>;
        ranges;

        gpio0: gpio@fdd60000 {
            compatible = "rockchip,gpio-bank";
            reg = <0x0 0xfdd60000 0x0 0x100>;
            interrupts = ;
            clocks = <&pmucru PCLK_GPIO0>, <&pmucru DBCLK_GPIO0>;

            gpio-controller;
            #gpio-cells = <2>;
            gpio-ranges = <&pinctrl 0 0 32>;
            interrupt-controller;                                                                                                                                                                                                     
            #interrupt-cells = <2>;
        };

        gpio1: gpio@fe740000 {
            compatible = "rockchip,gpio-bank";
            reg = <0x0 0xfe740000 0x0 0x100>;
            interrupts = ;
            clocks = <&cru PCLK_GPIO1>, <&cru DBCLK_GPIO1>;

            gpio-controller;
            #gpio-cells = <2>;
            gpio-ranges = <&pinctrl 0 32 32>;
            interrupt-controller;
            #interrupt-cells = <2>;
        };   

        gpio2: gpio@fe750000 {
            compatible = "rockchip,gpio-bank";
            reg = <0x0 0xfe750000 0x0 0x100>;
            interrupts = ;
            clocks = <&cru PCLK_GPIO2>, <&cru DBCLK_GPIO2>;

            gpio-controller;
            #gpio-cells = <2>; 
            gpio-ranges = <&pinctrl 0 64 32>;
            interrupt-controller;
            #interrupt-cells = <2>; 
        };   

        gpio3: gpio@fe760000 {
            compatible = "rockchip,gpio-bank";
            reg = <0x0 0xfe760000 0x0 0x100>;
            interrupts = ;
            clocks = <&cru PCLK_GPIO3>, <&cru DBCLK_GPIO3>;

            gpio-controller;
            #gpio-cells = <2>;
            gpio-ranges = <&pinctrl 0 96 32>;
            interrupt-controller;
            #interrupt-cells = <2>;
        };

        gpio4: gpio@fe770000 {
            compatible = "rockchip,gpio-bank";
            reg = <0x0 0xfe770000 0x0 0x100>;
            interrupts = ;
            clocks = <&cru PCLK_GPIO4>, <&cru DBCLK_GPIO4>;

            gpio-controller;
            #gpio-cells = <2>;
            gpio-ranges = <&pinctrl 0 128 32>;
            interrupt-controller;
            #interrupt-cells = <2>;
        };
    };
};

#include "rk3568-pinctrl.dtsi"

在上面的pinctrl节点中,描述了RK3568 GPIO控制器的配置和使用方式,pinctrl节点总共描述了五个GPIO控制器,分别是gpio0、gpio1、gpio2、gpio3和gpio4。通过这些GPIO控制器节点,可以在设备树中配置和控制RK3568芯片上的GPIO引脚,包括设置引脚功能、中断处理等。

在设备树的最下方通过include包含了rk3568-pinctrl.dtsi设备树,该设备树中包含了所有复用功能的配置,具体内容如下所示(更具体的内容描述可以回顾设备树pinctrl章节内容):

RK3568驱动指南|第十一篇 pinctrl子系统-第120章 pinctrl子系统的引入_第1张图片

图 120-1

无论是rk3568.dtsi设备树中的pinctrl节点,还是上面rk3568-pinctrl.dtsi设备树中的一系列复用关系都是由瑞芯微原厂BSP工程师编写的,我们只需知道如何使用即可,而pinctrl客户端设备树是由我们自己根据特定需求来编写的,具体可以回顾前面设备树相关的章节,这里就不再进行赘述。

设备树中存放的只是设备的描述信息,而具体的功能实现取决于相应的pinctrl驱动,根据rk3568.dtsi设备树中pinctrl节点的compatible属性进行查找,可以查找到pinctrl的驱动文件是内核源码的“/driver/pinctrl/pinctrl-rockchip.c”,如下所示:

图120- 2

在下个小节中将对pinctrl的驱动部分进行简单的介绍。

120.2 pinctrl 驱动

首先进入到内核源码目录下的“/drivers/pinctrl/pinctrl-rockchip.c”驱动文件中,找到驱动的入口函数,具体内容如下所示:

static struct platform_driver rockchip_pinctrl_driver = {
    .probe      = rockchip_pinctrl_probe,
    .driver = {
        .name   = "rockchip-pinctrl",
        .pm = &rockchip_pinctrl_dev_pm_ops,
        .of_match_table = rockchip_pinctrl_dt_match,
    },
};

static int __init rockchip_pinctrl_drv_register(void)
{                                                                                                                                                                                                                                     
    return platform_driver_register(&rockchip_pinctrl_driver);
}
postcore_initcall(rockchip_pinctrl_drv_register);

static void __exit rockchip_pinctrl_drv_unregister(void)
{
    platform_driver_unregister(&rockchip_pinctrl_driver);
}

可以看到pinctrl驱动使用的是platform总线,当设备和驱动匹配成功之后会进入rockchip_pinctrl_probe函数进行初始化,probe函数的具体内容如下所示:

static int rockchip_pinctrl_probe(struct platform_device *pdev)
{
    struct rockchip_pinctrl *info;       // Rockchip GPIO控制器的信息结构体指针
    struct device *dev = &pdev->dev;     // 设备结构体指针
    struct rockchip_pin_ctrl *ctrl;      // Rockchip GPIO控制器的配置结构体指针
    struct device_node *np = pdev->dev.of_node, *node;          // 设备节点指针
    struct resource *res;                                       // 设备资源指针
    void __iomem *base;                                         // 寄存器基地址指针
    int ret;                                                    // 返回值

    if (!dev->of_node) {
        dev_err(dev, "device tree node not found\n");
        return -ENODEV;
    }

    // 分配并初始化一个rockchip_pinctrl结构体
    info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
    if (!info)
        return -ENOMEM;

    info->dev = dev;

    // 获取并设置与pdev相关的rockchip_pin_ctrl结构体
    ctrl = rockchip_pinctrl_get_soc_data(info, pdev);
    if (!ctrl) {
        dev_err(dev, "driver data not available\n");
        return -EINVAL;
    }
    info->ctrl = ctrl;

    // 解析设备树中的"rockchip,grf"节点,获取寄存器映射基地址
    node = of_parse_phandle(np, "rockchip,grf", 0);
    if (node) {
        info->regmap_base = syscon_node_to_regmap(node);
        if (IS_ERR(info->regmap_base))
            return PTR_ERR(info->regmap_base);
    } else {
        // 如果找不到"rockchip,grf"节点,则获取IORESOURCE_MEM类型的资源,得到寄存器基地址
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        base = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(base))
            return PTR_ERR(base);

        // 配置寄存器映射的最大寄存器地址和名称
        rockchip_regmap_config.max_register = resource_size(res) - 4;
        rockchip_regmap_config.name = "rockchip,pinctrl";
        info->regmap_base = devm_regmap_init_mmio(&pdev->dev, base,
                            &rockchip_regmap_config);

        // 检查旧的dt-bindings
        info->reg_size = resource_size(res);

        // 如果控制器类型为RK3188且reg_size小于0x200,则获取第二个IORESOURCE_MEM类型的资源,作为pull寄存器的基地址
        if (ctrl->type == RK3188 && info->reg_size < 0x200) {
            res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
            base = devm_ioremap_resource(&pdev->dev, res);
            if (IS_ERR(base))
                return PTR_ERR(base);

            // 配置pull寄存器映射的最大寄存器地址和名称
            rockchip_regmap_config.max_register =
                            resource_size(res) - 4;
            rockchip_regmap_config.name = "rockchip,pinctrl-pull";
            info->regmap_pull = devm_regmap_init_mmio(&pdev->dev,
                            base,
                            &rockchip_regmap_config);
        }
    }

    // 尝试查找可选的pmu syscon引用
    node = of_parse_phandle(np, "rockchip,pmu", 0);
    if (node) {
        info->regmap_pmu = syscon_node_to_regmap(node);
        if (IS_ERR(info->regmap_pmu))
            return PTR_ERR(info->regmap_pmu);
    }

    // 对于某些SoC进行特殊处理
    if (ctrl->soc_data_init) {
        ret = ctrl->soc_data_init(info);
        if (ret)
            return ret;
    }

    // 注册rockchip_pinctrl设备
    ret = rockchip_pinctrl_register(pdev, info);
    if (ret)
        return ret;

    // 设置pdev的私有数据为info
    platform_set_drvdata(pdev, info);

    // 注册GPIO设备
    ret = of_platform_populate(np, rockchip_bank_match, NULL, NULL);
    if (ret) {
        dev_err(&pdev->dev, "failed to register gpio device\n");
        return ret;
    }
    dev_info(dev, "probed %s\n", dev_name(dev));

    return 0;
}

上面Probe函数的作用是初始化和配置Rockchip GPIO控制器,并将相关信息存储在rockchip_pinctrl结构体中,最后注册相关设备和GPIO接口,关于Probe函数会在后面的小节中进行更加具体的分析。

最后来带领大家思考一个问题,假如我们要配置一个LED外设,该LED需要使用一个管脚来进行控制,那这个控制引脚需要复用成GPIO之后才能完成相应的功能,通过上面内容的学习之后,我们知道是pinctrl子系统将这个管脚复用为了GPIO功能,那pinctrl子系统是什么时候对该引脚进行的复用呢?带着这个疑问,让我们一起进入后面章节的学习吧。

你可能感兴趣的:(RK3568驱动开发指南,#,RK3568驱动指南,第十一期,驱动开发,linux)