{ aliases { can0 = &flexcan1; can1 = &flexcan2; ethernet0 = &fec1; ethernet1 = &fec2; gpio0 = &gpio1; gpio1 = &gpio2; gpio2 = &gpio3; gpio3 = &gpio4; gpio4 = &gpio5; //... } gpio1: gpio@0209c000 { compatible = 'fsl,imx6ul-gpio', 'fsl,imx35-gpio'; reg = <0x0209c000 0x4000>; interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>, <GIC_SPI 67 IRQ_TYPE_LEVEL_HIGH>; //两个必须的节点 gpio-controller; //表明这是一个gpio controller #gpio-cells = <2>; //表明多少个cell来描述一个引脚 interrupt-controller; #interrupt-cells = <2>; }; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 当解析设备节点中的GPIO信息时,需要用到上面的属性: 比如下面的led-gpios,在#gpio-cells = <2>的情况下,它表示的引脚数量时1. myled { compatible = '100ask,leddrv'; led-gpios = <&gpio1 10 GPIO_ACTIVE_LOW>; }; 1 2 3 4 二、驱动程序 驱动源码在driver/gpio/gpio-mxc.c 2.1 分配gpio_chip 支持的设备列表: static const struct of_device_id mxc_gpio_dt_ids[] = { { .compatible = 'fsl,imx1-gpio', .data = &mxc_gpio_devtype[IMX1_GPIO], }, { .compatible = 'fsl,imx21-gpio', .data = &mxc_gpio_devtype[IMX21_GPIO], }, { .compatible = 'fsl,imx31-gpio', .data = &mxc_gpio_devtype[IMX31_GPIO], }, { .compatible = 'fsl,imx35-gpio', .data = &mxc_gpio_devtype[IMX35_GPIO], }, { /* sentinel */ } }; 1 2 3 4 5 6 7 匹配到设备树后,probe函数就被调用: static int mxc_gpio_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct mxc_gpio_port *port; struct resource *iores; int irq_base = 0; int err; mxc_gpio_get_hw(pdev); port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL); //分配了一个mxc_gpio_port结构体 //.... iores = platform_get_resource(pdev, IORESOURCE_MEM, 0); //从设备节点中获得寄存器的地址 port->base = devm_ioremap_resource(&pdev->dev, iores); //把物理地址重映射 //.... port->irq_high = platform_get_irq(pdev, 1); //获取irq的高位地址 port->irq = platform_get_irq(pdev, 0); //获得irq的地位地址 //.... port->clk = devm_clk_get(&pdev->dev, NULL); //获取gpio的时钟 //.... err = clk_prepare_enable(port->clk); //是能时钟 //.... err = bgpio_init(&port->gc, &pdev->dev, 4, port->base + GPIO_PSR, //pad status寄存器,得到引脚当前电平 port->base + GPIO_DR, NULL, //数据寄存器 port->base + GPIO_GDIR, NULL, //方向寄存器 BGPIOF_READ_OUTPUT_REG_SET); //... port->gc.request = mxc_gpio_request; port->gc.free = mxc_gpio_free; port->gc.parent = &pdev->dev; port->gc.to_irq = mxc_gpio_to_irq; port->gc.base = (pdev->id < 0) ? of_alias_get_id(np, 'gpio') * 32 : pdev->id * 32; //.... err = devm_gpiochip_add_data(&pdev->dev, &port->gc, port); //注册 } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 mxc_gpio_port结构体 struct mxc_gpio_port { struct list_head node; struct clk *clk; void __iomem *base; int irq; int irq_high; struct irq_domain *domain; struct gpio_chip gc; //gpio_chip结构体 u32 both_edges; int saved_reg[6]; bool gpio_ranges; }; 1 2 3 4 5 6 7 8 9 10 11 12 2.2 设置gpio_chip int bgpio_init(struct gpio_chip *gc, struct device *dev, unsigned long sz, void __iomem *dat, void __iomem *set, void __iomem *clr, void __iomem *dirout, void __iomem *dirin, unsigned long flags) { //... ret = bgpio_setup_io(gc, dat, set, clr, flags); //设置函数写入gpio_chip中的接口 //.... ret = bgpio_setup_accessors(dev, gc, flags & BGPIOF_BIG_ENDIAN, flags & BGPIOF_BIG_ENDIAN_BYTE_ORDER); //设置函数 //.... ret = bgpio_setup_direction(gc, dirout, dirin, flags); //设置函数 //.... } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 2.3 注册gpio_chip int devm_gpiochip_add_data(struct device *dev, struct gpio_chip *chip, void *data) { ptr = devres_alloc(devm_gpio_chip_release, sizeof(*ptr), GFP_KERNEL); //... ret = gpiochip_add_data(chip, data); //... *ptr = chip; devres_add(dev, ptr); //... } int gpiochip_add_data(struct gpio_chip *chip, void *data) { //... gdev = kzalloc(sizeof(*gdev), GFP_KERNEL); //分配一个gpio_device,用来表示一个gpio device //...设置gdev gdev->dev.bus = &gpio_bus_type; gdev->chip = chip; //传入的参数chip分配给gpio_device的gpio_chip chip->gpiodev = gdev; if (chip->parent) { gdev->dev.parent = chip->parent; gdev->dev.of_node = chip->parent->of_node; } //....依然时一些设置函数 dev_set_name(&gdev->dev, 'gpiochip%d', gdev->id); device_initialize(&gdev->dev); dev_set_drvdata(&gdev->dev, gdev); //.... gdev->descs = kcalloc(chip->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL); //分配一个gpio_desc数组 //.... gdev->ngpio = chip->ngpio; gdev->data = data; //.... status = gpiodev_add_to_list(gdev); //把分配好的gpio_device加进系统列表 //.... }怎么读内容:这篇文章主要介绍了Linux内核GPIO驱动的实现原理,包括设备树中GPIO节点的解析和驱动程序中的gpio_chip的分配、设置和注册等内容。其中,驱动程序中的bgpio_init函数用于设置gpio_chip,devm_gpiochip_add_data函数用于注册gpio_chip。

Linux 内核 GPIO 驱动实现原理详解

原文地址: https://www.cveoy.top/t/topic/n2EN 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录