【分析笔记】全志方案通过命令行操作 GPIO 口(带源码分析)

前言说明

在项目开发初期,很经常会需要临时操作某个GPIO来验证某些功能,可以通过编写一个简单的驱动程序来操作,但更方便的是可以通过命令行直接操作 GPIO ,这样不需要经过编写代码、编译驱动、推入文件、加载驱动那么繁琐的步骤。

以下为全志平台命令行操作 GPIO 的方法

启用功能

  • 挂载 debugfs 
root@sun8i:/# mount -t debugfs debug /proc/sys/debug
mount -t debugfs debug /proc/sys/debug
  • 进入全志的引脚控制目录
root@sun8i:/# cd /proc/sys/debug/sunxi_pinctrl
cd /proc/sys/debug/sunxi_pinctrl

root@sun8i:/proc/sys/debug/sunxi_pinctrl# ls -l
ls -l
-rw-rw-r--    1 root     root             0 Jan  1  1970 data
-rw-rw-r--    1 root     root             0 Jan  1  1970 dlevel
-rw-rw-r--    1 root     root             0 Jan  1  1970 function
-rw-rw-r--    1 root     root             0 Jan  1  1970 platform
-rw-rw-r--    1 root     root             0 Jan  1  1970 pull
-rw-rw-r--    1 root     root             0 Jan  1  1970 sunxi_pin
-rw-rw-r--    1 root     root             0 Jan  1  1970 sunxi_pin_configure
  • 节点介绍
data                    // 引脚的电平状态
dlevel                  // 引脚的驱动等级(结合芯片手册)
function                // 引脚的功能配置(结合芯片手册, 输入\输出\功能复用)
platform                // 当前平台
pull                    // 上下拉功能配置
sunxi_pin               // 指定引脚
sunxi_pin_configure     // 引脚所有的配置信息

GPIO 输入测试

  • 设置指定引脚(注意, PB2 不能写成 PB02, 本文结尾会通过代码分析来说明原因)
root@sun8i:/proc/sys/debug/sunxi_pinctrl# echo PB2 > sunxi_pin
echo PB2 > sunxi_pin
  • 查看引脚配置信息
root@sun8i:/proc/sys/debug/sunxi_pinctrl# cat sunxi_pin_configure
cat sunxi_pin_configure
printf register value...
pin[PB2] function:  6;                 register addr: 0xf1c20824
pin[PB2] data:  0(default value : 0);  register addr: 0xf1c20834
pin[PB2] dleve: 1(default value : 1);  register addr: 0xf1c20838
pin[PB2] pull:  0(default value : 0);  register addr: 0xf1c20840

pin[PB2] trigger:  4;                  register addr: 0xf1c20a20
trigger mode in sunxi platform:
0 : Positive edge   1: Negative edge
2 : High level      3: Low level
4 : Double level    5: other
pin[PB2] mask :     1(0:disable 1:enable);        register addr: 0xf1c20a30
pin[PB2] pending :  0(0:No Pending 1:Pending);    register addr: 0xf1c20a34
  • 测试手动拉高(除了 cat sunxi_pin_configure,也可以 cat data 查看,注意 data 的值)
root@sun8i:/proc/sys/debug/sunxi_pinctrl# cat sunxi_pin_configure
cat sunxi_pin_configure
printf register value...
pin[PB2] function:  6;                 register addr: 0xf1c20824
pin[PB2] data:  1(default value : 0);  register addr: 0xf1c20834
pin[PB2] dleve: 1(default value : 1);  register addr: 0xf1c20838
pin[PB2] pull:  0(default value : 0);  register addr: 0xf1c20840

pin[PB2] trigger:  4;                  register addr: 0xf1c20a20
trigger mode in sunxi platform:
0 : Positive edge   1: Negative edge
2 : High level      3: Low level
4 : Double level    5: other
pin[PB2] mask :     1(0:disable 1:enable);        register addr: 0xf1c20a30
pin[PB2] pending :  0(0:No Pending 1:Pending);    register addr: 0xf1c20a34

