当前位置: 首页 > news >正文

Linux驱动开发进阶(八)- GPIO子系统BSP驱动

文章目录

  • 1、前言
  • 2、pinctrl子系统
  • 3、pinctrl bsp驱动
  • 4、gpio子系统
  • 5、gpio bsp驱动

1、前言

  1. 学习参考书籍以及本文涉及的示例程序:李山文的《Linux驱动开发进阶》
  2. 本文属于个人学习后的总结,不太具备教学功能。

2、pinctrl子系统

在讨论gpio子系统时,一般要带上pinctrl子系统。pinctrl子系统管理着io的功能复用,gpio子系统依赖于pinctrl子系统。所以先讨论pinctrl子系统。

先看设备树,下面是瑞芯微平台的pinctrl控制器的设备树节点:

&pinctrl {compatible = "rockchip,rk3568-pinctrl";xl9535:xl9535 {rockchip,pins = <4 RK_PA0 RK_FUNC_GPIO &pcfg_pull_up>;};
};

下面是全志平台的pinctrl控制器的设备树节点:

&pio {compatible = "allwinner,sun8iw20-pinctrl";csi_mclk0_pins_a: csi_mclk0@0 {pins = "PE3";function = "csi0";drive-strength = <10>;};
}

上面展示了两个不同平台的pinctrl控制器设备树节点。我们不关心他们配置了什么内容,但值得注意的是,他们用于表示IO复用的属性是不一样的。这也说明了这方面是高度自由的,通常都由厂家来定义。

Linux内核使用一个pinctrl_desc结构体来描述pinctrl控制器:

struct pinctrl_desc {const char *name;	// pinctrl的名称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_PINCONFunsigned int num_custom_params;const struct pinconf_generic_params *custom_params;const struct pin_config_item *custom_conf_items;
#endifbool link_consumers; // 用于指示是否将使用该pinctrl控制器的设备链接到该控制器
};

先看引脚描述结构体:

struct pinctrl_pin_desc {unsigned number;	// GPIO引脚的编号const char *name;	// GPIO引脚名称void *drv_data;		// 私有数据
};

再看pinctrl_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);
};

该结构体的函数都要实现。get_groups_count用于获取IO分组,get_group_name用于获取IO分组名称,get_group_pins用于获取IO引脚资源,pin_dbg_show用于打印调试信息,dt_node_to_map用于从设备树获取设备节点然后映射(这个函数挺重要的),dt_free_map用于释放映射。

再看pinmux_ops结构体:

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);int (*gpio_request_enable) (struct pinctrl_dev *pctldev,struct pinctrl_gpio_range *range,unsigned offset);void (*gpio_disable_free) (struct pinctrl_dev *pctldev,struct pinctrl_gpio_range *range,unsigned offset);int (*gpio_set_direction) (struct pinctrl_dev *pctldev,struct pinctrl_gpio_range *range,unsigned offset,bool input);bool strict;
};

request和free函数指针用于请求和释放一个引脚,get_functions_count函数指针用于获取pin控制器中的function的个数,get_function_name函数指针用于获取指定function的名称,get_function_groups函数指针用于获取指定function所占用的引脚group,set_mux函数指针用于将指定的引脚group(group_selector)设置为指定的function。

再看pinconf_ops结构体:

struct pinconf_ops {
#ifdef CONFIG_GENERIC_PINCONFbool is_generic;
#endifint (*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);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);
};

pin_config_get用于获取被选中引脚的配置,pin_config_set用于配置被选中的引脚,pin_config_group_get用于获取被选中的引脚分组配置,pin_config_group_set用于配置被选中的引脚分组,pin_cofig_dbg_show、pin_config_group_dbg_show用于调试信息。

3、pinctrl bsp驱动

pinctrl的3大作用:

作用1(分为两部分):

  1. 描述、获得单个引脚的信息
  2. 描述、获得某组引脚的信息

pinctrl_ops这个操作集合说是获取各组引脚的信息。但这个“组”到底体现了在哪?有些驱动里是把单个引脚归为一组,所以因此产生了名字和实现不匹配现象。

作用2:

用来把某组引脚(group)复用为某个功能(function)

作用3:

用来配置某个引脚(pin)或某组引脚(group)

关于bsp驱动示例程序可以参考韦东山的驱动大全或者李山文的《Linux驱动开发进阶》的pinctrl bsp示例程序。

如下贴出韦东山的示例程序:


