IMX6ULL的GPIO子系统驱动代码说明

本文详细分析了Linux内核中I.MX6ULL开发板上UART1_RTS_BGPIO驱动的实现过程,涉及GPIO子系统的驱动结构、设备树配置、以及mxc_gpio_probe函数中的关键步骤,展示了如何通过mxc_gpio_driver与GPIO硬件交互及中断处理。
摘要由CSDN通过智能技术生成

一.  简介

前面几篇文章简单了解了 Linux设备树文件中的 GPIO设备节点信息。

本文简单了解一下  Linux内核源码中,有关 GPIO子系统的驱动实现。本文继续以 I.MX6ULL-ALPHA 开发板上的 UART1_RTS_B SD 卡的检测引脚)为例,分析GPIO子系统的驱动实现。

注意:本小节会涉及到 Linux 驱动分层与分离、平台设备驱动等还未讲解的知识,所以本小节教
程可以不用看,不会影响后续的实验。

二.  IMX6ULL的GPIO子系统驱动

通过前面几篇文章学习,I.MX6ULL-ALPHA 开发板上的 SD 卡的检测引脚(即UART1_RTS_B)所使用的GPIO为 GPIO1_IO19。也就是 GPIO1组。

打开 imx6ull.dtsi,找到 GPIO1的设备节点信息:

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-cells = <2>;
		interrupt-controller;
		#interrupt-cells = <2>;
};

gpio1 节点的 compatible 属性描述了兼容性,在 Linux 内核中搜索这两个字符串:

“fsl,imx6ul-gpio” 
“fsl,imx35-gpio” 

查找 GPIO 驱动代码实现。可以在 drivers/gpio/gpio-mxc.c文件中匹配到, gpio-mxc.c 就是 I.MX6ULL GPIO 驱动文件。

gpio-mxc.c 文件中有如下所示 of_device_id 匹配表:

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 */ }
};
156 行的 compatible 值为“ fsl,imx35-gpio ”,和 gpio1 compatible 属性匹配,因此 gpio-mxc.c 就是 I.MX6ULL GPIO 控制器驱动文件。 gpio-mxc.c 所在的目录为 drivers/gpio ,打开这 个目录可以看到很多芯片的 gpio 驱动文件, “ gpiolib ” 开始的文件是 gpio 驱动的核心文件。

我们重点来看一下 gpio-mxc.c 这个文件,在 gpio-mxc.c 文件中,有如下所示内容:
static struct platform_driver mxc_gpio_driver = {
	.driver		= {
		.name	= "gpio-mxc",
		.of_match_table = mxc_gpio_dt_ids,
	},
	.probe		= mxc_gpio_probe,
	.id_table	= mxc_gpio_devtype,
};
可以看出, GPIO 驱动也是个平台设备驱动,因此当设备树中的设备节点与驱动的
of_device_id 匹配以后 probe 函数就会执行,在这里就是 mxc_gpio_probe 函数,这个函数就是
I.MX6ULL GPIO 驱动入口函数。
我们简单来分析一下 mxc_gpio_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;
	int err;

	mxc_gpio_get_hw(pdev);

	port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
	if (!port)
		return -ENOMEM;

	iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	port->base = devm_ioremap_resource(&pdev->dev, iores);
	if (IS_ERR(port->base))
		return PTR_ERR(port->base);

	port->irq_high = platform_get_irq(pdev, 1);
	port->irq = platform_get_irq(pdev, 0);
	if (port->irq < 0)
		return port->irq;

	/* disable the interrupt and clear the status */
	writel(0, port->base + GPIO_IMR);
	writel(~0, port->base + GPIO_ISR);

	if (mxc_gpio_hwtype == IMX21_GPIO) {
		/*
		 * Setup one handler for all GPIO interrupts. Actually setting
		 * the handler is needed only once, but doing it for every port
		 * is more robust and easier.
		 */
		irq_set_chained_handler(port->irq, mx2_gpio_irq_handler);
	} 
    else {
		/* setup one handler for each entry */
		irq_set_chained_handler(port->irq, mx3_gpio_irq_handler);
		irq_set_handler_data(port->irq, port);
		if (port->irq_high > 0) {
			/* setup handler for GPIO 16 to 31 */
			irq_set_chained_handler(port->irq_high,
						mx3_gpio_irq_handler);
			irq_set_handler_data(port->irq_high, port);
		}
	}

	err = bgpio_init(&port->bgc, &pdev->dev, 4, port->base + GPIO_PSR, port->base + GPIO_DR, NULL, port->base + GPIO_GDIR, NULL, 0);	
	if (err)
		goto out_bgio;

	port->bgc.gc.to_irq = mxc_gpio_to_irq;
	port->bgc.gc.base = (pdev->id < 0) ? of_alias_get_id(np, "gpio") * 32 :
					     pdev->id * 32;

	err = gpiochip_add(&port->bgc.gc);
	if (err)
		goto out_bgpio_remove;

	irq_base = irq_alloc_descs(-1, 0, 32, numa_node_id());
	if (irq_base < 0) {
		err = irq_base;
		goto out_gpiochip_remove;
	}

	port->domain = irq_domain_add_legacy(np, 32, irq_base, 0, &irq_domain_simple_ops, NULL);				    
	if (!port->domain) {
		err = -ENODEV;
		goto out_irqdesc_free;
	}

	/* gpio-mxc can be a generic irq chip */
	mxc_gpio_init_gc(port, irq_base);

	list_add_tail(&port->node, &mxc_gpio_ports);

	return 0;

out_irqdesc_free:
	irq_free_descs(irq_base, 32);
out_gpiochip_remove:
	gpiochip_remove(&port->bgc.gc);
out_bgpio_remove:
	bgpio_remove(&port->bgc);