PB02 引脚我们用来作为检测触摸按键使用,当有触摸的时候,GPIO 会被拉高,如果没有触摸,GPIO 会被拉低,因此第一次查看 GPIO 配置信息的时候,data 为 0。

该引脚设置中断触发模式为 IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,因此 pin[PB2] trigger 为 4,即 Double leve (双边沿触发模式)。

这里的 function 为 6,是因为我将这个 GPIO 设置为外部中断,根据 datasheet 描述,外部中断的功能号是 6。

GPIO 输出测试

  • 设置指定引脚
root@sun8i:/proc/sys/debug/sunxi_pinctrl# echo PE24 > sunxi_pin
echo PE24 > sunxi_pin
  • 查看引脚配置(注意 data 的值)
root@sun8i:/proc/sys/debug/sunxi_pinctrl# cat sunxi_pin_configure
cat sunxi_pin_configure
printf register value...
pin[PE24] function:  1;                 register addr: 0xf1c2089c
pin[PE24] data:  1(default value : 0);  register addr: 0xf1c208a0
pin[PE24] dleve: 1(default value : 1);  register addr: 0xf1c208a8
pin[PE24] pull:  0(default value : 0);  register addr: 0xf1c208b0
  • 设置输出低电平(此时可以拿万用表或者示波器测量该引脚的实际状态)
root@sun8i:/proc/sys/debug/sunxi_pinctrl# echo PE24 0 > data
echo PE24 0 > data
  • 查看引脚配置信息(注意 data 的值)
root@sun8i:/proc/sys/debug/sunxi_pinctrl# cat sunxi_pin_configure
cat sunxi_pin_configure
printf register value...
pin[PE24] function:  1;                 register addr: 0xf1c2089c
pin[PE24] data:  0(default value : 0);  register addr: 0xf1c208a0
pin[PE24] dleve: 1(default value : 1);  register addr: 0xf1c208a8
pin[PE24] pull:  0(default value : 0);  register addr: 0xf1c208b0
  • 设置输出高电平(此时可以拿万用表或者示波器测量该引脚的实际状态)
root@sun8i:/proc/sys/debug/sunxi_pinctrl# echo PE24 1 > data
echo PE24 1 > data
  •  查看引脚配置信息(注意 data 的值)
root@sun8i:/proc/sys/debug/sunxi_pinctrl# cat sunxi_pin_configure
cat sunxi_pin_configure
printf register value...
pin[PE24] function:  1;                 register addr: 0xf1c2089c
pin[PE24] data:  1(default value : 0);  register addr: 0xf1c208a0
pin[PE24] dleve: 1(default value : 1);  register addr: 0xf1c208a8
pin[PE24] pull:  0(default value : 0);  register addr: 0xf1c208b0

内核配置

学会用还不行,还需要知道如何启用该功能,虽然全志大部分平台都会默认启用这个功能,万一遇到没有启用的呢?通过以 sunxi_pin_configure 关键词搜索代码, 以上调试功能是在该驱动中实现 lichee\linux-3.4\drivers\pinctrl\pinctrl-sunxi.c,并且依赖 CONFIG_DEBUG_FS 配置。

static int sunxi_pinctrl_probe(struct platform_device *pdev)
{
	......
	
#ifdef CONFIG_DEBUG_FS
	sunxi_pinctrl_debugfs();
#endif

	return 0;
	......
}

