一. 简介
上一篇文章简单了解了 pinctrl子系统驱动框架,文章地址如下:
本文继上一篇文章pinctrl子系统驱动代码的学习,具体来了解 pinctrl子系统驱动所做的最关键的工作。
二. pinctrl子系统驱动所做的最关键的工作
1. pinctrl子系统驱动思路
上一篇文章的学习中可以知道,设备树中的 compatible 属性值会和 of_device_id 中的所有兼容性字符串比较,查看是否可以使用此驱动。
imx6ul_pinctrl_of_match 结构体数组一共有两个兼容性字符串,分别为“fsl,imx6ul-iomuxc”和“fsl,imx6ull-iomuxc-snvs”,因此 iomuxc 节点与此驱动匹配,所以 pinctrl-imx6ul.c 会完成 I.MX6ULL 的 PIN 配置工作。
当设备和驱动匹配成功以后, platform_driver 的 probe 成员变量所代表的函数就会执行,在代码中设置 probe 成员变量为 imx6ul_pinctrl_probe 函数,因此, imx6ul_pinctrl_probe 这个函数就会执行,可 以认为 imx6ul_pinctrl_probe 函数,就是 I.MX6ULL 这个 SOC 的 PIN 配置入口函数。以此为入口,如下图的函数调用路径:
2. pinctrl子系统驱动所做的最关键的工作
(1) 获取设备树中关于 PIN 的配置信息
imx_pinctrl_parse_groups 函数 负责获取设备树中关于 PIN 的配置信息,也就是我们前面分析的那 6 个 u32 类型的值。处理过程如下所示:
static int imx_pinctrl_parse_groups(struct device_node *np, struct imx_pin_group *grp,
struct imx_pinctrl_soc_info *info, u32 index)
{
int size, pin_size;
const __be32 *list;
int i;
...........
pin_id = (mux_reg != -1) ? mux_reg / 4 : conf_reg / 4;
pin_reg = &info->pin_regs[pin_id];
pin->pin = pin_id;
grp->pin_ids[i] = pin_id;
pin_reg->mux_reg = mux_reg;
pin_reg->conf_reg = conf_reg;
pin->input_reg = be32_to_cpu(*list++);
pin->mux_mode = be32_to_cpu(*list++);
pin->input_val = be32_to_cpu(*list++);
/* SION bit is in mux register */
config = be32_to_cpu(*list++);
if (config & IMX_PAD_SION)
pin->mux_mode |= IOMUXC_CONFIG_SION;
pin->config = config & ~IMX_PAD_SION;
............
}
return 0;
}
第 1~2 行,设备树中的 mux_reg 和 conf_reg 值会保存在 info 参数中, input_reg 、 mux_mode 、 input_val 和 config 值会保存在 grp 参数中。
第 14~ 18 行,获取 mux_reg 、 conf_reg 、 input_reg 、 mux_mode 和 input_val 值。
第 24 行,获取 config 值。
(2) 注册PIN控制器
下来看一下函数 pinctrl_register,此函数用于向 Linux 内核注册一个 PIN 控制器,此函数原型如下:
struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc, struct device *dev,
void *driver_data)
参数 pctldesc 非常重要,因为此参数就是要注册的 PIN 控制器,PIN 控制器用于配置 SOC 的 PIN 复用功能和电气特性。参数 pctldesc 是 pinctrl_desc 结构体类型指针,pinctrl_desc 结构体如下所示:
struct pinctrl_desc {
const char *name;
struct pinctrl_pin_desc const *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
};
第 5~7 行,这三个 “ _ops ” 结构体指针非常重要!!!因为这三个结构体就是 PIN 控制器的 “工具”,这三个结构体里面包含了很多操作函数,通过这些操作函数就可以完成对某一个 PIN 的配置。
pinctrl_desc 结构体需要由用户提供,结构体里面的成员变量也是用户提供的。但 是这个用户并不是我们这些使用芯片的程序员,而是半导体厂商,半导体厂商发布的 Linux 内 核源码中,已经把这些工作做完了。例如,在 imx_pinctrl_probe 函数中可以找到如下所示代码:
int imx_pinctrl_probe(struct platform_device *pdev, struct imx_pinctrl_soc_info *info)
{
struct device_node *dev_np = pdev->dev.of_node;
struct device_node *np;
struct imx_pinctrl *ipctl;
struct resource *res;
struct pinctrl_desc *imx_pinctrl_desc;
......
imx_pinctrl_desc = devm_kzalloc(&pdev->dev, sizeof(*imx_pinctrl_desc), GFP_KERNEL);
if (!imx_pinctrl_desc)
return -ENOMEM;
......
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;
......
ipctl->pctl = pinctrl_register(imx_pinctrl_desc, &pdev->dev, ipctl);
......
}
第 5 行,定义结构体指针变量 imx_pinctrl_desc 。
第 9 行,向指针变量 imx_pinctrl_desc 分配内存。
第 15~21 行,初始化 imx_pinctrl_desc 结构体指针变量,重点是 pctlops 、 pmxops 和 confops 这三个成员变量,分别对应 imx_pctrl_ops 、 imx_pmx_ops 和 imx_pinconf_ops 这三个结构体。
第 23 行,调用函数 pinctrl_register ,向 Linux 内核注册 imx_pinctrl_desc ,注册以后 Linux 内 核就有了对 I.MX6ULL 的 PIN 进行配置的工具。
imx_pctrl_ops 、 imx_pmx_ops 和 imx_pinconf_ops 这三个结构体定义如下:
static const struct pinctrl_ops imx_pctrl_ops = {
.get_groups_count = imx_get_groups_count,
.get_group_name = imx_get_group_name,
.get_group_pins = imx_get_group_pins,
.pin_dbg_show = imx_pin_dbg_show,
.dt_node_to_map = imx_dt_node_to_map,
.dt_free_map = imx_dt_free_map,
};
static const struct pinmux_ops imx_pmx_ops = {
.get_functions_count = imx_pmx_get_funcs_count,
.get_function_name = imx_pmx_get_func_name,
.get_function_groups = imx_pmx_get_groups,
.set_mux = imx_pmx_set,
.gpio_request_enable = imx_pmx_gpio_request_enable,
.gpio_set_direction = imx_pmx_gpio_set_direction,
};
static const struct pinconf_ops imx_pinconf_ops = {
.pin_config_get = imx_pinconf_get,
.pin_config_set = imx_pinconf_set,
.pin_config_dbg_show = imx_pinconf_dbg_show,
.pin_config_group_dbg_show = imx_pinconf_group_dbg_show,
};
这三个结构体下的所有函数就是 I.MX6ULL 的 PIN 配置函数,我们就 不再去分析这些函数了,有兴趣的可以去看一下。