#include <linux/module.h>
#include <linux/err.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/mfd/syscon.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_address.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/slab.h>
#include <linux/regmap.h>#include "core.h"static struct pinctrl_dev *g_pinctrl_dev;static const struct pinctrl_pin_desc pins[] = {{0, "pin0", NULL},{1, "pin1", NULL},{2, "pin2", NULL},{3, "pin3", NULL},
};static unsigned long g_configs[4];struct virtual_functions_desc {const char *func_name;const char **groups;int num_groups;
};static const char *func0_grps[] = {"pin0", "pin1", "pin2", "pin3"};
static const char *func1_grps[] = {"pin0", "pin1"};
static const char *func2_grps[] = {"pin2", "pin3"};static struct virtual_functions_desc g_funcs_des[] = {{"gpio", func0_grps, 4},{"i2c",  func1_grps, 2},{"uart", func2_grps, 2},
};static const struct of_device_id virtual_pinctrl_of_match[] = {{ .compatible = "100ask,virtual_pinctrl", },{ },
};static int virtual_get_groups_count(struct pinctrl_dev *pctldev)
{return pctldev->desc->npins;
}static const char *virtual_get_group_name(struct pinctrl_dev *pctldev,unsigned selector)
{return pctldev->desc->pins[selector].name;
}static int virtual_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,const unsigned **pins,unsigned *npins)
{if (selector >= pctldev->desc->npins)return -EINVAL;*pins = &pctldev->desc->pins[selector].number;*npins = 1;return 0;
}static void virtual_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s,unsigned offset)
{seq_printf(s, "%s", dev_name(pctldev->dev));
}/*i2cgrp {functions = "i2c", "i2c";groups = "pin0", "pin1";configs = <0x11223344	0x55667788>;};one pin ==> two pinctrl_map (one for mux, one for config)*/
static int virtual_dt_node_to_map(struct pinctrl_dev *pctldev,struct device_node *np,struct pinctrl_map **map, unsigned *num_maps)
{int i;int num_pins = 0;const char *pin;const char *function;unsigned int config;struct pinctrl_map *new_map;unsigned long *configs;/* 1. 确定pin个数/分配pinctrl_map */while (1){if (of_property_read_string_index(np, "groups", num_pins, &pin) == 0)num_pins++;elsebreak;}new_map = kmalloc(sizeof(struct pinctrl_map) * num_pins * 2, GFP_KERNEL);for (i = 0; i < num_pins; i++){/* 2. get pin/function/config */of_property_read_string_index(np, "groups", i, &pin);of_property_read_string_index(np, "functions", i, &function);of_property_read_u32_index(np, "configs", i, &config);/* 3. 存入pinctrl_map   */configs = kmalloc(sizeof(*configs), GFP_KERNEL);new_map[i*2].type = PIN_MAP_TYPE_MUX_GROUP;new_map[i*2].data.mux.function = function;new_map[i*2].data.mux.group = pin;new_map[i*2+1].type = PIN_MAP_TYPE_CONFIGS_PIN;new_map[i*2+1].data.configs.group_or_pin = pin;new_map[i*2+1].data.configs.configs = configs;configs[0] = config;new_map[i*2+1].data.configs.num_configs = 1;}*map = new_map;*num_maps = num_pins * 2;return 0;
}
static void virtual_dt_free_map(struct pinctrl_dev *pctldev,struct pinctrl_map *map, unsigned num_maps)
{while (num_maps--){if (map->type == PIN_MAP_TYPE_CONFIGS_PIN)kfree(map->data.configs.configs);kfree(map);map++;}
}static const struct pinctrl_ops virtual_pctrl_ops = {.get_groups_count = virtual_get_groups_count,.get_group_name = virtual_get_group_name,.get_group_pins = virtual_get_group_pins,.pin_dbg_show = virtual_pin_dbg_show,.dt_node_to_map = virtual_dt_node_to_map,.dt_free_map = virtual_dt_free_map,};static int virtual_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
{return ARRAY_SIZE(g_funcs_des);
}static const char *virtual_pmx_get_func_name(struct pinctrl_dev *pctldev,unsigned selector)
{return g_funcs_des[selector].func_name;
}static int virtual_pmx_get_groups(struct pinctrl_dev *pctldev, unsigned selector,const char * const **groups,unsigned * const num_groups)
{*groups = g_funcs_des[selector].groups;*num_groups = g_funcs_des[selector].num_groups;return 0;
}static int virtual_pmx_set(struct pinctrl_dev *pctldev, unsigned selector,unsigned group)
{printk("set %s as %s\n", pctldev->desc->pins[group].name, g_funcs_des[selector].func_name);return 0;
}static const struct pinmux_ops virtual_pmx_ops = {.get_functions_count = virtual_pmx_get_funcs_count,.get_function_name = virtual_pmx_get_func_name,.get_function_groups = virtual_pmx_get_groups,.set_mux = virtual_pmx_set,
};static int virtual_pinconf_get(struct pinctrl_dev *pctldev,unsigned pin_id, unsigned long *config)
{*config = g_configs[pin_id];return 0;
}static int virtual_pinconf_set(struct pinctrl_dev *pctldev,unsigned pin_id, unsigned long *configs,unsigned num_configs)
{if (num_configs != 1)return -EINVAL;g_configs[pin_id] = *configs;printk("config %s as 0x%lx\n", pctldev->desc->pins[pin_id].name, *configs);return 0;
}static void virtual_pinconf_dbg_show(struct pinctrl_dev *pctldev,struct seq_file *s, unsigned pin_id)
{seq_printf(s, "0x%lx", g_configs[pin_id]);
}static void virtual_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,struct seq_file *s, unsigned pin_id)
{seq_printf(s, "0x%lx", g_configs[pin_id]);
}static const struct pinconf_ops virtual_pinconf_ops = {.pin_config_get = virtual_pinconf_get,.pin_config_set = virtual_pinconf_set,.pin_config_dbg_show = virtual_pinconf_dbg_show,.pin_config_group_dbg_show = virtual_pinconf_group_dbg_show,
};static int virtual_pinctrl_probe(struct platform_device *pdev)
{struct pinctrl_desc *pictrl;printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);/* a. 分配pinctrl_desc */pictrl = devm_kzalloc(&pdev->dev, sizeof(*pictrl), GFP_KERNEL);/* b. 设置pinctrl_desc */pictrl->name = dev_name(&pdev->dev);pictrl->owner = THIS_MODULE;/* b.1 pins and group */pictrl->pins = pins;pictrl->npins = ARRAY_SIZE(pins);pictrl->pctlops = &virtual_pctrl_ops;/* b.2 pin mux */pictrl->pmxops = &virtual_pmx_ops;/* b.3 pin config */pictrl->confops = &virtual_pinconf_ops;/* c. 注册pinctrl_desc */g_pinctrl_dev = devm_pinctrl_register(&pdev->dev, pictrl, NULL);return 0;
}
static int virtual_pinctrl_remove(struct platform_device *pdev)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}static struct platform_driver virtual_pinctrl_driver = {.probe		= virtual_pinctrl_probe,.remove		= virtual_pinctrl_remove,.driver		= {.name	= "100ask_virtual_pinctrl",.of_match_table = of_match_ptr(virtual_pinctrl_of_match),}
};/* 1. 入口函数 */
static int __init virtual_pinctrl_init(void)
{	printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);/* 1.1 注册一个platform_driver */return platform_driver_register(&virtual_pinctrl_driver);
}/* 2. 出口函数 */
static void __exit virtual_pinctrl_exit(void)
{printk("%s %s %d\n", __FILE__, __FUNCTION__, __LINE__);/* 2.1 反注册platform_driver */platform_driver_unregister(&virtual_pinctrl_driver);
}module_init(virtual_pinctrl_init);
module_exit(virtual_pinctrl_exit);MODULE_LICENSE("GPL");

