专栏文章目录传送门:返回专栏目录
目录
Linux内核的Pinctrl子系统是一个非常见的框架系统,该子系统的目的是为了提供对硬件引脚(Pin)进行配置和控制的通用接口。通过 pinctrl 子系统,驱动程序可以请求和配置特定硬件引脚的功能和属性,从而实现对硬件引脚的灵活控制。
1. Pinctrl 子系统的作用
pinctrl子系统在Linux内核中的作用和目标是为了实现设备引脚的统一管理和配置,提供硬件抽象和解耦,支持平台适配性,允许动态配置设备引脚的功能和特性,以及促进驱动程序的共享,从而提高Linux内核在不同硬件平台上的可移植性和灵活性。
-
硬件抽象和解耦:pinctrl将硬件引脚配置与驱动程序分离,简化驱动开发,让开发人员专注于设备功能。
-
平台适配性:pinctrl支持不同硬件平台的设备引脚配置,提高内核的可移植性。
-
设备树集成:通过设备树描述硬件引脚配置,实现硬件信息的抽象和动态加载。
-
灵活性和可配置性:允许在运行时动态配置设备引脚的功能和特性,适应不同应用场景。
-
驱动程序共享:多个设备可共享同一设备引脚配置,减少重复代码,简化系统维护。
pinctrl简化了驱动开发,提高内核可移植性,允许灵活配置设备引脚,并促进代重用。通俗来讲,在如今各个厂商SOC的pin脚设计使用可能会有一些不一样,但是采用了pinctrl子系统就可以统一进行管理,对于开发人员来说只需要去操作pinctrl就可以。
2. Pinctrl实现的功能
-
管理系统中所有可以控制的pin,初始化过程,枚举所有的pin, 配置;
-
管理这些pin的复用功能,pin有的可以配置为gpio, 也可以配置特定功能I2C,SPI,uart等 ;
-
配置这些pin, 比如配置引脚的上拉,下拉,配置引脚的驱动强度;
3. 软件框架
从软件结构图可以看出,蓝色部分就是内核虚拟出的pin control,这个是和硬件无关的模块,抽象出所有pin controller的硬件特性。为底层的Soc提供底层的通信接口能力,在用户层,提供的都是各个Driver,而不需要去关注底层是如何实现。
4. 驱动相关分析
这里以i.MX8MQ 为例子
./arch/arm64/boot/dts/freescale/imx8mq.dtsi
./arch/arm64/boot/dts/freescale/imx8mq-evk.dts
./drivers/pinctrl/core.c
./drivers/pinctrl/devicetree.c
./drivers/pinctrl/pinmux.c
./drivers/pinctrl/pinconf.c
// 不同厂商代码
./drivers/pinctrl/freescale/pinctrl-imx8mq.c
./drivers/pinctrl/freescale/pinctrl-imx.c
上面展示了设备树,Linux 内部的pinctrl,另外是不同Soc实现的代码;
pincontroller虽然是没有硬件的支持,是一个软件的概念,所以对于这个我们需要进行去构造;构造pinctrl dev 设备需要提供结构体pinctrl_desc 再进行注册;
先从DTS设备树查看
//vim ./arch/arm64/boot/dts/freescale/imx8mq-evk.dts
iomuxc: pinctrl@30330000 {
compatible = "fsl,imx8mq-iomuxc";
reg = <0x30330000 0x10000>;
};
根据compatible 中,查找到驱动:
./drivers/pinctrl/freescale/pinctrl-imx8mq.c
struct pinctrl_desc *imx_pinctrl_desc;
//...
imx_pinctrl_desc->name = dev_name(&pdev->dev);
imx_pinctrl_desc->pins = info->pins;
imx_pinctrl_desc->npins = info->npins;
imx_pinctrl_desc->pctlops = &imx_pctrl_ops;
imx_pinctrl_desc->pmxops = &imx_pmx_ops;
imx_pinctrl_desc->confops = &imx_pinconf_ops;
imx_pinctrl_desc->owner = THIS_MODULE;
/* for generic pinconf */
imx_pinctrl_desc->custom_params = info->custom_params;
imx_pinctrl_desc->num_custom_params = info->num_custom_params;
从代码明显看到probe 函数功能,另外一个重点是注册pinctrl设备,probe中将根据设备树信息,进行配置,对结构体pinctrl_desc一些接口进行设置。
struct pinctrl_desc *imx_pinctrl_desc;
//...
imx_pinctrl_desc->name = dev_name(&pdev->dev);
imx_pinctrl_desc->pins = info->pins;
imx_pinctrl_desc->npins = info->npins;
imx_pinctrl_desc->pctlops = &imx_pctrl_ops;
imx_pinctrl_desc->pmxops = &imx_pmx_ops;
imx_pinctrl_desc->confops = &imx_pinconf_ops;
imx_pinctrl_desc->owner = THIS_MODULE;
/* for generic pinconf */
imx_pinctrl_desc->custom_params = info->custom_params;
imx_pinctrl_desc->num_custom_params = info->num_custom_params;
4.1 pinctrl_desc结构体
回头看看pinctrl_desc结构体:
./include/linux/pinctrl/pinctrl.h
./include/linux/pinctrl/pinctrl.h
/**
* struct pinctrl_desc - pin controller descriptor, register this to pin
* control subsystem
* @name: name for the pin controller
* @pins: an array of pin descriptors describing all the pins handled by
* this pin controller
* @npins: number of descriptors in the array, usually just ARRAY_SIZE()
* of the pins field above
* @pctlops: pin control operation vtable, to support global concepts like
* grouping of pins, this is optional.
* @pmxops: pinmux operations vtable, if you support pinmuxing in your driver
* @confops: pin config operations vtable, if you support pin configuration in
* your driver
* @owner: module providing the pin controller, used for refcounting
* @num_custom_params: Number of driver-specific custom parameters to be parsed
* from the hardware description
* @custom_params: List of driver_specific custom parameters to be parsed from
* the hardware description
* @custom_conf_items: Information how to print @params in debugfs, must be
* the same size as the @custom_params, i.e. @num_custom_params
* @link_consumers: If true create a device link between pinctrl and its
* consumers (i.e. the devices requesting pin control states). This is
* sometimes necessary to ascertain the right suspend/resume order for
* example.
*/
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;
#ifdef CONFIG_GENERIC_PINCONF
unsigned int num_custom_params;
const struct pinconf_generic_params *custom_params;
const struct pin_config_item *custom_conf_items;
#endif
bool link_consumers;
};
这三个都是需要硬件厂商去实现的,不同的Soc 厂商会对这些进行实现,如果是做原厂的驱动开发,需要去深入了解,对于做普通驱动,我们知道如何使用他们就可以。
const struct pinctrl_ops *pctlops; //对管脚组的操作集
const struct pinmux_ops *pmxops;//对管脚复用的操作集
const struct pinconf_ops *confops;//对管脚电气的操作集
对于pinctrol 的注册,也是在i.MX8MQ probe部分,在对pinctrl_desc已经定义好后就开始注册,
static int imx8mq_pinctrl_probe(struct platform_device *pdev)
--int imx_pinctrl_probe(struct platform_device *pdev,
const struct imx_pinctrl_soc_info *info)
--int devm_pinctrl_register_and_init(struct device *dev,
struct pinctrl_desc *pctldesc,
void *driver_data,
struct pinctrl_dev **pctldev)
4.2 引脚获取与描述
对于一个引脚来说,一般都需要去操作它,那么就要用到刚刚那个结构体pinctrl_pin_desc首先进行描述一个引脚,
pinctrl_ops 这个结构体对管脚进行操作
static const struct pinctrl_ops imx_pctrl_ops = {
.get_groups_count = pinctrl_generic_get_group_count,
.get_group_name = pinctrl_generic_get_group_name,
.get_group_pins = pinctrl_generic_get_group_pins,
.pin_dbg_show = imx_pin_dbg_show,
}
获取某一组引脚:get_groups_count, get_group_pins
处理设备树中Pin Controller的某个节点dt_node_to_map 这个函数的作用就是叫设备树转换成pinctrl map,在驱动中可认识;
5. 实例
i.MX8MQ 为例
-
Pin Controller
-
Clinet Device
从上面例子来看,clinet 的设备节点转换成Platform_device, 如图中的SD卡,其中每个结构体都有一个dev_pin_info结构体,用来保存设备的pinctrl信息