GPIO子系统架构分析(二)

GPIO子系统架构分析(一)
GPIO子系统架构分析(二)
通过sysf操作gpio

gpio lib

gpio lib(drivers\gpio\gpiolib.c)中提供了一组不依赖于硬件平台的接口函数,理解其中的主要函数非常关键。gpio lib提供的主要函数如下。

(1) gpio的设置与gpio值的获取

void gpiod_set_value(struct gpio_desc *desc, int value);
int gpiod_get_value(const struct gpio_desc *desc);

(2) 设置gpio方向的接口

int gpiod_direction_output(struct gpio_desc *desc, int value);
int gpiod_direction_input(struct gpio_desc *desc);

(3) gpio的申请与释放

int gpiod_request(struct gpio_desc *desc, const char *label);
void gpiod_free(struct gpio_desc *desc);

(4) gpio控制器驱动的注册与注销

int gpiochip_add(struct gpio_chip *chip);
void gpiochip_remove(struct gpio_chip *chip);

gpio控制器驱动

对于用户而言,只需知道要操作的gpio引脚对应的编号,调用gpio lib提供的函数如gpiod_set_value等,即可操作gpio引脚。gpio_chip,表示一个gpio控制器,里面含有各种成员函数,用于操作gpio引脚。gpio lib提供的函数最终会调用到gpio_chip里面的函数来操作gpio引脚。

gpio控制器驱动的主要工作就是,设置好gpio_chip,然后调用gpio lib提供的gpiochip_add函数进行注册。

下面就来分析gpiochip_add函数,定义如下:

static inline int gpiochip_add(struct gpio_chip *chip)
{
	return gpiochip_add_data(chip, NULL);
}


int gpiochip_add_data(struct gpio_chip *chip, void *data)
{
	unsigned long	flags;
	int		status = 0;
	unsigned	i;
	int		base = chip->base;
	struct gpio_device *gdev;

	//申请一个gpio_device 
	gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
	if (!gdev)
		return -ENOMEM;

	//设置gpio_device 
	gdev->dev.bus = &gpio_bus_type;
	gdev->chip = chip;
	chip->gpiodev = gdev;
	
	......

	//根据gpio控制器的gpio个数,分配n个gpio_desc
	gdev->descs = kcalloc(chip->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL);
	if (!gdev->descs) {
		status = -ENOMEM;
		goto err_free_gdev;
	}

	......


	gdev->ngpio = chip->ngpio;
	gdev->data = data;

	spin_lock_irqsave(&gpio_lock, flags);

	/* gpio控制器的起始gpio号base可以由驱动自身设置,gpio号范围为:[base, base + gdev->ngpio)
     这个范围不能与系统中的gpio号有所重复,如果不能确定base的话,将gpio_chip->base设为-1
     由系统来选择一个合适的base,这个合适的base通过遍历gpio_devices链表计算得到
   */
	if (base < 0) {
		base = gpiochip_find_base(chip->ngpio);
		
		......

		chip->base = base;
	}
	gdev->base = base;

	//将gpio_device插入全局链表gpio_devices
	status = gpiodev_add_to_list(gdev);
	......

	spin_unlock_irqrestore(&gpio_lock, flags);

	......

	/* 在支持设备树的内核中,驱动想使用gpio,可以在设备节点描述需要使用的gpio
     如在设备节点里描述属性gpios = <&gpio4 14 GPIO_ACTIVE_LOW>;
     这个属性怎么解析得到对应的gpio_desc呢?由gpio_chip->of_xlate函数进行解析
     如果未提供of_xlate函数,默认设置为of_gpio_simple_xlate
   */
	status = of_gpiochip_add(chip);

	......

	//字符设备相关设置
	status = gpiochip_setup_dev(gdev);
	
	......

	
}

gpiochip_setup_dev:

static int gpiochip_setup_dev(struct gpio_device *gdev)
{
	int status;

	//初始化字符设备,file_operations设置为gpio_fileops
	cdev_init(&gdev->chrdev, &gpio_fileops);
	gdev->chrdev.owner = THIS_MODULE;
	gdev->chrdev.kobj.parent = &gdev->dev.kobj;
	gdev->dev.devt = MKDEV(MAJOR(gpio_devt), gdev->id);

	//注册字符设备
	status = cdev_add(&gdev->chrdev, gdev->dev.devt, 1);
	
	......
	
	status = device_add(&gdev->dev);

	......

}

驱动程序中怎么使用gpio?

前面提到过,驱动程序可以在其设备节点里描述需要使用的gpio,之后中调用相关函数就可以获得对应的gpio_desc了。

现在主流的驱动程序,都是在设备树中,通过设备节点,描述硬件相关信息,在驱动程序中,读取设备节点的信息,注册驱动程序。要想通过设备树来使用gpio资源,其gpio控制器驱动程序要以这种方式来编写。

gpio控制器的设备节点必须有两项属性,gpio-controller和#gpio-cells,如下图:
在这里插入图片描述
gpio-controller属性,表明这是一个gpio控制器。#gpio-cells属性的值为n,表示用n项信息描述一个gpio引脚。
要描述一个gpio引脚,基本的信息是要知道这个gpio引脚接在哪个控制器上,控制器上的几号引脚。
说了那么多,还不如来个例子,如下图的设备节点,描述了两个gpio引脚:
在这里插入图片描述

cs-gpios = <&gpio4 26 GPIO_ACTIVE_LOW>, <&gpio4 24 GPIO_ACTIVE_LOW>;
/* &gpio4,引用了gpio4控制器设备节点,表示gpio接在gpio4控制器上,26一般表示引脚是在
    gpio4这组引脚的第26号引脚,GPIO_ACTIVE_LOW表示引脚低电平有效

    可以判断gpio4控制器设备节点的#gpio-cells = <2>,如果#gpio-cells = <1>
    那么cs-gpios属性要变成:cs-gpios = <&gpio4 26>, <&gpio4 24>; 
*/

