当前位置: 代码迷 >> 综合 >> DeviceDriver(三):GPIO子系统
  详细解决方案

DeviceDriver(三):GPIO子系统

热度:78   发布时间:2023-10-24 00:38:53.0

一:GPIO子系统

         当PIN被pinctrl子系统复用为gpio时,就需要用到gpio子系统,gpio子系统的主要目的是为了方便使用gpio,在设备树中添加gpio相关信息,然后就可以在驱动程序中使用gpio子系统提供的API函数来操作gpio,linux内核屏蔽了gpio的设置过程,这样极大的方便了使用gpio

二:GPIO设备树信息

&iomuxc {pinctrl-names = "default";pinctrl-0 = <&pinctrl_hog_1>;imx6ul-evk {pinctrl_hog_1: hoggrp-1 {fsl,pins = <MX6UL_PAD_UART1_RTS_B__GPIO1_IO19	0x17059 /* SD1 CD */>;};};
};&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>;status = "okay";
};

(1)usdhc1节点作为SD卡设备总节点,该节点需要描述SD卡所有的信息。本该位于&usdhc1节点下的复位引脚GPIO1_IO19描述节点pinctrl_hog_1被&iomuxc节点引用“pinctrl-0 = <&pinctrl_hog_1>;”所以linux内核中的iomuxc驱动也会自动初始化pinctrl_hog_1节点下的引脚,效果相同。

(2)“cd-gpios = <&gpio1 19 GPIO_ACTIVE_LOW>;”描述了SD卡的CD引脚使用了gpio1_19,而“GPIO_ACTIVE_LOW”则表示低电平有效。

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节点描述了gpio1控制器的所有信息。

“gpio-controller”表示gpio1节点为gpio控制器。

“#gpio-cells = <2>”表示一共有两个cell,第一个cell为gpio编号,比如“&gpio1 3”就表示为GPIO1_IO03;第二个cell表示gpio极性,如果为0表示高电平有效,如果为1表示低电平有效。

三:GPIO驱动程序框架

由gpio1节点的compatible属性找到gpio-mxc.c文件

static struct platform_device_id mxc_gpio_devtype[] = {{.name = "imx1-gpio",.driver_data = IMX1_GPIO,}, {.name = "imx21-gpio",.driver_data = IMX21_GPIO,}, {.name = "imx31-gpio",.driver_data = IMX31_GPIO,}, {.name = "imx35-gpio",.driver_data = IMX35_GPIO,}, {/* sentinel */}
};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 */ }
};

反推结构体“mxc_gpio_dt_ids”

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系统也是挂在platform平台上,如果设备与驱动“.of_match_table ”相匹配则调用入口函数probe:

static int mxc_gpio_probe(struct platform_device *pdev)

(1)创建设备树结点指针,定义mxc_gpio_port结构的port指针,其中变量bgc非常重要:

struct device_node *np = pdev->dev.of_node;
struct mxc_gpio_port *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;
};

(2)获取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,
};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;
... ...mxc_gpio_hwtype = hwtype;
}

(3)获取设备树中的内存资源信息reg属性值

iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);struct resource *platform_get_resource(struct platform_device *dev,unsigned int type, unsigned int num)
{int i;for (i = 0; i < dev->num_resources; i++) {struct resource *r = &dev->resource[i];if (type == resource_type(r) && num-- == 0)return r;}return NULL;
}

(4)映射内存地址,得到物理地址在Linux内核中对应的虚拟地址

port->base = devm_ioremap_resource(&pdev->dev, iores);

(5)获取中断号

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

(6)关闭GPIO1所有IO中断,并且清除状态寄存器

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

(7)设置对应GPIO的中断服务函数。

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

(8)初始化变量bgc的成员gc

变量bgc结构:struct bgpio_chip *bgc

