pinctrl 子系统介绍
Pin Controller
Client Device
具体到每个控制器一般都要通过pin脚与外部连接的,这就需要使用SOC的pin脚,那么这个控制器就是pinctrl的client device,具体的client device会通过pinctrl来将pin设置为相应的功能及配置。
数据结构
上图是数据结构的整体结构图,主要分为三部分,下面我将分别介绍每一部分:
pin controller
pinctrl_maps
pinctrl
pinctrl子系统之框架组成
pinctrl子系统之图解相关数据结构
主要包含
- struct dev_pin_info
- struct pinctrl
- struct pinctrl_state
- struct pinctrl_setting
文件:kernel\drivers\pinctrl\core.h
/**
* struct dev_pin_info - pin state container for devices
* @p: pinctrl handle for the containing device
* @default_state: the default state for the handle, if found
*/
struct dev_pin_info {
struct pinctrl *p;
struct pinctrl_state *default_state;
#ifdef CONFIG_PM
struct pinctrl_state *sleep_state;
struct pinctrl_state *idle_state;
#endif
};
/**
* struct pinctrl - per-device pin control state holder
* @node: global list node
* @dev: the device using this pin control handle
* @states: a list of states for this device
* @state: the current state
* @dt_maps: the mapping table chunks dynamically parsed from device tree for
* this device, if any
* @users: reference count
*/
struct pinctrl {
struct list_head node;
struct device *dev;
struct list_head states;
struct pinctrl_state *state;
struct list_head dt_maps;
struct kref users;
};
/**
* struct pinctrl_state - a pinctrl state for a device
* @node: list node for struct pinctrl's @states field
* @name: the name of this state
* @settings: a list of settings for this state
*/
struct pinctrl_state {
struct list_head node;
const char *name;
struct list_head settings;
};
/**
* struct pinctrl_setting - an individual mux or config setting
* @node: list node for struct pinctrl_settings's @settings field
* @type: the type of setting
* @pctldev: pin control device handling to be programmed. Not used for
* PIN_MAP_TYPE_DUMMY_STATE.
* @dev_name: the name of the device using this state
* @data: Data specific to the setting type
*/
struct pinctrl_setting {
struct list_head node;
enum pinctrl_map_type type;
struct pinctrl_dev *pctldev;
const char *dev_name;
union {
struct pinctrl_setting_mux mux;
struct pinctrl_setting_configs configs;
} data;
};
pinctrl子系统之与dev的绑定
struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
struct device *dev, void *driver_data)
/*
struct pinctrl_desc *pctldesc 其中包含了SOC中所有的引脚号和名称,以及对引脚的操作函数pinctrl_ops,pinmux_ops,pinconf_ops,分别是访问控制器下的pin和group的方法、设置复用功能和设置配置引脚的操作方法。
struct device *dev 为平台设备,即pinctrl设备树设备。
void* driver_data 为struct msm_pinctrl结构体。
*/
先申请pinctrl_dev结构体,然后检测pctldesc中对引脚操作函数的健全性检查,将pctldesc中的所有引脚信息注册到pinctrl_dev的radix树中,将申请的pinctrl_dev添加到全局链表pinctrldev_list中,查找全局pinctrl_list中有没有dev设备的pinctrl,没有则进行创建。
当设备和驱动匹配时会调用really_probe函数,其中函数中有一个pinctrl_bind_pins函数,这就是设备和pinctrl子系统联系的函数。
int pinctrl_bind_pins(struct device *dev)
/*
struct device *dev,为和驱动匹配的设备。
*/
在该函数下会先分配一个struct dev_pin_info结构体,这个结构体主要是设备引脚状态的容器。
/**
* struct dev_pin_info - pin state container for devices
* @p: pinctrl handle for the containing device
* @default_state: the default state for the handle, if found
*/
struct dev_pin_info {
struct pinctrl *p;
struct pinctrl_state *default_state;
#ifdef CONFIG_PM
struct pinctrl_state *sleep_state;
struct pinctrl_state *idle_state;
#endif
};
之后会调用create_pinctrl来创建为设备创建pinctrl结构体,这个结构体主要包括设备所需要的引脚复用和配置信息,用dt_maps链表表示,和设备当前的引脚状态信息,用state表示。
static struct pinctrl *create_pinctrl(struct device *dev)
/**
* struct pinctrl - per-device pin control state holder
* @node: global list node
* @dev: the device using this pin control handle
* @states: a list of states for this device
* @state: the current state
* @dt_maps: the mapping table chunks dynamically parsed from device tree for
* this device, if any
* @users: reference count
*/
struct pinctrl {
struct list_head node;
struct device *dev;
struct list_head states;
struct pinctrl_state *state;
struct list_head dt_maps;
struct kref users;
};
在create_pinctrl函数中首先会分配prictrl结构体,之后调用pinctrl_dt_to_map函数来解析设备树的引脚信息。
int pinctrl_dt_to_map(struct pinctrl *p)
/*
struct pinctrl *p 为构建的pinctrl结构体
*/
pinctrl_dt_to_map函数首先会获取设备树设备节点的pinctrl-(0,1,2.....)和pinctrl-names节点,分别为pinctrl phandle的引用和pinctrl配置的名字。通过获取的pinctrl phandle引用来获取device node(设备树解析出来的结构体),然后通过dt_to_map_one_config来解析device node。
static int dt_to_map_one_config(struct pinctrl *p, const char *statename,
struct device_node *np_config)
/*
struct pinctrl *p 为构造的pinctrl结构体
const char *statename 为设备树定义的pinctrl-names属性(例如:spi_defalut,spi_sleep)
struct device_node *np_config 为通过pinctrl phandle引用获取到的device node
*/
在dt_to_map_one_config函数中,首先根据np_config的父节点获取到pinctrl controller的device node,然后通过pinctrl controller的device node来获取到pinctrl_dev结构体,此结构体包含SOC所有的引脚信息,以及对引脚的操作函数等。然后调用pinctrl_dev下的pinctrl_ops操作集的dt_node_to_map函数来解析np_config。
int pinconf_generic_dt_subnode_to_map(struct pinctrl_dev *pctldev,
struct device_node *np, struct pinctrl_map **map,
unsigned *reserved_maps, unsigned *num_maps,
enum pinctrl_map_type type)
/*
struct pinctrl_dev *pctldev pinctrl controller设备
struct device_node *np 引脚配置设备树节点
struct pinctrl_map **map 返回给上层调用的pinctrl_map结构体
unsigned *reserved_maps 无什么大用
unsigned *num_maps 返回给上层调用的pinctrl_map个数
enum pinctrl_map_type type 为默认的map类型PIN_MAP_TYPE_CONFIGS_GROUP
*/
该函数会解析该设备引用的pinctrl节点中的子节点mux和config节点,其中会根据mux节点的pins标签中属性的数量创建相应数量的mux pinctrl map结构体,其中type属性为PIN_MAP_TYPE_MUX_GROUP,group为pins标签中的属性名称,function为function标签的值。
/**
* struct pinctrl_map_mux - mapping table content for MAP_TYPE_MUX_GROUP
* @group: the name of the group whose mux function is to be configured. This
* field may be left NULL, and the first applicable group for the function
* will be used.
* @function: the mux function to select for the group
*/
struct pinctrl_map_mux {
const char *group;
const char *function;
};
其次也会根据config节点的pins标签中属性的数量创建相应数量的config pinctrl map结构体,其中type属性为PIN_MAP_TYPE_CONFIGS_GROUP。group_or_pin的值为pins标签中的属性名称。configs为所有要配置的属性值数组,其中config配置的标签已经定义好了,只需要查找config节点中是否有对应的标签,如果有则将对应的数值和在dt_params数组的位置构建成数据放入到configs数组中。num_configs的值为所有属性值的数量。
static struct pinconf_generic_dt_params dt_params[] = {
{ "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
{ "bias-high-impedance", PIN_CONFIG_BIAS_HIGH_IMPEDANCE, 0 },
{ "bias-bus-hold", PIN_CONFIG_BIAS_BUS_HOLD, 0 },
{ "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 1 },
{ "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 1 },
{ "bias-pull-pin-default", PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 1 },
{ "drive-push-pull", PIN_CONFIG_DRIVE_PUSH_PULL, 0 },
{ "drive-open-drain", PIN_CONFIG_DRIVE_OPEN_DRAIN, 0 },
{ "drive-open-source", PIN_CONFIG_DRIVE_OPEN_SOURCE, 0 },
{ "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 },
{ "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 },
{ "input-disable", PIN_CONFIG_INPUT_ENABLE, 0 },
{ "input-schmitt-enable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 1 },
{ "input-schmitt-disable", PIN_CONFIG_INPUT_SCHMITT_ENABLE, 0 },
{ "input-debounce", PIN_CONFIG_INPUT_DEBOUNCE, 0 },
{ "power-source", PIN_CONFIG_POWER_SOURCE, 0 },
{ "low-power-enable", PIN_CONFIG_LOW_POWER_MODE, 1 },
{ "low-power-disable", PIN_CONFIG_LOW_POWER_MODE, 0 },
{ "output-low", PIN_CONFIG_OUTPUT, 0, },
{ "output-high", PIN_CONFIG_OUTPUT, 1, },
{ "slew-rate", PIN_CONFIG_SLEW_RATE, 0},
};
/**
* struct pinctrl_map_configs - mapping table content for MAP_TYPE_CONFIGS_*
* @group_or_pin: the name of the pin or group whose configuration parameters
* are to be configured.
* @configs: a pointer to an array of config parameters/values to program into
* hardware. Each individual pin controller defines the format and meaning
* of config parameters.
* @num_configs: the number of entries in array @configs
*/
struct pinctrl_map_configs {
const char *group_or_pin;
unsigned long *configs;
unsigned num_configs;
};
接下来会将创建好的pinctrl_map数组挂到设备pinctrl的pinctrl_map链表下,并且挂载到全局pinctrl_map下。
随后回到create_pinctrl函数中,调用add_setting函数来根据构建的pinctrl_map来构建设备相应的pinctrl_state以及该状态的相应pinctrl_settings。
static int add_setting(struct pinctrl *p, struct pinctrl_map const *map)
/*
struct pinctrl *p 构建的pinctrl
struct pinctrl_map const *map 构建的pinctrl_map
*/
在add_setting函数中,首先,先根据map的名称判断p有没有创建相应的state,如果没有则调用create_state函数新建一个pinctrl_state结构体。然后根据map->type类型来调用相应的函数来构造setting,pinmux_map_to_setting用来构造mux的setting,pinconf_map_to_setting用来构建config的setting。
int pinmux_map_to_setting(struct pinctrl_map const *map,
struct pinctrl_setting *setting)
/*
struct pinctrl_map const *map, 查询到与设备同名的map
struct pinctrl_setting *setting 构建的setting
*/
static inline int pinconf_map_to_setting(struct pinctrl_map const *map,
struct pinctrl_setting *setting)
/*
struct pinctrl_map const *map, 查询到与设备同名的map
struct pinctrl_setting *setting 构建的setting
*/
最后会根据map中的数值等信息找到如下定义,其中包含了该引脚的寄存器,和默认配置以及可以配置成的function。构建完setting后,加入到state的settings链表中。最后将构建好的pinctrl结构体添加到全局的pinctrl_list中。
#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9) \
{ \
.name = "gpio" #id, \
.pins = gpio##id##_pins, \
.npins = (unsigned)ARRAY_SIZE(gpio##id##_pins), \
.funcs = (int[]){ \
msm_mux_gpio, /* gpio mode */ \
msm_mux_##f1, \
msm_mux_##f2, \
msm_mux_##f3, \
msm_mux_##f4, \
msm_mux_##f5, \
msm_mux_##f6, \
msm_mux_##f7, \
msm_mux_##f8, \
msm_mux_##f9 \
}, \
.nfuncs = 10, \
.ctl_reg = REG_BASE + REG_SIZE * id, \
.io_reg = REG_BASE + 0x4 + REG_SIZE * id, \
.intr_cfg_reg = REG_BASE + 0x8 + REG_SIZE * id, \
.intr_status_reg = REG_BASE + 0xc + REG_SIZE * id, \
.intr_target_reg = REG_BASE + 0x8 + REG_SIZE * id, \
.mux_bit = 2, \
.pull_bit = 0, \
.drv_bit = 6, \
.oe_bit = 9, \
.in_bit = 0, \
.out_bit = 1, \
.intr_enable_bit = 0, \
.intr_status_bit = 0, \
.intr_target_bit = 5, \
.intr_target_kpss_val = 4, \
.intr_raw_status_bit = 4, \
.intr_polarity_bit = 1, \
.intr_detection_bit = 2, \
.intr_detection_width = 2, \
}
构建好pinctrl结构体后,回到pinctrl_bind_pins函数,之后会调用pinctrl_lookup_state函数找到default、sleep、idle的state,如果没有就算了。之后会通过pinctrl_select_state->pinctrl_commit_state函数来初始化默认default引脚状态。
static int pinctrl_commit_state(struct pinctrl *p, struct pinctrl_state *state)
/*
struct pinctrl *p 构建的pinctrl结构体
struct pinctrl_state *state 要切换的引脚状态
*/
在pinctrl_commit_state函数中首先会判断p->state存在与否,其实存在也就是切换引脚状态(需要disable之前的状态)。我们重点关注不存在的情况,如果不存在则通过pinmux_enable_setting函数来使能default状态所需的引脚并配置引脚的复用。然后通过pinconf_apply_setting函数来配置default状态所需引脚的电器属性。最后将p->state赋值为default状态,也就是设备的当前状态。
int pinmux_enable_setting(struct pinctrl_setting const *setting)
/*
struct pinctrl_setting const *setting setting->type为PIN_MAP_TYPE_MUX_GROUP的setting
*/
int pinconf_apply_setting(struct pinctrl_setting const *setting)
/*
struct pinctrl_setting const *setting setting->type为PIN_MAP_TYPE_CONFIGS_GROUP的setting
*/
在pinmux_enable_setting函数中首先调用pin_request函数来申请pin引脚,pin_request函数中主要是通过引脚号来查询radix树的pin_desc存不存在,如果存在则查看是否有人申请了该引脚(desc->mux_uescount是否大于0),如果没人引用则使mux_uescount++,并且将desc->mux_owner的值赋为本设备。之后调用msm_pinmux_set_mux函数来对寄存器操作,进行引脚复用。
static int msm_pinmux_set_mux(struct pinctrl_dev *pctldev,
unsigned function,
unsigned group)
/*
struct pinctrl_dev *pctldev 相信大家都知道这是什么了
unsigned function 复用的功能号
unsigned group 引脚组号
*/
msm_pinmux_set_mux函数就是根据找到的PINGROUP中的寄存器和function所在的数组下标来进行引脚复用的。
例:
#define PINGROUP(id, f1, f2, f3, f4, f5, f6, f7, f8, f9) \
{ \
.name = "gpio" #id, \
.pins = gpio##id##_pins, \
.npins = (unsigned)ARRAY_SIZE(gpio##id##_pins), \
.funcs = (int[]){ \
msm_mux_gpio, /* gpio mode */ \
msm_mux_##f1, \
msm_mux_##f2, \
msm_mux_##f3, \
msm_mux_##f4, \
msm_mux_##f5, \
msm_mux_##f6, \
msm_mux_##f7, \
msm_mux_##f8, \
msm_mux_##f9 \
}, \
.nfuncs = 10, \
.ctl_reg = REG_BASE + REG_SIZE * id, \
.io_reg = REG_BASE + 0x4 + REG_SIZE * id, \
.intr_cfg_reg = REG_BASE + 0x8 + REG_SIZE * id, \
.intr_status_reg = REG_BASE + 0xc + REG_SIZE * id, \
.intr_target_reg = REG_BASE + 0x8 + REG_SIZE * id, \
.mux_bit = 2, \
.pull_bit = 0, \
.drv_bit = 6, \
.oe_bit = 9, \
.in_bit = 0, \
.out_bit = 1, \
.intr_enable_bit = 0, \
.intr_status_bit = 0, \
.intr_target_bit = 5, \
.intr_target_kpss_val = 4, \
.intr_raw_status_bit = 4, \
.intr_polarity_bit = 1, \
.intr_detection_bit = 2, \
.intr_detection_width = 2, \
}
pinconf_apply_setting函数中主要使执行msm_config_group_set函数来进行引脚电器属性的配置。
static int msm_config_group_set(struct pinctrl_dev *pctldev,
unsigned group,
unsigned long *configs,
unsigned num_configs)
/*
struct pinctrl_dev *pctldev
unsigned group 引脚组号
unsigned long *configs 配置值数组
unsigned long *configs 配置值个数
*/
msm_config_group_set函数中主要负责解析configs数组中的数值,并进行相应的电器属性的配置。
这样该设备所需要的引脚就已经初始化完毕了,当前设备的引脚状态为default。