static struct dentry *debugfs_root;
static void sunxi_pinctrl_debugfs(void)
{
	debugfs_root = debugfs_create_dir("sunxi_pinctrl", NULL);
	if (IS_ERR(debugfs_root) || !debugfs_root) {
		pr_debug("failed to create debugfs directory\n");
		debugfs_root = NULL;
		return;
	}
	debugfs_create_file("sunxi_pin_configure",(S_IRUGO | S_IWUSR | S_IWGRP),
			    debugfs_root, NULL, &sunxi_pin_configure_ops);
	debugfs_create_file("sunxi_pin", (S_IRUGO | S_IWUSR | S_IWGRP),
			    debugfs_root, NULL, &sunxi_pin_ops);
	debugfs_create_file("function", (S_IRUGO | S_IWUSR | S_IWGRP),
			    debugfs_root, NULL, &sunxi_pin_function_ops);
	debugfs_create_file("data", (S_IRUGO | S_IWUSR | S_IWGRP),
			    debugfs_root, NULL, &sunxi_pin_data_ops);
	debugfs_create_file("dlevel", (S_IRUGO | S_IWUSR | S_IWGRP),
			    debugfs_root, NULL, &sunxi_pin_dlevel_ops);
	debugfs_create_file("pull", (S_IRUGO | S_IWUSR | S_IWGRP),
			    debugfs_root, NULL, &sunxi_pin_pull_ops);
	debugfs_create_file("platform", (S_IRUGO | S_IWUSR | S_IWGRP),
				debugfs_root, NULL, &sunxi_platform_ops);

}

通过 Makefile 和 Kconfig 来确认 make menuconfig 的菜单选项和菜单位置 

lichee\linux-3.4\drivers\pinctrl\Makefile: 
obj-$(CONFIG_PINCTRL_SUNXI)	+= pinctrl-sunxi.o

lichee\linux-3.4\drivers\pinctrl\Kconfig:
config PINCTRL_SUNXI
	bool "SUNXI pin controller driver"
	select PINMUX
	select GENERIC_PINCONF

因此要使用此功能需要进行三个步骤:

  • 启用驱动程序 pinctrl-sunxi
make kernel_menuconfig 

Device Drivers -> 
    Pin controllers -> 
        [*] SUNXI pin controller driver
  • 启用 debugfs
make kernel_menuconfig

Kernel hacking -> 
    [*] Debug Filesystem
  • 挂载 debugfs 
mount -t debugfs debug /proc/sys/debug

源码浅析

有时候我们需要操作的 GPIO 是已经被驱动程序申请占用,那么我们通过这种方式是否会有冲突呢?答案是,不会。

知其然,也要知其所以然。以下代码以操作 GPIO 电平的设备节点入手:/proc/sys/debug/sunxi_pinctrl/data

这里略过 debugfs 的知识点介绍,如果不知道为什么要从这里入手,可以先去了解一下 debugfs 。

注意代码里面的注释信息,该注释信息就是分析记录。

// 从操作 GPIO 的 data 入手
static void sunxi_pinctrl_debugfs(void)
{
	...
	debugfs_create_file("data", (S_IRUGO | S_IWUSR | S_IWGRP),
			    debugfs_root, NULL, &sunxi_pin_data_ops);
	...

}

// 找到关联的 ops
static const struct file_operations sunxi_pin_data_ops = {
	.open		= sunxi_pin_data_open,
	.write		= sunxi_pin_data_write,
	.read		= seq_read,
	.llseek		= seq_lseek,
	.release	= single_release,
	.owner		= THIS_MODULE,
};

// 分析实际写操作
static int sunxi_pin_data_write(struct file *file,
	const char __user *user_buf, size_t count, loff_t *ppos)
{
	int						err;
	unsigned int			data;
	long unsigned int 		config;

        // 从命令行解析得到引脚名称以及要设置的电平状态
	err = sscanf(user_buf, "%s %u", sunxi_dbg_pinname,&data);
	if(err != 2 )
		return err;
	if (data <= 1)
		sunxi_dbg_data = data;
	else{
		pr_debug("Input Parameters data error!\n");
		return -EINVAL;
	}
	config = SUNXI_PINCFG_PACK(SUNXI_PINCFG_TYPE_DAT,data);

        // 实际操作,注意 SUNXI_PINCTRL ,并将引脚名字传入
        // 如果是 echo PH02 1 > data, 那么引脚名字就是 "PH02" 
	pin_config_set(SUNXI_PINCTRL,sunxi_dbg_pinname,config);
	
	return count;

}

// 传入给 pin_config_set() 第一个参数 dev_name 
#define SUNXI_PINCTRL 	"sunxi-pinctrl"

