6.3 虚拟pinctrl驱动

Pinctrl子系统主要有三大作用,分别是引脚枚举与命名(获取单个引脚或引脚组、设备树解析等)、引脚复用功能配置(比如用作GPIO、I2C或其他功能)、引脚电气特性配置(比如上拉、下拉、open drain、驱动强度等)

驱动代码编写

实验程序是一个虚拟的pinctrl控制器,它一共有6个引脚,每个引脚分别有gpio、af0~af15、analog、reserved等复用功能,在程序中将这6个引脚分别作为一个引脚组(即每个组一个引脚),另外程序还用到了/drivers/pinctrl目录下core.h文件,因为此文件未包含在内核编译头文件路径中,这里采用将此头文件复制到虚拟驱动实在目录方案。

设备树编写

设备树包括两个部分,分别是虚拟pinctr控制器l的节点和使用pinctrl的虚拟设备节点

//在顶层设备树的跟节点中加入如下内容
	//虚拟pinctrl的节点
	virtual_pinctrl {
		compatible = "atk,virtual_pinctrl";
	
		//描述设备引脚的节点,节点内容通过dt_node_to_map解析到pinctrl_map中
		pinctrl_i2c: i2cgrp {
			functions = "af1", "af5";
			groups = "pin0", "pin1";
			configs = <0x1122 0x3344>;
		};
	};
	
	//使用pinctrl的虚拟设备
	virtual_i2c {
		compatible = "atk,virtual_i2c";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_i2c>;
	};
//用make ARCH=arm CROSS_COMPILE=arm-none-linux-gnueabihf- dtbs -j8编译设备树
//用新的.dtb文件启动系统

驱动代码编写

编写一个pinctrl驱动主要包括以下步骤

  1. 分配一个pinctrl_desc
  2. 设置pinctrl_desc的名字等参数
  3. 实现对单个引脚的描述,主要实现pinctrl_desc中pins和npins部分内容
  4. 实现引脚组获取、设备树解析的功能,主要实现pinctrl_desc中pctlops部分内容
  5. 实现引脚电气特性配置相关功能,主要实现pinctrl_desc中confops部分内容
  6. 实现引脚复用功能配置相关功能,主要实现pinctrl_desc中pmxops部分内容
  7. 使用pinctrl_desc在系统分配并注册pinctrl_dev
#include <linux/clk.h>
#include <linux/gpio/driver.h>
#include <linux/hwspinlock.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/machine.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/reset.h>
#include <linux/slab.h>

#include "core.h"

/* 引脚支持的复用功能列表 */
static const char * const pin_functions[] = {
   "gpio", "af0", "af1",
   "af2", "af3", "af4",
   "af5", "af6", "af7",
   "af8", "af9", "af10",
   "af11", "af12", "af13",
   "af14", "af15", "analog",
   "reserved",
};

/* 引脚列表 */
static const struct pinctrl_pin_desc pins_desc[] = {
   {0, "pin0", NULL},
   {1, "pin1", NULL},
   {2, "pin2", NULL},
   {3, "pin3", NULL},
   {4, "pin4", NULL},
   {5, "pin5", NULL},
};

/* 引脚组,将每个引脚单独作为一个组 */
const char *pin_groups[] = {pins_desc[0].name, pins_desc[1].name, pins_desc[2].name, pins_desc[3].name, pins_desc[4].name, pins_desc[5].name};

/* 缓存引脚电气特性配置 */
unsigned long pin_configs[ARRAY_SIZE(pins_desc)] = {0};
/* 缓存引脚复用功能配置 */
unsigned long pin_mux[ARRAY_SIZE(pins_desc)] = {0};

/* 获取总共多少组,将一个引脚作为一个组 */
static int virtual_get_groups_count(struct pinctrl_dev *pctldev)
{
   return ARRAY_SIZE(pins_desc);
}

/* 获取每某一组的名称,组名与引脚名相同 */
static const char *virtual_get_group_name(struct pinctrl_dev *pctldev, unsigned group)
{
   if (group >= ARRAY_SIZE(pins_desc))
   	return NULL;

   //每个引脚都作为一个组
   return pins_desc[group].name;
}

