pinctrl 子系统

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。

  • 17
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

伴君者

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

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

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

打赏作者

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

抵扣说明:

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

余额充值