// 引脚配置设置 dev_name:sunxi-pinctrl  name: PH02
int pin_config_set(const char *dev_name, const char *name,
		   unsigned long config)
{
	struct pinctrl_dev *pctldev;
	int pin, ret;

	mutex_lock(&pinctrl_mutex);

        // 这里关键,通过名字 pinctrl 设备驱动
	pctldev = get_pinctrl_dev_from_devname(dev_name);
	if (!pctldev) {
		ret = -EINVAL;
		goto unlock;
	}

        // 通过 pinctrl 找名为 name 的引脚
	pin = pin_get_from_name(pctldev, name);
	if (pin < 0) {
		ret = pin;
		goto unlock;
	}

        // 修改指定引脚配置
	ret = pin_config_set_for_pin(pctldev, pin, config);

unlock:
	mutex_unlock(&pinctrl_mutex);
	return ret;
}
EXPORT_SYMBOL(pin_config_set);

// 通过名字找引脚的实现,其实质就是和预先定于好的名字对比
int pin_get_from_name(struct pinctrl_dev *pctldev, const char *name)
{
	unsigned i, pin;

	/* The pin number can be retrived from the pin controller descriptor */
	for (i = 0; i < pctldev->desc->npins; i++) {
		struct pin_desc *desc;

		pin = pctldev->desc->pins[i].number;
		desc = pin_desc_get(pctldev, pin);
		/* Pin space may be sparse */
		if (desc == NULL)
			continue;
		if (desc->name && !strcmp(name, desc->name))
			return pin;
	}

	return -EINVAL;
}

// 修改指定引脚配置
static int pin_config_set_for_pin(struct pinctrl_dev *pctldev, unsigned pin,
			   unsigned long config)
{
	const struct pinconf_ops *ops = pctldev->desc->confops;
	int ret;

	if (!ops || !ops->pin_config_set) {
		dev_err(pctldev->dev, "cannot configure pin, missing "
			"config function in driver\n");
		return -EINVAL;
	}

        // 调用 pin_config_set 回调接口进行真正的操作
	ret = ops->pin_config_set(pctldev, pin, config);
	if (ret) {
		dev_err(pctldev->dev,
			"unable to set pin configuration on pin %d\n", pin);
		return ret;
	}

	return 0;
}

以上分析最关键的有两个地方,通过名字找到具体的 pinctrl 设备驱动,然后调用其回调来进行真正的GPIO配置操作:

get_pinctrl_dev_from_devname(dev_name);  和  ops->pin_config_set(pctldev, pin, config);

// pin_config_set 原型
struct pinconf_ops {
    ......
	int (*pin_config_set) (struct pinctrl_dev *pctldev,
			       unsigned pin,
			       unsigned long config);
	......
};

// 以名字寻找 pinctrl 设备驱动
struct pinctrl_dev *get_pinctrl_dev_from_devname(const char *devname)
{
	struct pinctrl_dev *pctldev = NULL;
	bool found = false;

	if (!devname)
		return NULL;

        // 遍历 pinctrldev_list 链表
        // 从上面分析的 sunxi_pin_data_write() 传入的参数可知名字为 "sunxi-pinctrl" 
	list_for_each_entry(pctldev, &pinctrldev_list, node) {
		if (!strcmp(dev_name(pctldev->dev), devname)) {
			/* Matched on device name */
			found = true;
			break;
		}
	}

	return found ? pctldev : NULL;
}

要找到 pin_config_set(pctldev, pin, config); 真正实现,首先需要找到名为 "sunxi-pinctrl" 的 pinctrl 驱动,而要找到这个驱动,首先先找到操作 pinctrldev_list,增加节点的地方,即 pinctrl_register();

// 通过搜索 pinctrldev_list 来找增加链表的地方,找到这里。
struct pinctrl_dev *pinctrl_register(struct pinctrl_desc *pctldesc,
				    struct device *dev, void *driver_data)
{
	......

	mutex_lock(&pinctrl_mutex);