out_bgio:
	dev_info(&pdev->dev, "%s failed with errno %d\n", __func__, err);
	return err;
}

3 行,设备树节点指针。
4 行,定义一个结构体指针 port ,结构体类型为 mxc_gpio_port gpio-mxc.c 的重点工作就是维护 结构体mxc_gpio_port 结构体 mxc_gpio_port 就是对 I.MX6ULL GPIO 的抽象。 mxc_gpio_port 构体定义如下:
struct mxc_gpio_port {
	struct list_head node;
	void __iomem *base;
	int irq;
	int irq_high;
	struct irq_domain *domain;
	struct bgpio_chip bgc;
	u32 both_edges;
};

mxc_gpio_port bgc 成员变量很重要,因为稍后的重点就是初始化 bgc
继续回到 mxc_gpio_probe 函数函数。第 9 行调用 mxc_gpio_get_hw() 函数,获取 gpio 的硬件相关数据,其实就是获取 gpio 的寄存器组。
函数 mxc_gpio_get_hw()实现 如下:
static void mxc_gpio_get_hw(struct platform_device *pdev)
{
    const struct of_device_id *of_id =
    of_match_device(mxc_gpio_dt_ids, &pdev->dev);
    enum mxc_gpio_hwtype hwtype;
......
    if (hwtype == IMX35_GPIO)
        mxc_gpio_hwdata = &imx35_gpio_hwdata;
    else if (hwtype == IMX31_GPIO)
        mxc_gpio_hwdata = &imx31_gpio_hwdata;
    else
        mxc_gpio_hwdata = &imx1_imx21_gpio_hwdata;
    mxc_gpio_hwtype = hwtype;
}

385 行, mxc_gpio_hwdata 是个全局变量,如果硬件类型是 IMX35_GPIO 的话设置
mxc_gpio_hwdat imx35_gpio_hwdata 。对于 I.MX6ULL 而言,硬件类型就是 IMX35_GPIO
imx35_gpio_hwdata 是个结构体变量,描述了 GPIO 寄存器组,内容如下:
static struct mxc_gpio_hwdata imx35_gpio_hwdata = {
	.dr_reg		= 0x00,
	.gdir_reg	= 0x04,
	.psr_reg	= 0x08,
	.icr1_reg	= 0x0c,
	.icr2_reg	= 0x10,
	.imr_reg	= 0x14,
	.isr_reg	= 0x18,
	.edge_sel_reg	= 0x1c,
	.low_level	= 0x00,
	.high_level	= 0x01,
	.rise_edge	= 0x02,
	.fall_edge	= 0x03,
};

大家将 imx35_gpio_hwdata 中的各个成员变量和 《IMX6ULL参考手册》中的 GPIO 寄存器表对比,就会发 现, imx35_gpio_hwdata 结构体就是 GPIO 寄存器组结构。这样我们后面就可以通过
mxc_gpio_hwdata 这个全局变量,来访问 GPIO 的相应寄存器了。

16 行,调用 devm_ioremap_resource() 函数进行内存映射,得到地址 0x0209C000 Linux 内核中的虚拟地址。
20~21 行,通过 platform_get_irq() 函数获取中断号。 20 行获取高 16 GPIO 的中 断号,第 21 行获取低 16 GPIO 中断号。
26 27 行,操作 GPIO1 IMR ISR 这两个寄存器,关闭 GPIO1 所有 IO 中断,并 且清除状态寄存器。
37~46 行,设置对应 GPIO 的中断服务函数,不管是高 16 位还是低 16 位,中断服务 函数都是 mx3_gpio_irq_handler
第 49 行, bgpio_init() 函数第一个参数为 bgc ,是 bgpio_chip 结构体指针。
bgpio_chip 结构体有个 gc 成员变量, gc 是个 gpio_chip 结构体类型的变量。 gpio_chip 结构体是抽象出来的 GPIO 控制器, gpio_chip 结构体如下所示 ( 有缩减 )
struct gpio_chip {
    const char *label;
    struct device *dev;
    struct module *owner;
    struct list_head list;
 
    int (*request)(struct gpio_chip *chip,
    unsigned offset);
    void (*free)(struct gpio_chip *chip,
    unsigned offset);
    int (*get_direction)(struct gpio_chip *chip,
    unsigned offset);
    int (*direction_input)(struct gpio_chip *chip,
    unsigned offset);
    int (*direction_output)(struct gpio_chip *chip,
    unsigned offset, int value);
    int (*get)(struct gpio_chip *chip,
    unsigned offset);
    void (*set)(struct gpio_chip *chip,
    unsigned offset, int value);
......
};

可以看出, gpio_chip 大量的成员都是函数,这些函数就是 GPIO 操作函数。
bgpio_init()函数 主 要 任 务 就 是 初 始 化 bgc->gc
bgpio_init 里 面 有 三 个 setup 函数: bgpio_setup_io bgpio_setup_accessors bgpio_setup_direction 。这三个函数就是初始化 bgc->gc 中的各种有关 GPIO 的操作,比如,输出,输入等等。
mxc_gpio_probe() 函数中,第 49 行的 GPIO_PSR GPIO_DR GPIO_GDIR I.MX6ULL GPIO 寄存器。这些寄存器地址会赋值给 bgc 参数的 reg_dat reg_set reg_clr reg_dir 这些成员变量。至此, bgc 既有了对 GPIO 的操作函数,又有了 I.MX6ULL 有关 GPIO 的寄存器,那么只要得到 bgc 就可以对 I.MX6ULL GPIO 进行操作。
57 行,调用 gpiochip_add ()函数,向 Linux 内核注册 gpio_chip 也就是 port->bgc.gc 。注册完成以后,我们就可以在驱动中使用 gpiolib 提供的各个 API 函数。

  • 11
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值