struct bgpio_chip {struct gpio_chip gc;unsigned long (*read_reg)(void __iomem *reg);void (*write_reg)(void __iomem *reg, unsigned long data);void __iomem *reg_dat;void __iomem *reg_set;void __iomem *reg_clr;void __iomem *reg_dir;/* Number of bits (GPIOs): <register width> * 8. */int bits;/** Some GPIO controllers work with the big-endian bits notation,* e.g. in a 8-bits register, GPIO7 is the least significant bit.*/unsigned long (*pin2mask)(struct bgpio_chip *bgc, unsigned int pin);/** Used to lock bgpio_chip->data. Also, this is needed to keep* shadowed and real data registers writes together.*/spinlock_t lock;/* Shadowed data register to clear/set bits safely. */unsigned long data;/* Shadowed direction registers to clear/set direction safely. */unsigned long dir;
};

变量gc结构:struct gpio_chip gc

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);void			(*set_multiple)(struct gpio_chip *chip,unsigned long *mask,unsigned long *bits);int			(*set_debounce)(struct gpio_chip *chip,unsigned offset,unsigned debounce);int			(*to_irq)(struct gpio_chip *chip,unsigned offset);void			(*dbg_show)(struct seq_file *s,struct gpio_chip *chip);int			base;u16			ngpio;struct gpio_desc	*desc;const char		*const *names;bool			can_sleep;bool			irq_not_threaded;bool			exported;#ifdef CONFIG_GPIOLIB_IRQCHIP/** With CONFIG_GPIOLIB_IRQCHIP we get an irqchip inside the gpiolib* to handle IRQs for most practical cases.*/struct irq_chip		*irqchip;struct irq_domain	*irqdomain;unsigned int		irq_base;irq_flow_handler_t	irq_handler;unsigned int		irq_default_type;
#endif#if defined(CONFIG_OF_GPIO)/** If CONFIG_OF is enabled, then all GPIO controllers described in the* device tree automatically may have an OF translation*/struct device_node *of_node;int of_gpio_n_cells;int (*of_xlate)(struct gpio_chip *gc,const struct of_phandle_args *gpiospec, u32 *flags);
#endif
#ifdef CONFIG_PINCTRL/** If CONFIG_PINCTRL is enabled, then gpio controllers can optionally* describe the actual pin range which they serve in an SoC. This* information would be used by pinctrl subsystem to configure* corresponding pins for gpio usage.*/struct list_head pin_ranges;
#endif
};

初始化bgc->gc中的各种GPIO操作函数,将GPIO寄存器地址赋给bgc的参数reg_dat等:

err = bgpio_init(&port->bgc, &pdev->dev, 4,port->base + GPIO_PSR,port->base + GPIO_DR, NULL,port->base + GPIO_GDIR, NULL, 0);int bgpio_init(struct bgpio_chip *bgc, struct device *dev,unsigned long sz, void __iomem *dat, void __iomem *set,void __iomem *clr, void __iomem *dirout, void __iomem *dirin,unsigned long flags)
{int ret;if (!is_power_of_2(sz))return -EINVAL;bgc->bits = sz * 8;if (bgc->bits > BITS_PER_LONG)return -EINVAL;spin_lock_init(&bgc->lock);bgc->gc.dev = dev;bgc->gc.label = dev_name(dev);bgc->gc.base = -1;bgc->gc.ngpio = bgc->bits;bgc->gc.request = bgpio_request;ret = bgpio_setup_io(bgc, dat, set, clr);if (ret)return ret;ret = bgpio_setup_accessors(dev, bgc, flags & BGPIOF_BIG_ENDIAN,flags & BGPIOF_BIG_ENDIAN_BYTE_ORDER);if (ret)return ret;ret = bgpio_setup_direction(bgc, dirout, dirin);if (ret)return ret;bgc->data = bgc->read_reg(bgc->reg_dat);if (bgc->gc.set == bgpio_set_set &&!(flags & BGPIOF_UNREADABLE_REG_SET))bgc->data = bgc->read_reg(bgc->reg_set);if (bgc->reg_dir && !(flags & BGPIOF_UNREADABLE_REG_DIR))bgc->dir = bgc->read_reg(bgc->reg_dir);return ret;
}