        // 找到加入 pinctrldev_list 的地方
	list_add_tail(&pctldev->node, &pinctrldev_list);

	pctldev->p = pinctrl_get_locked(pctldev->dev);
	if (!IS_ERR(pctldev->p)) {
		struct pinctrl_state *s =
			pinctrl_lookup_state_locked(pctldev->p,
						    PINCTRL_STATE_DEFAULT);
		if (!IS_ERR(s))
			pinctrl_select_state_locked(pctldev->p, s);
	}

	mutex_unlock(&pinctrl_mutex);
	......
}
EXPORT_SYMBOL_GPL(pinctrl_register);

回过头来检查 lichee\linux-3.4\drivers\pinctrl\pinctrl-sunxi.c 有没有调用  pinctrl_register();找到如下:


static int sunxi_pinctrl_probe(struct platform_device *pdev)
{
    ......
#if defined(CONFIG_OF)
	pctl->membase = of_iomap(node, 0);
	if (!pctl->membase)
		return -ENOMEM;
        // 如果采用 dts ,那么就会从 dts 获取引脚的描述信息
	device = of_match_device(sunxi_pinctrl_match, &pdev->dev);
	if (!device)
		return -ENODEV;

	pctl->desc = (struct sunxi_pinctrl_desc *)device->data;
#else
        // 一般是这里在 sun[?]iw[?].c 提供,如 R58 就是 sun8iw6.c
	pctl->desc = (struct sunxi_pinctrl_desc *)(&sunxi_pinctrl_data);
#endif /* CONFIG_OF */

	ret = sunxi_pinctrl_build_state(pdev);
	if (ret) {
		dev_err(&pdev->dev, "dt probe failed: %d\n", ret);
		return ret;
	}

	pins = devm_kzalloc(&pdev->dev,
			    pctl->desc->npins * sizeof(*pins),
			    GFP_KERNEL);
	if (!pins)
		return -ENOMEM;

	for (i = 0; i < pctl->desc->npins; i++)
		pins[i] = pctl->desc->pins[i].pin;
	
        // 在这里配置, 这里的名字是关键,用的是平台设备的名字,其实就是 "sunxi-pinctrl" 
	sunxi_pctrl_desc.name = dev_name(&pdev->dev);
	sunxi_pctrl_desc.owner = THIS_MODULE;
	sunxi_pctrl_desc.pins = pins;
	sunxi_pctrl_desc.npins = pctl->desc->npins;
	pctl->dev = &pdev->dev;

        // 注册进去
	pctl->pctl_dev = pinctrl_register(&sunxi_pctrl_desc,
					  &pdev->dev, pctl);
	if (!pctl->pctl_dev) {
		dev_err(&pdev->dev, "couldn't register pinctrl driver\n");
		return -EINVAL;
	}
	......
}

从上述所知,pinctrl_register(); 所用的名字是平台设备驱动的名字,因此查看平台设备驱动的结构即可确认,其中 sunxi_pctrl_desc 这个结构的内容就包含了我们一直苦苦寻找的 pin_config_set(pctldev, pin, config); 真正实现所在。

static struct platform_driver sunxi_pinctrl_driver = {
	.probe = sunxi_pinctrl_probe,
	.driver = {
		.name  = SUNXI_PINCTRL,    // 平台设备驱动匹配的名字
		.owner = THIS_MODULE,
#if defined(CONFIG_OF)
		.of_match_table = sunxi_pinctrl_match,
#endif
	},
};

static struct platform_device sunxi_pinctrl_device = {
	.name = SUNXI_PINCTRL,     // 平台设备驱动匹配的名字   
	.id = PLATFORM_DEVID_NONE, /* this is only one device for pinctrl driver */
};

static int __init sunxi_pinctrl_init(void)
{
	int ret;

        // 注册平台设备驱动
	ret = platform_device_register(&sunxi_pinctrl_device);
	if (IS_ERR_VALUE(ret)) {
		pr_debug("register sunxi platform device failed\n");
		return -EINVAL;
	}

	ret = platform_driver_register(&sunxi_pinctrl_driver);
	if (IS_ERR_VALUE(ret)) {
		pr_debug("register sunxi platform device failed\n");
		return -EINVAL;
	}
	return 0;
}
postcore_initcall(sunxi_pinctrl_init);