重点看virtual_dt_node_to_map,该函数主要是将设备树pinctrl节点信息转成pinctrl_map结构体。同时,内核也提供了该功能的函数,叫pinconf_generic_dt_node_to_map_all,如果这样,设备树pinctrl节点属性就得按照一定的格式来写。

4、gpio子系统

pinctrl子系统的主要作用是管理引脚的功能,即复用功能。而gpio子系统则是控制gpio功能的io口的高低电平。gpio子系统依赖于pinctrl子系统的实现,因此,在加载gpio bsp驱动时,也需要加载pinctrl的bsp驱动,这样gpio bsp驱动才能正常工作。

5、gpio bsp驱动

linux内核抽象出了一个结构体,叫gpio_chip,用来控制IO的电平及获取IO的电平:

注册一个gpio bsp驱动调用如下函数即可:

该函数将gpio_chip注册到内核中,这样用户在调用gpiod_xxx接口时就可以正常工作了。

示例程序可以参考李山文的《Linux驱动开发进阶》的gpio bsp示例程序。

相关文章:

  • 【Qt】初识Qt
  • 使用Python+xml+shutil修改目标检测图片和对应xml标注文件
  • Python 导出 PDF(ReportLab )
  • 【算法】椭圆曲线签名(ECDSA)
  • 数据库性能优化(sql优化)_分布式优化思路01_yxy
  • Spring AI 发布了它的 1.0.0 版本的第七个里程碑(M7)
  • jmeter中文使用手册
  • 能源智能管控:开启工厂节能增效
  • MCP(Model Context Protocol)技术白皮书与项目集成指南
  • T101D加固平板电脑:无人机地面站的高效智能控制核心
  • MariaDB MaxScale 的用途与实现细节
  • 如何成为一名嵌入式软件工程师?
  • 【C语言】--- 预处理详解
  • spring-boot-devtools如何使用有哪些功能?
  • 【vscode】vscode链接关联github/gitlab
  • BUUCTF-Web(1-20)
  • 记录鸿蒙应用上架应用未配置图标的前景图和后景图标准要求尺寸1024px*1024px和标准要求尺寸1024px*1024px
  • VSCode Continue 扩展踩坑记录
  • [Java实战经验]链式编程与Builder模式
  • MySQL索引和事务
  • 欧盟公布关税反制清单,瞄准美国飞机、汽车等产品
  • 追光|铁皮房、土操场,这有一座“筑梦”摔跤馆
  • 国家主席习近平同普京总统共见记者
  • 优化网络营商环境,上海严厉打击涉企网络谣言、黑灰产等违法犯罪
  • 4月深圳新房、二手房成交同比均上涨,“5月有望延续积极向好的发展态势”
  • 太空摄影的发展