/* 获取某一组的所有引脚 */
static int virtual_get_group_pins(struct pinctrl_dev *pctldev, unsigned group, const unsigned **pins, unsigned *npins)
{
   if (group >= ARRAY_SIZE(pins_desc))
   	return -EINVAL;

   //每个引脚都作为一个组
   *pins = &pins_desc[group].number;
   *npins = 1;

   return 0;
}

/* 解析设备树内容为pinctrl_map列表 */
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_groups;
   int num_functions;
   u32 config;
   char *buffer;
   const char *group;
   const char *function;
   unsigned long *configs;
   struct pinctrl_map *pctl_map;

   /* 获取设备树中引脚组数组的长度,即此节点描述了多少个引脚 */
   for(num_groups=0; ; num_groups++)
   {
   	if(of_property_read_string_index(np, "groups", num_groups, &group) < 0)
   		break;
   }
   /* 获取设备树中复用功能组数组的长度 */
   for(num_functions=0; ; num_functions++)
   {
   	if(of_property_read_string_index(np, "functions", num_functions, &function) < 0)
   		break;
   }
   /* 引脚数数组长度和复用功能数组长度必须相等,即每个引脚都对应一个复用功能 */
   if((num_groups != num_functions) || (num_groups == 0))
   {
   	printk("device tree error\r\n");
   	return -EINVAL;
   }

   /* 分配pinctrl_map和以及存储配置参数的内存,每个(组)引脚2个pinctrl_map加4byte配置字(分别用于存储配置复用功能配置和电气特性配置),pinctrl_map位于前面部分空间 */
   buffer = kmalloc((sizeof(struct pinctrl_map) * num_groups * 2) + (num_groups * sizeof(unsigned long)), GFP_KERNEL);
   if(!buffer) {
   	printk("alloc pinctrl_map failed\r\n");
   	return -ENOMEM;
   }

   /* 偏移到pinctrl_map地址 */
   pctl_map = (struct pinctrl_map*)buffer;
   /* 偏移到配置字地址 */
   configs = (unsigned long*)(buffer + sizeof(struct pinctrl_map) * num_groups * 2);

   for(i=0; i<num_groups; i++)
   {
   	//获取引脚组名称
   	of_property_read_string_index(np, "groups", i, &group);
   	//获取引脚组的复用功能
   	of_property_read_string_index(np, "functions", i, &function);
   	//获取引脚组的配置参数
   	if(of_property_read_u32_index(np, "configs", i, &config) == 0)
   		configs[i] = config;
   	else
   		configs[i] = 0x9988;

   	//复用功能配置的pctl_map
   	pctl_map[i*2].type = PIN_MAP_TYPE_MUX_GROUP;
   	pctl_map[i*2].data.mux.function = function;
   	pctl_map[i*2].data.mux.group = group;

   	//电气特性配置的pctl_map
   	pctl_map[i*2+1].type = PIN_MAP_TYPE_CONFIGS_GROUP;
   	pctl_map[i*2+1].data.configs.group_or_pin = group;
   	pctl_map[i*2+1].data.configs.num_configs = 1;
   	pctl_map[i*2+1].data.configs.configs = &configs[i];
   }

   //pctl_map地址和数量
   *map = pctl_map;
   *num_maps = num_groups * 2;

   return 0;
}

/* 释放virtual_dt_node_to_map中分配的资源 */
static void virtual_dt_free_map(struct pinctrl_dev *pctldev, struct pinctrl_map *map, unsigned num_maps)
{
   kfree(map);
}

/* 获取引脚组的引脚和处理设备树操的作函数集合 */
static const struct pinctrl_ops virtual_ops = {
   .get_groups_count = virtual_get_groups_count,
   .get_group_name = virtual_get_group_name,
   .get_group_pins = virtual_get_group_pins,
   .dt_node_to_map = virtual_dt_node_to_map,
   .dt_free_map = virtual_dt_free_map,
};

/* 获取引脚配置 */
static int virtual_pconf_get(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *config)
{
   if(pin >= ARRAY_SIZE(pin_configs))
   	return -EINVAL;

   *config = pin_configs[pin];

   return 0;
}

/* 配置引脚 */
static int virtual_pconf_set(struct pinctrl_dev *pctldev, unsigned pin, unsigned long *configs, unsigned num_configs)
{
   if((num_configs != 1) || (pin >= ARRAY_SIZE(pin_configs)))
   	return -EINVAL;

   pin_configs[pin] = *configs;
   printk("pin config %s as 0x%lx\n", pctldev->desc->pins[pin].name, *configs);

   return 0;
}

