pinctrl/gpio子系统(2)-gpio子系统介绍及驱动源码简单分析

1.gpio子系统api

这里的api都是基于gpio的编号去进行操作
1)gpio_request,用于申请一个GPIO管脚
int gpio_request(unsigned gpio, const char *label)
gpio:要申请的gpio标号
label:gpio名字

2)gpio_free,用于释放gpio
void gpio_free(unsigned gpio)

3)gpio_direction_input,设置gpio为输入
int gpio_direction_input(unsigned gpio)

4) gpio_direction_output ,设置gpio为输出
int gpio_direction_output(unsigned gpio, int value)

5) gpio_get_value ,获取gpio值
#define gpio_get_value __gpio_get_value int __gpio_get_value(unsigned gpio)

6) gpio_set_value ,设置gpio值
#define gpio_set_value __gpio_set_value void __gpio_set_value(unsigned gpio, int value)

2.gpio相关of函数

1) of_gpio_named_count ,获取设备树某属性定义了几个gpio信息
int of_gpio_named_count(struct device_node *np, const char *propname)
np:设备节点
propname:要统计的gpio属性

2) of_gpio_count ,获取gpios属性的gpio数量
int of_gpio_count(struct device_node *np)
np:设备节点

**3) of_get_named_gpio , 获取gpio编号,**将设备树中类似 <&gpio5 7 GPIO_ACTIVE_LOW> 的属性信息转换为对应的 GPIO 编号,用于gpio子系统api操作
int of_get_named_gpio(struct device_node *np, const char *propname, int index)
np:设备节点
propname:要获取的gpio信息属性名
index:gpio索引

3.gpio子系统驱动分析

3.1设备树信息分析

先以 SD卡为入口,分析gpio子系统的设备树相关信息。

1)市面上常见的SD卡引脚图如下:
image.png

2)SD卡连接在usdhc1接口上,而usdhc1的设备树节点信息如下:

&usdhc1 {
   
    pinctrl-names = "default", "state_100mhz", "state_200mhz";
    pinctrl-0 = <&pinctrl_usdhc1>;
    pinctrl-1 = <&pinctrl_usdhc1_100mhz>;
    pinctrl-2 = <&pinctrl_usdhc1_200mhz>;
    cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;
    keep-power-in-suspend;
    enable-sdio-wakeup;
    vmmc-supply = <&reg_sd1_vmmc>;
    no-1-8-v;
    status = "okay";
};

pinctrl-names:定义了三种状态,分别是"default", “state_100mhz”, “state_200mhz”。
“default” =》 pinctrl-0 =》pinctrl_usdhc1
“state_100mhz” =》 pinctrl-1 =》pinctrl_usdhc1_100mhz
“state_200mhz” =》 pinctrl-2 =》pinctrl_usdhc1_200mhz

其中pinctrl_usdhc1对应的引脚配置信息如下

pinctrl_usdhc1: usdhc1grp {
   
    fsl,pins = <
        MX6UL_PAD_SD1_CMD__USDHC1_CMD     0x17059
        MX6UL_PAD_SD1_CLK__USDHC1_CLK     0x10071
        MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x17059
        MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x17059
        MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x17059
        MX6UL_PAD_SD1_DATA3__USDHC1_DATA3 0x17059
    >;
};

可以看出是将SD卡的几个引脚分别进行了复用,其中没有关于CD引脚的配置,CD引脚用于检测SD卡是否插入。
而cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>; 设置CD脚,为GPIO1组的19号引脚,GPIO_ACTIVE_LOW代表低电平有效,既然如此,那么代表CD引脚使用的是GPIO1_IO19,查看设备树文件,其中可以找到如下:

pinctrl_hog_1: hoggrp-1 {
   
    fsl,pins = <
        MX6UL_PAD_UART1_RTS_B__GPIO1_IO19	0x17059 /* SD1 CD */
        MX6UL_PAD_GPIO1_IO05__USDHC1_VSELECT	0x17059 /* SD1 VSELECT */
        MX6UL_PAD_GPIO1_IO00__ANATOP_OTG1_ID    0x13058 /* USB_OTG1_ID */
    >;
};

