Linux驱动开发(速记版)--printctl子系统

第102章 pinctrl 子系统的引入

        Linux中的 pinctrl子系统是管理和配置GPIO引脚的框架,提供标准化方法以适应不同硬件。

        它遵循 Linux内核设备模型,分为设备、驱动等部分。

        本章节从设备和驱动角度介绍 pinctrl子系统。

102.1 pinctrl 设备树

        在设备树中,RK3568的pinctrl配置被详细定义,

        主要包括服务端(pinctrl节点及其下的GPIO控制器)

        客户端(在rk3568-pinctrl.dtsi中定义的引脚复用配置)

服务端配置(rk3568.dtsi):

        在设备树根节点下,定义了pinctrl节点,指定了兼容性和时钟等属性。

        包含了五个GPIO控制器(gpio0-gpio4),每个都指定了地址、中断、时钟等配置。

        通过 gpio-ranges属性将GPIO引脚映射到 pinctrl系统。

pinctrl: pinctrl {  
    compatible = "rockchip,rk3568-pinctrl";  //兼容性字符串
    gpio0: gpio@fdd60000 { ... };  
    gpio1: gpio@fe740000 { ... };  
    ...  
    #include "rk3568-pinctrl.dtsi"  
};

        在设备树的最下方通过 include 包含了 rk3568-pinctrl.dtsi 设备树,该设备树中包含了所有复用功能的配置。

客户端配置(rk3568-pinctrl.dtsi):

        定义了具体的引脚复用配置,如 acodec的引脚配置。

        这些配置由瑞芯微原厂提供,用户通常只需根据需求使用。

&pinctrl {  
    acodec-pins: acodec-pins {  
        rockchip,pins = <  
            1 RK PB1 5 &pcfg_pull_none>,  
            1 RK PA1 5 &pcfg_pull_none>,  
            ...  
        >;  
    };  
    ...  
};

驱动实现:

        pinctrl 驱动根据设备树中的 compatible属性进行匹配。

        RK3568的 pinctrl驱动位于内核源码的 /driver/pinctrl/pinctrl-rockchip.c。

102.2 pinctrl 驱动

        在 /drivers/pinctrl/pinctrl-rockchip.c中,

        Rockchip pinctrl驱动的入口函数通过platform_driver_register注册了一个platform驱动。

        当设备与驱动匹配时,会调用 rockchip_pinctrl_probe()函数进行初始化。

rockchip_pinctrl_probe函数的主要作用包括:

        分配并初始化 rockchip_pinctrl结构体

        获取与平台设备相关的 rockchip_pin_ctrl结构体

        解析设备树中的"rockchip,grf"节点,获取寄存器映射基地址

        如果找不到"rockchip,grf"节点,则通过平台资源获取寄存器基地址,并配置寄存器映射。

        尝试查找可选的"rockchip,pmu"syscon引用。

        对某些SoC进行特殊处理。

        注册 rockchip_pinctrl设备。

        设置平台设备的私有数据。

        注册GPIO设备。