/* 获取引脚组配置 */
static int virtual_pconf_group_get(struct pinctrl_dev *pctldev, unsigned group, unsigned long *config)
{
   if(group >= ARRAY_SIZE(pin_configs))
   	return -EINVAL;

   //因为一个引脚对应一个引脚组,所以引脚的配置与引脚组的配置一样
   *config = pin_configs[group];

   return 0;
}

/* 配置引脚组 */
static int virtual_pconf_group_set(struct pinctrl_dev *pctldev, unsigned group, unsigned long *configs, unsigned num_configs)
{
   if((num_configs != 1) || (group >= ARRAY_SIZE(pin_configs)))
   	return -EINVAL;

   pin_configs[group] = *configs;
   printk("group config %s as 0x%lx\n", pctldev->desc->pins[group].name, *configs);

   return 0;
}

static void virtual_pconf_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned pin)
{
   if(pin >= ARRAY_SIZE(pin_configs))
   	return ;

   seq_printf(s, "0x%lx", pin_configs[pin]);
}

static void virtual_pconf_group_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned group)
{
   if(group >= ARRAY_SIZE(pin_configs))
   	return ;

   seq_printf(s, "0x%lx", pin_configs[group]);
}

/* 引脚配置的操作函数集合 */
static const struct pinconf_ops virtual_pconf_ops = {
   .pin_config_get = virtual_pconf_get,
   .pin_config_set = virtual_pconf_set,
   .pin_config_group_get = virtual_pconf_group_get,
   .pin_config_group_set = virtual_pconf_group_set,
   .pin_config_dbg_show = virtual_pconf_dbg_show,
   .pin_config_group_dbg_show = virtual_pconf_group_dbg_show,
};

/* 获取共计多少种复用功能 */
static int virtual_pmx_get_funcs_cnt(struct pinctrl_dev *pctldev)
{
   return ARRAY_SIZE(pin_functions);
}

/* 获取某种复用功能的名称 */
static const char *virtual_pmx_get_func_name(struct pinctrl_dev *pctldev, unsigned function)
{
   if(function >= ARRAY_SIZE(pin_functions))
   	return NULL;

   return pin_functions[function];
}

/* 获取支持某复用功能的引脚组 */
static int virtual_pmx_get_func_groups(struct pinctrl_dev *pctldev, unsigned function, const char * const **groups, unsigned * const num_groups)
{
   if(function >= ARRAY_SIZE(pin_functions))
   	return -EINVAL;

   //所有引脚都支持相同的复用功能
   *groups = pin_groups;
   *num_groups = ARRAY_SIZE(pin_groups);

   return 0;
}

/* 设置引脚组的复用功能 */
static int virtual_pmx_set_mux(struct pinctrl_dev *pctldev, unsigned function, unsigned group)
{
   if((group >= ARRAY_SIZE(pin_mux)) || (function >= ARRAY_SIZE(pin_functions)))
   	return -EINVAL;

   pin_mux[group] = function;
   printk("group mux %s as 0x%x\n", pctldev->desc->pins[group].name, function);

   return 0;
}

/* 引脚复用功能选择和GPIO控制相关的操作函数集合 */
static const struct pinmux_ops virtual_pmx_ops = {
   .get_functions_count = virtual_pmx_get_funcs_cnt,
   .get_function_name = virtual_pmx_get_func_name,
   .get_function_groups = virtual_pmx_get_func_groups,
   .set_mux = virtual_pmx_set_mux,
   .strict = true,
};