(9)向Linux内核注册gpio_chip结构的变量port->bgc.gc,这样就可以使用提供的API函数

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

四:GPIO子系统API函数

1:gpio_request函数用于申请一个GPIO管脚,使用of_get_named_gpio函数从设备树获取指定GPIO属性信息

int gpio_request(unsigned gpio, const char *label)
{struct gpio_desc *desc = gpio_to_desc(gpio);/* Compatibility: assume unavailable "valid" GPIOs will appear later */if (!desc && gpio_is_valid(gpio))return -EPROBE_DEFER;return gpiod_request(desc, label);
}

2:gpio_free函数用于释放GPIO管脚

void gpio_free(unsigned gpio)
{gpiod_free(gpio_to_desc(gpio));
}

3:gpio_direction_input函数用于设置GPIO为输入

static inline int gpio_direction_input(unsigned gpio)
{return gpiod_direction_input(gpio_to_desc(gpio));
}

4:gpio_direction_output函数用于设置GPIO为输出

static inline int gpio_direction_output(unsigned gpio, int value)
{return gpiod_direction_output_raw(gpio_to_desc(gpio), value);
}

5:设置/获取GPIO引脚值

#define gpio_get_value  __gpio_get_value
#define gpio_set_value  __gpio_set_valuestatic inline int __gpio_get_value(unsigned gpio)
{return gpiod_get_raw_value(gpio_to_desc(gpio));
}static inline void __gpio_set_value(unsigned gpio, int value)
{return gpiod_set_raw_value(gpio_to_desc(gpio), value);
}

五:添加gpio节点模板

1,创建设备节点

test {/* 节点内容 */
}

2、将上一节的pinctrl节点信息添加进来,参考第四节:DeviceDriver(二):Pinctrl子系统_猩猩の點燈的博客-CSDN博客_pinctrl_hog

test {/* 节点内容 */pinctrl-names = "default";pinctrl-0 = <&pinctrl_test>;
}

3、添加gpio属性信息

test {/* 节点内容 */... ...gpio = <&gpio1 0 GPIO_ACTIVE_LOW>;
}

属性“gpio”的三个值分别表示为:

“&gpio1”:该引脚所使用的IO属于GPIO1组。

“0”:表示GPIO1组的第0号IO。

“GPIO_ACTIVE_LOW”:表示低电平有效。

综合实例:

/ {model = "Freescale i.MX6 ULL 14x14 EVK Board";compatible = "fsl,imx6ull-14x14-evk", "fsl,imx6ull";
... ...beep {#address-cells = <1>;#size-cells = <1>;compatible = "my-beep";pinctrl-names = "default";pinctrl-0 = <&pinctrl_beep>;beep-gpio = <&gpio5 1 GPIO_ACTIVE_HIGH>;status = "okay";};
};&iomuxc {pinctrl-names = "default";pinctrl-0 = <&pinctrl_hog_1>;imx6ul-evk {pinctrl_beep: beepgrp {fsl,pins = <MX6UL_PAD_SNVS_TAMPER1__GPIO5_IO01   0x10b0>;};};
};

如果是在公版设备树上修改,则需要检查GPIO引脚是否被用作其他功能,如果是则需要删除对应的设置。

六:与GPIO相关的OF函数

1、of_gpio_named_count用于获取设备树某个属性里面定义了几个GPIO信息

static inline int of_gpio_named_count(struct device_node *np, const char* propname)
{return of_count_phandle_with_args(np, propname, "#gpio-cells");
}

2、of_gpio_count用于获取特定属性“gpios”里的GPIO信息

static inline int of_gpio_count(struct device_node *np)
{return of_gpio_named_count(np, "gpios");
}

3、of_get_named_gpio用于获取GPIO编号

static inline int of_get_named_gpio(struct device_node *np,const char *propname, int index)
{return of_get_named_gpio_flags(np, propname, index, NULL);
}