// 这个宏定义就是 "sunxi-pinctrl"
#define SUNXI_PINCTRL 	"sunxi-pinctrl"

查看 sunxi_pctrl_desc 看看实际是怎么实现的。


static struct pinctrl_desc sunxi_pctrl_desc = {
	.confops	= &sunxi_pconf_ops,
	......
};

static struct pinconf_ops sunxi_pconf_ops = {
	.pin_config_get		= sunxi_pinconf_get,
	.pin_config_set		= sunxi_pinconf_set,
	.pin_config_group_get	= sunxi_pinconf_group_get,
	.pin_config_group_set	= sunxi_pinconf_group_set,
};


// 真正的实现所在
static int sunxi_pinconf_set(struct pinctrl_dev *pctldev,
			     unsigned pin,
			     unsigned long config)
{
	struct sunxi_pinctrl  *pctl = pinctrl_dev_get_drvdata(pctldev);
	struct sunxi_pin_bank *bank = sunxi_pin_to_bank(pctl, pin);
	unsigned int  pin_bias;
	void __iomem		*reg;
	u32                  val;
	u32                  mask;
	u16                  dlevel;
	u16                  data;
	u16                  func;
	u16                  pull;

	pin_reset_bias(&pin_bias, pin);
	if (IS_ERR_OR_NULL(bank)) {
		pr_debug("invalid pin number [%d] to set pinconf\n", pin);
		return -EINVAL;
	}
	switch (SUNXI_PINCFG_UNPACK_TYPE(config)) {
	case SUNXI_PINCFG_TYPE_DRV:
		dlevel = SUNXI_PINCFG_UNPACK_VALUE(config);
		val = pinctrl_readl_reg(bank->membase + sunxi_dlevel_reg(pin_bias));
	    mask = DLEVEL_PINS_MASK << sunxi_dlevel_offset(pin_bias);
		val=(val & ~mask) | (dlevel << sunxi_dlevel_offset(pin_bias));
		reg=bank->membase + sunxi_dlevel_reg(pin_bias);
		pinctrl_write_reg(val,reg);
		pr_debug("sunxi pconf set pin [%s] drive strength to [LEVEL%d]\n", 
		         pin_get_name(pctl->pctl_dev, pin), dlevel);
		break;
	case SUNXI_PINCFG_TYPE_PUD:
		pull = SUNXI_PINCFG_UNPACK_VALUE(config);
		val = pinctrl_readl_reg(bank->membase + sunxi_pull_reg(pin_bias));
		mask = PULL_PINS_MASK << sunxi_pull_offset(pin_bias);
		val=(val & ~mask) | (pull << sunxi_pull_offset(pin_bias));
		reg=bank->membase + sunxi_pull_reg(pin_bias);
		pinctrl_write_reg(val,reg);
		pr_debug("sunxi pconf set pin [%s] pull to [%d]\n", 
		        pin_get_name(pctl->pctl_dev, pin), pull);
		break;
	case SUNXI_PINCFG_TYPE_DAT:
		data = SUNXI_PINCFG_UNPACK_VALUE(config);
		val = pinctrl_readl_reg(bank->membase + sunxi_data_reg(pin_bias));
		mask = DATA_PINS_MASK << sunxi_data_offset(pin_bias);
		val=(val & ~mask) | (data << sunxi_data_offset(pin_bias));
		reg=bank->membase + sunxi_data_reg(pin_bias);
		pinctrl_write_reg(val,reg);
		pr_debug("sunxi pconf set pin [%s] data to [%d]\n", 
		        pin_get_name(pctl->pctl_dev, pin), data);
		break;
	case SUNXI_PINCFG_TYPE_FUNC:
		func = SUNXI_PINCFG_UNPACK_VALUE(config);
		val = pinctrl_readl_reg(bank->membase + sunxi_mux_reg(pin_bias));
	    mask = MUX_PINS_MASK << sunxi_mux_offset(pin_bias);
		val=(val & ~mask) | (func << sunxi_mux_offset(pin_bias));
		reg=bank->membase + sunxi_mux_reg(pin_bias);
		pinctrl_write_reg(val,reg);
		pr_debug("sunxi pconf set pin [%s] func to [%d]\n", 
		         pin_get_name(pctl->pctl_dev, pin), func);
		break;
	default:
		pr_debug("invalid sunxi pconf type for set\n");
		return -EINVAL;
	}
	return 0;
}

