文章目录
- 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卡引脚图如下:
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 = <®_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,在寄存器手册中可以看到各寄存器地址。
这里基本就看懂一个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,
};
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~