//设备和驱动匹配成功执行
static int virtual_probe(struct platform_device *pdev)
{
   struct pinctrl_desc *pctl_desc;
   struct pinctrl_dev *pctl_dev;

   printk("%s\n", __FUNCTION__);

   /* a. 分配pinctrl_desc */
   pctl_desc = devm_kzalloc(&pdev->dev, sizeof(struct pinctrl_desc), GFP_KERNEL);
   if(!pctl_desc)
   {
   	printk("alloc pctl_desc failed\r\n");
   	return -ENOMEM;
   }

   /* b. 设置pinctrl_desc */
   /* b.1 设置名称和所属模块 */
   pctl_desc->name = dev_name(&pdev->dev);
   pctl_desc->owner = THIS_MODULE;
   /* b.2 描述单个引脚 */
   pctl_desc->pins = pins_desc;
   pctl_desc->npins = ARRAY_SIZE(pins_desc);
   /* b.3 绑定用于获取引脚组的引脚和处理设备树操的作函数集合 */
   pctl_desc->pctlops = &virtual_ops;
   /* b.5 绑定引脚配置的操作函数集合 */
   pctl_desc->confops = &virtual_pconf_ops;
   /* b.4 绑定引脚复用功能选择和GPIO控制相关的操作函数集合 */
   pctl_desc->pmxops = &virtual_pmx_ops;
   /* c. 注册pinctrl_desc,devm表示模块卸载时自动释放 */
   pctl_dev = devm_pinctrl_register(&pdev->dev, pctl_desc, NULL);
   if (IS_ERR(pctl_dev))
   {
   	printk("Failed pinctrl register\n");
   	return PTR_ERR(pctl_dev);
   }

   return 0;
}

//设备或驱动卸载时执行
static int virtual_remove(struct platform_device *pdev)
{
   printk("%s\n", __FUNCTION__);
   return 0;
}

/* 匹配列表,用于设备树和平台驱动匹配 */
static const struct of_device_id virtual_of_match[] = {
   {.compatible = "atk,virtual_pinctrl"},
   { /* Sentinel */ }
};
/* 平台驱动 */
static struct platform_driver virtual_drv = {
   .driver = {
   	.name = "virtual_pinctrl",
   	.owner = THIS_MODULE,
   	.pm = NULL,
   	.of_match_table = virtual_of_match,
   },
   .probe = virtual_probe,
   .remove = virtual_remove,
};
static int __init virtual_drv_init(void)
{
   int result;

   printk("%s\r\n", __FUNCTION__);

   //注册平台驱动
   result = platform_driver_register(&virtual_drv);
   if(result != 0)
   	printk("add virtual driver failed\r\n");
   return result;
}

static void __exit virtual_drv_exit(void)
{
   printk("%s\r\n", __FUNCTION__);

   //注销平台驱动
   platform_driver_unregister(&virtual_drv);
}

module_init(virtual_drv_init);
module_exit(virtual_drv_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("CSDN");
MODULE_DESCRIPTION("virtual_drv");

驱动测试程序

驱动测试程序是一个platform driver它没有实现如何具体的功能,仅仅是在设备树节点中引用了虚拟pinctrl的引脚描述节点

#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>
static int virtual_client_probe(struct platform_device *pdev)
{
	printk("%s\n", __FUNCTION__);
	return 0;
}
static int virtual_client_remove(struct platform_device *pdev)
{
	printk("%s\n", __FUNCTION__);
	return 0;
}

static const struct of_device_id virtual_client_of_match[] = {
	{ .compatible = "atk,virtual_i2c", },
	{ },
};

static struct platform_driver virtual_client_driver = {
	.probe		= virtual_client_probe,
	.remove		= virtual_client_remove,
	.driver		= {
		.name	= "atk,virtual_i2c",
		.of_match_table = of_match_ptr(virtual_client_of_match),
	}
};


/* 1. 入口函数 */
static int __init virtual_client_init(void)
{
	printk("%s\n", __FUNCTION__);
	/* 1.1 注册一个platform_driver */
	return platform_driver_register(&virtual_client_driver);
}


/* 2. 出口函数 */
static void __exit virtual_client_exit(void)
{
	printk("%s\n", __FUNCTION__);
	/* 2.1 反注册platform_driver */
	platform_driver_unregister(&virtual_client_driver);
}

module_init(virtual_client_init);
module_exit(virtual_client_exit);

MODULE_LICENSE("GPL");

上机测试

  1. 修改设备树,然后用新的设备树启动目标板
  2. 这里下载代码。然后进行编译,并拷贝到目标板root目录
  3. 执行命令insmod virtual_pinctrl.ko加载虚拟pinctrl控制器驱动
    在这里插入图片描述
  4. 执行命令insmod virtual_client.ko加载使用虚拟pinctrl控制器的platform driver,可以看到在执行probe函数之前引脚电气特性配置函数和复用功能配置函数被调用
    5.
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值