//具体参考Documentation/devicetree/bindings/gpio/gpio.txt

下面就来分析在设备节点描述的gpio引脚信息如何解析成gpio号的吧。

驱动程序可以调用of_get_named_gpiod_flags函数获取gpio_desc ,函数定义如下:


struct of_phandle_args {
	struct device_node *np;
	int args_count;
	uint32_t args[MAX_PHANDLE_ARGS];
};

/*参数说明:
	np,设备节点
	list_name,属性名,如cs-gpios = <&gpio4 26 GPIO_ACTIVE_LOW> ...; 则传入"cs-gpios"
	index,获取哪个gpio号,如cs-gpios描述了多个gpio,0表示获取第一个,1表示...
*/
struct gpio_desc *of_get_named_gpiod_flags(struct device_node *np,
		     const char *propname, int index, enum of_gpio_flags *flags)
{
	struct of_phandle_args gpiospec;
	struct gpio_chip *chip;
	struct gpio_desc *desc;
	int ret;

	/* 如,解析<&gpio4 26 GPIO_ACTIVE_LOW>,解析结果就是:
	gpio4控制器设备节点的device_node存入gpiospec.np,
	                      gpiospec.args_count = 2,
	                      gpiospec.args[0] = <26>,
		                  gpiospec.args[1] = <GPIO_ACTIVE_LOW>
  */
	ret = of_parse_phandle_with_args(np, propname, "#gpio-cells", index,
					 &gpiospec);
	......

	/* 遍历全局链表gpio_devices,调用of_gpiochip_match_node_and_xlate函数
     主要是通过gpio4控制器设备节点的device_node找到对应的gpio_chip
	*/
	chip = of_find_gpiochip_by_xlate(&gpiospec);
	......

	//调用gpio_chip->of_xlate解析参数,默认的of_xlate函数为of_gpio_simple_xlate
	desc = of_xlate_and_get_gpiod_flags(chip, &gpiospec, flags);
	......

	//返回gpio_desc 
	return desc;
}

of_xlate_and_get_gpiod_flags:

static struct gpio_desc *of_xlate_and_get_gpiod_flags(struct gpio_chip *chip,
					struct of_phandle_args *gpiospec,
					enum of_gpio_flags *flags)
{
	int ret;

	......

	/* 调用gpio_chip->of_xlate,返回该gpio引脚在gpio控制器的offset
	   如gpio4_26,那么返回26
	 */ 
	ret = chip->of_xlate(chip, gpiospec, flags);
	
	......
	
	//通过offset在gpio控制器内找到对应的gpio_desc
	return gpiochip_get_desc(chip, ret);

}

//默认的解析函数
int of_gpio_simple_xlate(struct gpio_chip *gc,
			 const struct of_phandle_args *gpiospec, u32 *flags)
{
	
	......
	
	if (flags)
		*flags = gpiospec->args[1];
	
	//即第一个参数,如<&gpio4 26 GPIO_ACTIVE_LOW>,返回26
	return gpiospec->args[0];
}

//通过offset在gpio控制器内找到对应的gpio_desc
struct gpio_desc *gpiochip_get_desc(struct gpio_chip *chip,
				    u16 hwnum)
{
	struct gpio_device *gdev = chip->gpiodev;

	if (hwnum >= gdev->ngpio)
		return ERR_PTR(-EINVAL);

	return &gdev->descs[hwnum];
}

在获取到gpio_desc后,并不是马上就可以使用了,使用之前需要申请,调用gpiod_request函数进行申请。

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
很抱歉,作为一个语言模型,我无法生成图片,但我可以为您提供一份文字描述的逻辑结构框图: IIO子系统架构: IIO子系统主要由以下几个模块组成: - IIO核心模块(IIO core) - IIO缓冲区(IIO buffer) - IIO设备(IIO device) - IIO通道(IIO channel) - IIO传感器(IIO sensor) - IIO触发器(IIO trigger) IIO子系统各种模块: - IIO核心模块(IIO core):负责处理IIO子系统的核心逻辑,如设备和通道的注册与卸载、传感器的采样与转换、触发器的触发等。 - IIO缓冲区(IIO buffer):负责管理IIO子系统中的数据缓冲区,包括数据的读取和写入等操作。 - IIO设备(IIO device):表示一个物理设备,其中包含多个IIO通道和传感器。 - IIO通道(IIO channel):表示一个IIO设备中的一个数据通道,通常对应一个物理量。 - IIO传感器(IIO sensor):与IIO通道类似,但是它通常不是直接连接到IIO设备上的,而是通过某种传感器接口连接。 - IIO触发器(IIO trigger):负责触发IIO设备的数据采集和传输。 IIO子系统主要数据结构: - struct iio_dev:表示一个IIO设备的数据结构。 - struct iio_chan_spec:表示一个IIO通道的数据结构,包含通道的名称、类型、数据格式等信息。 - struct iio_buffer:表示一个IIO缓冲区的数据结构,包含缓冲区的大小、写指针、读指针等信息。 IIO子系统功能: - 提供一个统一的接口,方便用户管理和控制IIO设备、通道和传感器。 - 支持多种数据格式和数据采样方式,满足不同应用场景的需求。 - 支持多种传感器接口,包括SPI、I2C、GPIO等。 - 提供丰富的触发器类型,包括软件触发、定时触发、外部触发等。 - 提供Libiio库,方便用户在用户空间中访问IIO子系统
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值