一: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 = <®_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);
}