arch/arm/boot/dts/imx6ull.dtsi中,可以看到

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的地址为0x0209c000,在寄存器手册中可以看到各寄存器地址。
image.png
这里基本就看懂一个usb驱动的设备树信息啦~接下来可以继续分析驱动函数

3.2驱动程序分析

以设备树中的gpio1节点为入口,分析gpio子系统驱动代码。

其中arch/arm/boot/dts/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>;
};

使用compatible属性去匹配OF表,全局搜索可以看到fsl,imx35-gpio对应的of信息在drivers/gpio/gpio-mxc.c下,内容为:

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 */ }
};

当compatible属性匹配的时候,将调用 .probe 函数,也就是mxc_gpio_probe函数

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,
};

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;
}

mxc_gpio_probe函数中有几个比较重要的函数:
1)**mxc_gpio_get_hw **:获取gpio寄存器组,拿到的值其实就是gpio寄存器组的各个值

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 (of_id)
		pdev->id_entry = of_id->data;
	hwtype = pdev->id_entry->driver_data;

	if (mxc_gpio_hwtype) {
   
		/*
		 * The driver works with a reasonable presupposition,
		 * that is all gpio ports must be the same type when
		 * running on one soc.
		 */
		BUG_ON(mxc_gpio_hwtype != hwtype);
		return;
	}

	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;
}

.........................
.........................
static struct mxc_gpio_hwdata imx1_imx21_gpio_hwdata = {
   
    .dr_reg		= 0x1c,
    .gdir_reg	= 0x00,
    .psr_reg	= 0x24,
    .icr1_reg	= 0x28,
    .icr2_reg	= 0x2c,
    .imr_reg	= 0x30,
    .isr_reg	= 0x34,
    .edge_sel_reg	= -EINVAL,
    .low_level	= 0x03,
    .high_level	= 0x02,
    .rise_edge	= 0x00,
    .fall_edge	= 0x01,
};

image.png

2) platform_get_resource:获取设备树中内存资源信息,其实就是reg属性值
3) devm_ioremap_resource :内存映射,寄存器地址映射出虚拟地址
4) platform_get_irq :获取中断号

其中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;
};

其中 gpio_chip 中包含GPIO的操作函数,定义好这些函数之后, 调用函数gpiochip_add向Linux内核注册gpio_chip, 也就是 port->bgc.gc 注册完成以后我们就可以在驱动中使用 gpiolib 提供的各个 API 函数 。
这就是驱动源码中,注册gpio子系统API的过程,注册好之后,便可以在编写驱动的时候使用gpio操作的API。

4.最后

哈喽~我是徐章鑫,沪漂嵌入式开发工程师一枚,立志成为嵌入式全栈开发工程师,成为优秀博客创作者,共同学习进步。
以上代码全部放在我私人的github地址,其中有许多自己辛苦敲的例程源码,供大家参考、批评指正,有兴趣还可以直接提patch修改我的仓库~:
https://github.com/Xuzhangxin
觉得不错的话可以点个收藏和star~

相关推荐

最近更新

  1. TCP协议是安全的吗?

    2024-02-03 13:48:03       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-02-03 13:48:03       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-02-03 13:48:03       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-02-03 13:48:03       18 阅读

热门阅读

  1. 鸿蒙设置沉浸式状态栏、全屏、获取导航栏高度

    2024-02-03 13:48:03       115 阅读
  2. json文件缺少的语言key的检测

    2024-02-03 13:48:03       33 阅读
  3. 【Python】requests库的介绍及用法

    2024-02-03 13:48:03       26 阅读
  4. python--第三方包的使用

    2024-02-03 13:48:03       34 阅读
  5. Vue3 vant4 底部tabbar图标如何修改为自定义图片

    2024-02-03 13:48:03       32 阅读
  6. ChatGPT等大模型AI能干什么?

    2024-02-03 13:48:03       26 阅读
  7. 力扣0120——三角形最小路径和

    2024-02-03 13:48:03       36 阅读