static struct platform_driver rockchip_pinctrl_driver = {  
    .probe = rockchip_pinctrl_probe,  
    .driver = {  
        .name = "rockchip-pinctrl",  
        .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 int rockchip_pinctrl_probe(struct platform_device *pdev)  
{  
    // 分配并初始化结构体  
    // 获取并设置soc数据  
    // 解析设备树,获取寄存器基地址  
    // 查找可选的pmu syscon引用  
    // 对某些SoC进行特殊处理  
    // 注册pinctrl设备  
    // 设置私有数据  
    // 注册GPIO设备  
    return 0;  
}

第103章 pinctrl probe 函数讲解

103.1 pinctrl_desc 结构体

        pinctrl_desc 结构体用于描述引脚控制器(pinctrl)的属性和操作,

struct pinctrl_desc {  
    const char *name;                    // 引脚控制器名称  
    const struct pinctrl_pin_desc *pins; // 引脚描述符数组  
    unsigned int npins;                  // 引脚数量  
    const struct pinctrl_ops *pctlops;  // 引脚控制操作函数  
    const struct pinmux_ops *pmxops;    // 引脚复用操作函数  
    const struct pinconf_ops *confops;  // 引脚配置操作函数  
    struct module *owner;               // 拥有模块  
  
    // 自定义配置参数(可选)  
    unsigned int num_custom_params;  
    const struct pinconf_generic_params *custom_params;  
    const struct pin_config_item *custom_conf_items;  
};

103.2 rockchip_pinctrl 结构体

        rockchip_pinctrl 结构体是瑞芯微为了适应其芯片特定需求和功能,对标准的 pinctrl_desc 结构体进行的封装。

/*瑞芯微对pinctrl结构体的封装*/
struct rockchip_pinctrl {  
    struct regmap *regmap_base;    // 基本寄存器映射  
    int reg_size;                  // 寄存器大小  
    struct regmap *regmap_pull;    // 拉取寄存器映射  
    struct regmap *regmap_pmu;     // PMU寄存器映射  
    struct device *dev;            // 设备指针  
    struct rockchip_pin_ctrl *ctrl; // 瑞芯微引脚控制器指针  
    struct pinctrl_desc pctl;      // 引脚控制器描述符(包含标准pinctrl_desc)  
    struct pinctrl_dev *pctl_dev;  // 引脚控制器设备指针  
    struct rockchip_pin_group *groups; // 引脚组指针  
    unsigned int ngroups;          // 引脚组数量  
    struct rockchip_pmx_func *functions; // 引脚功能指针  
    unsigned int nfunctions;       // 引脚功能数量  
};

103.3 pinctrl probe 函数

probe()函数中,主要完成了以下任务:

        分配并初始化rockchip_pinctrl结构体

        获取SoC特定的配置,

        解析设备树,获取寄存器映射,

        处理可选的PMU寄存器映射。

        进行SoC特定的初始化(如果适用)。

        注册 pinctrl设备。

ret = rockchip_pinctrl_register(pdev, info);  
if (ret) return ret;

        注册GPIO设备(通过of_platform_populate)。

rockchip_pinctrl_register()函数完成了以下任务:

        初始化pinctrl_desc结构体

        为每个引脚分配内存并设置编号和名称。

        解析设备树中的pinctrl信息。

        注册 pinctrl设备。

info->pctl_dev = devm_pinctrl_register(&pdev->dev, ctrldesc, info);  
if (IS_ERR(info->pctl_dev)) return PTR_ERR(info->pctl_dev);

devm_pinctrl_register()函数用于注册 pinctrl设备并将其与设备关联:

        分配内存以存储 pinctrl_dev指针,

        注册 pinctrl设备,

pctldev = pinctrl_register(pctldesc, dev, driver_data);  
if (IS_ERR(pctldev)) {  
    devres_free(ptr);  
    return pctldev;  
}

        将 pinctrl_dev指针存储到设备资源列表中。

pinctrl_register()函数用于注册并启用 pinctrl设备:

        初始化 pinctrl控制器,

pctldev = pinctrl_init_controller(pctldesc, dev, driver_data);  
if (IS_ERR(pctldev)) return pctldev;

        启用 pinctrl控制器。

error = pinctrl_enable(pctldev);  
if (error) return ERR_PTR(error);

        通过这一系列步骤,瑞芯微的pinctrl驱动成功注册并启用了pinctrl设备,为后续的引脚控制和配置提供了基础。

第104章 pinctrl 子系统函数操作集

        probe 函数的实际作用就是注册并启用 pinctrl 设备,pinctrl 设备由 pinctrl_desc 结构体所描述,所以在 probe() 函数中会对 pinctrl_desc 结构体中的内容进行填充。

104.1 groups 和 function

        在 Pinctrl 子系统中,关键概念包括引脚组(groups)功能(function)

以 rk3568-pinctrl.dtsi 设备树文件中的 can0 和 can1 为例:

can0 功能:

        can0m0-pins 组:

<0 RK_PB4 2 &pcfg_pull_none>, 
<0 RK_PB3 2 &pcfg_pull_none>

/*控制器编号、引脚编号、引脚模式编号(代表功能)、引脚配置选项*/
//pcfg_pull_none代表未启动上下拉,即浮空

        RK_PB4:CAN0 接收引脚(can0_rxm0)

        RK_PB3:CAN0 发送引脚(can0_txm0)

        can1m1-pins 组:

<4 RK_PC2 3 &pcfg_pull_none>, 
<4 RK_PC3 3 &pcfg_pull_none>

        RK_PC2:CAN1 接收引脚(can1_rxm1)

        RK_PC3:CAN1 发送引脚(can1_txm1)

        can0 和 can1 是两个功能,每个功能包含两个引脚组,分别用于配置 CAN0 和 CAN1 的接收和发送引脚。

104.2 函数操作集结构体

        在Linux内核中,pinctrl_desc 结构体包含三个函数操作集,

        分别用于引脚控制引脚复用引脚配置。瑞芯微在源码中对重要函数进行了实现。

        pinctrl_ops()引脚控制操作函数指针。

        pinmux_ops()引脚复用操作函数指针。

        pinconf_ops()引脚配置操作函数指针。

/*引脚控制操作函数*/
struct pinctrl_ops {    
    // 获取引脚控制器支持的引脚组数量  
    int (*get_groups_count)(struct pinctrl_dev *pctldev);    
      
    // 根据选择器获取引脚组的名称  
    const char *(*get_group_name)(struct pinctrl_dev *pctldev, unsigned selector);    
      
    // 根据选择器获取引脚组中的引脚和数量  
    int (*get_group_pins)(struct pinctrl_dev *pctldev, unsigned selector, const unsigned **pins, unsigned *num_pins);    
      
    // 打印引脚信息用于调试  
    void (*pin_dbg_show)(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned offset);    
      
    // 将设备树节点转换为引脚控制器映射  
    int (*dt_node_to_map)(struct pinctrl_dev *pctldev, struct device_node *np_config, struct pinctrl_map **map, unsigned *num_maps);    
      
    // 释放由设备树节点转换来的引脚控制器映射  
    void (*dt_free_map)(struct pinctrl_dev *pctldev, struct pinctrl_map *map, unsigned num_maps);    
};
/*引脚复用操作函数*/
struct pinmux_ops {    
    // 请求特定的引脚或引脚组进行复用  
    int (*request)(struct pinctrl_dev *pctldev, unsigned offset);    
      
    // 释放之前请求的引脚或引脚组  
    int (*free)(struct pinctrl_dev *pctldev, unsigned offset);    
      
    // 获取引脚控制器支持的功能数量  
    int (*get_functions_count)(struct pinctrl_dev *pctldev);    
      
    // 根据选择器获取功能名称  
    const char *(*get_function_name)(struct pinctrl_dev *pctldev, unsigned selector);    
      
    // 根据选择器获取功能对应的引脚组  
    int (*get_function_groups)(struct pinctrl_dev *pctldev, unsigned selector, const char * const **groups, unsigned *num_groups);    
      
    // 设置引脚复用功能(选择功能和引脚组)  
    int (*set_mux)(struct pinctrl_dev *pctldev, unsigned func_selector, unsigned group_selector);    
      
    // 请求并启用GPIO范围内的特定引脚  
    int (*gpio_request_enable)(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset);    
      
    // 禁用并释放GPIO范围内的特定引脚  
    void (*gpio_disable_free)(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset);    
      
    // 设置GPIO引脚的方向(输入或输出)  
    int (*gpio_set_direction)(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, unsigned offset, bool input);    
      
    // 指示是否严格遵循某些规则(如:是否要求必须先请求再设置等)  
    bool strict;    
};
/*引脚配置操作函数*/
struct pinconf_ops {    
    // 获取单个引脚的配置  
    int (*pin_config_get)(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config);    
      
    // 设置单个引脚的多个配置  
    int (*pin_config_set)(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *configs, unsigned num_configs);    
      
    // 获取引脚组的配置  
    int (*pin_config_group_get)(struct pinctrl_dev *pctldev, unsigned selector, unsigned long *config);    
      
    // 设置引脚组的多个配置  
    int (*pin_config_group_set)(struct pinctrl_dev *pctldev, unsigned selector, unsigned long *configs, unsigned num_configs);    
      
    // 解析并修改引脚配置的调试参数  
    int (*pin_config_dbg_parse_modify)(struct pinctrl_dev *pctldev, const char *arg, unsigned long *config);    
      
    // 打印单个引脚配置的调试信息  
    void (*pin_config_dbg_show)(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned offset);    
      
    // 打印引脚组配置的调试信息  
    void (*pin_config_group_dbg_show)(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned selector);    
      
    // 打印特定配置值的调试信息  
    void (*pin_config_config_dbg_show)(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned long config);    
};

104.3 rockchip_pinctrl 分析

        在 pinctrl 的初始化过程中,rockchip_pinctrl 结构体通过一系列函数调用与 pinctrl_dev 关联。

        rockchip_pinctrl_register() 函数将rockchip_pinctrl 结构体作为参数传入。

        经过函数调用链,rockchip_pinctrl 最终作为私有数据driver_data传入pinctrl_register() 函数。

        在 pinctrl_init_controller() 函数中,driver_data 被赋值给 pctldev->driver_data,从而建立了 pinctrl_dev 和 rockchip_pinctrl 之间的关联。 

接着,解析设备树中的引脚控制器信息:

        rockchip_pinctrl_parse_dt() 函数解析设备树,

        首先计算功能数量(functions)组数量(groups)

        为功能和组分配内存空间。

        遍历每个功能节点,调用 rockchip_pinctrl_parse_functions 函数解析功能信息。

        rockchip_pinctrl_parse_functions() 函数初始化功能结构体,并为每个功能分配组指针数组。

        遍历功能节点的每个子节点(引脚组),

        调用 rockchip_pinctrl_parse_groups() 函数解析组信息,将这些信息存储在相应的结构体中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大象荒野

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值