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