从上面可以看到,配置 GPIO 都是通过 pinctrl_write_reg(val,reg); 写寄存器的方式实现:

static inline void pinctrl_write_reg(u32 value,void __iomem *reg)
{
	if(is_arisc_pin(reg)) {
		sunxi_smc_writel(value, reg);
	} else {
		writel(value, reg);
	}
    ......
}

综上分析思路,之所有使用这种方式操作 GPIO 不会对已有的驱动造成影响,是因为该方式是直接操作 SOC 的寄存器方式实现,直接绕过来 Linux 驱动的 GPIO 操作接口。

其实如果有学过 pinctrl 驱动,很容易就能定位到最终的 pinctrl_write_reg() 实现方式,没接触过的话,也是可以通过上述思路找到。虽然过程有点繁琐,但是这种分析思路,可以应对很多自己没有接触的驱动框架。

那为什么设定 GPIO 的时候,如 PB2 不能写成 PB02 呢?这是因为跟 GPIO 名字匹配有关,操作哪个 GPIO 是通过传入的名字确定的,注意上述的 pin_get_from_name() 和 sunxi_pinctrl_probe() 的实现,很容易找到下面的代码

struct sunxi_pinctrl_desc sunxi_pinctrl_data = {
	.pins   = sun8i_w8_pins,
	.npins  = ARRAY_SIZE(sun8i_w8_pins),
	.banks  = sun8i_w8_banks,
	.nbanks = ARRAY_SIZE(sun8i_w8_banks),
};


static struct sunxi_desc_pin sun8i_w8_pins[]={
    ......
	SUNXI_PIN(SUNXI_PINCTRL_PIN_PB2,
		SUNXI_FUNCTION(0x0, "gpio_in"),
		SUNXI_FUNCTION(0x1, "gpio_out"),
		SUNXI_FUNCTION(0x2, "uart2"),		/* RTS */
		SUNXI_FUNCTION(0x6, "eint"),		/* EINT2 */
		SUNXI_FUNCTION(0x7, "io_disable")),
	......

};

// 可以看到,这里定义的名字是 PB2 而不是 PB02, 如果传入的是 PB02 就会匹配失败
#define SUNXI_PINCTRL_PIN_PB0	PINCTRL_PIN(SUNXI_PB_BASE + 0,  "PB0")
#define SUNXI_PINCTRL_PIN_PB1	PINCTRL_PIN(SUNXI_PB_BASE + 1,  "PB1")
#define SUNXI_PINCTRL_PIN_PB2	PINCTRL_PIN(SUNXI_PB_BASE + 2,  "PB2")
#define SUNXI_PINCTRL_PIN_PB3	PINCTRL_PIN(SUNXI_PB_BASE + 3,  "PB3")
#define SUNXI_PINCTRL_PIN_PB4	PINCTRL_PIN(SUNXI_PB_BASE + 4,  "PB4")
#define SUNXI_PINCTRL_PIN_PB5	PINCTRL_PIN(SUNXI_PB_BASE + 5,  "PB5")
#define SUNXI_PINCTRL_PIN_PB6	PINCTRL_PIN(SUNXI_PB_BASE + 6,  "PB6")

总结说明

该命令行方式通过 debugfs 暴露操作接口,通过 pinctrl 提供具体的 GPIO 操作接口,最终通过读写寄存器的方式实现。

 

你可能感兴趣的:(分析笔记)