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

文章目录

  • 1.gpio子系统api
  • 2.gpio相关of函数
  • 3.gpio子系统驱动分析
    • 3.1设备树信息分析
    • 3.2驱动程序分析
  • 4.最后

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     0x17059MX6UL_PAD_SD1_CLK__USDHC1_CLK     0x10071MX6UL_PAD_SD1_DATA0__USDHC1_DATA0 0x17059MX6UL_PAD_SD1_DATA1__USDHC1_DATA1 0x17059MX6UL_PAD_SD1_DATA2__USDHC1_DATA2 0x17059MX6UL_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;elsemxc_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~