DeviceDriver(三):GPIO子系统

 

一: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_value

static 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节点信息添加进来,参考第四节:https://blog.csdn.net/qq_34968572/article/details/103275579

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

猜你喜欢

转载自blog.csdn.net/qq_34968572/article/details/103295793
今日推荐