GPIO子系统
1.GPIO子系统作用
-
GPIO说明 芯片有很多引脚,每个引脚的功能很多,可用作GPIO、I2C、UART、SPI等功能,在Pincrt子系统篇,讲解了Pinctrl子系统,我们可以通过Pinctrl子系统对选择引脚的复用功能(如复用为GPIO或I2C等功能)以及配置引脚(上拉、下拉、驱动能力等)。 当一个引脚被复用为GPIO功能时,我们可以去设置它的方向(Input or Output),读取它的状态,GPIO名为"General Purpose Input/Output",通用目的的输入/输出,GPIO可以是芯片自带的,也可以使用扩展芯片通过I2C、SPI接口来扩展。 -
GPIO子系统作用 为了方便管理GPIO,linux内核抽象除了GPIO子系统,它既能支持芯片本身的GPIO,也能支持扩展的GPIO,提供统一的、简便的访问接口,实现GPIO基本的输入输出和中断操作。 在没有使用GPIO子系统时,我们使用一个引脚作为GPIO功能时,要通过寄存器来操作GPIO的引脚,对于不公的SOC,它的代码就不相同。 引入了GPIO子系统之后,我们就可以通过GPIO子系统来操作GPIO,操作方式如下:
- 在设备树中指定GPIO引脚
- 在驱动代码中:使用GPIO子系统提供的标准函数获取GPIO、设置GPIO方向、读取/设置GPIO值,而这段代码对于所有的SOC使用来说都是一样的。
2.GPIO子系统结构和相关数据结构
一般在驱动中使用GPIO,都是调用标准函数来操作GPIO,那标准函数是如何操作GPIO呢,下面是我整理的一个基本的框架图
上述框架图中牵扯到几个重要的结构体:
- struct gpio_device:用来描述一个gpio设备的状态和属性,如这个gpio设备有多少个pin,以及对每个pin的描述,最重要的struct gpio_chip结构体,它是gpio controller的抽象,用来控制引脚。例如在STM32MP157中的GPIOA就可以被抽象为一个struct gpio_device结构体,用来描述GPIOA的状态及属性,它下面有一个gpio_chip,里边有各种对gpio的操作,就是操作gpio0~gpio15。
- struct gpio_chip: 内核中对gpio controller的抽象,用来控制gpio device下都有一个gpio_chip,用来操作gpio device下的pin。
gpio controller中会对每一个pin进行编号,例如GPIOA0GPIO15编号015,GPIOB0GPIOB15编号1631等等,假如如我们GPIOA操作,在设备树中的节点就如下:
设备树:
beep-gpio = <&gpioa 0 GPIO_ACTIVE_HIGH>;
如果在驱动中我们要使用gpioa0这个pin,首先我们会使用of_get_named_gpio来解析设备节点,解析的结果就是获取gpio编号,后续对gpio的操作都是基于这个编号来操作。
驱动程序:
int beep_gpio;
beep_gpio=of_get_named_gpio(beep.node,"beep-gpio",0);
例如对于上面的gpioa0进行request操作流程就如下:
- 第一步:设备树中指定引脚gpioa0:beep-gpio = <&gpioa 0 GPIO_ACTIVE_HIGH>;
- 第二步:驱动程序中解析设备节点,获取gpioa0编号:beep_gpio=of_get_named_gpio(beep.node,“beep-gpio”,0);
- 第三步:调用gpiolib库中的标准gpio操作API函数gpio_request():gpio_request(beep_gpio,“BEEP-GPIO”);
- gpio_request()函数中根据gpioa0编号找到GPIOA对应的gpio device
- 调用GPIOA对应的gpio device下的gpio chip中的requst()函数来操作gpioa0
其他的gpio操作与上面的过程一致
将GPIO设备的抽象为struct gpio_device结构体:例如STM32MP157中GPIOA就是一个GPIO设备,最终我们可以使用struct gpio_device来描述GPIO的属性和状态
struct gpio_device {
int id;
struct device dev;
struct cdev chrdev;
struct device *mockdev;
struct module *owner;
struct gpio_chip *chip;
struct gpio_desc *descs;
int base;
u16 ngpio;
const char *label;
void *data;
struct list_head list;
#ifdef CONFIG_PINCTRL
struct list_head pin_ranges;
#endif
};
struct gpio_chip :gpio controller的抽象,就是对gpio最底层的控制,它里边的操作函数就是直接的通过控制寄存器来操作GPIO
struct gpio_chip {
const char *label;
struct gpio_device *gpiodev;
struct device *parent;
struct module *owner;
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);
int (*get_multiple)(struct gpio_chip *chip,
unsigned long *mask,
unsigned long *bits);
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_config)(struct gpio_chip *chip,
unsigned offset,
unsigned long config);
int (*to_irq)(struct gpio_chip *chip,
unsigned offset);
void (*dbg_show)(struct seq_file *s,
struct gpio_chip *chip);
int (*init_valid_mask)(struct gpio_chip *chip,
unsigned long *valid_mask,
unsigned int ngpios);
int base;
u16 ngpio;
const char *const *names;
bool can_sleep;
#if IS_ENABLED(CONFIG_GPIO_GENERIC)
unsigned long (*read_reg)(void __iomem *reg);
void (*write_reg)(void __iomem *reg, unsigned long data);
bool be_bits;
void __iomem *reg_dat;
void __iomem *reg_set;
void __iomem *reg_clr;
void __iomem *reg_dir_out;
void __iomem *reg_dir_in;
bool bgpio_dir_unreadable;
int bgpio_bits;
spinlock_t bgpio_lock;
unsigned long bgpio_data;
unsigned long bgpio_dir;
#endif
#ifdef CONFIG_GPIOLIB_IRQCHIP
struct gpio_irq_chip irq;
#endif
unsigned long *valid_mask;
#if defined(CONFIG_OF_GPIO)
struct device_node *of_node;
unsigned int of_gpio_n_cells;
int (*of_xlate)(struct gpio_chip *gc,
const struct of_phandle_args *gpiospec, u32 *flags);
#endif
};
综合上述代码我们可以看出对GPIO操作最底层的操作就是通过gpio_chip结构体来实现,其他从gpiolib库到调用gpio_chip结构体中的函数这个框架内核的gpio子系统驱动已经帮我们写好了,假如我们自己要写一个gpio controller驱动,我们只需要构建gpio_chip,所以写一个gpio controller驱动就有以下三个步骤:
- 第一步:分配gpio_chip结构体
- 第二步:设置gpio_chip结构体
- 第三步:注册gpio_chip结构体,注册采用gpiochip_add_data()函数,在include/linux/gpio/driver.h文件中定义,注册时会自动创建struct gpio_device结构体。
3.STM32MP157中GPIO Controller的实现过程
-
设备树 gpio controller在设备树中的描述:它分别在两个文件中进行了描述,arch/arm/boot/dts/stm32mp151.dtsi和 arch/arm/boot/dts/stm32mp15xxac-pinctrl.dtsi文件,现在我把它合并为一个,如下: pinctrl: pin-controller@50002000 {
#address-cells = <1>;
#size-cells = <1>;
compatible = "st,stm32mp157-pinctrl";
ranges = <0 0x50002000 0xa400>;
interrupt-parent = <&exti>;
st,syscfg = <&exti 0x60 0xff>;
hwlocks = <&hsem 0 1>;
pins-are-numbered;
gpioa: gpio@50002000 {
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
reg = <0x0 0x400>;
clocks = <&rcc GPIOA>;
st,bank-name = "GPIOA";
status = "okay";
ngpios = <16>;
gpio-ranges = <&pinctrl 0 0 16>;
};
gpiob: gpio@50003000 {
gpio-controller;
#gpio-cells = <2>;
interrupt-controller;
#interrupt-cells = <2>;
reg = <0x1000 0x400>;
clocks = <&rcc GPIOB>;
st,bank-name = "GPIOB";
status = "okay";
ngpios = <16>;
gpio-ranges = <&pinctrl 0 16 16>;
};
.........
};
里边例举了两个节点gpioa和gpiob,剩下的和它们结构一样,里边都有一个gpio-controller属性,表明这个设备节点是一个gpio controller节点,而且里边没有compatible属性,它的父节点是pinctrl: pin-controller,这个是pin controller节点,所以gpio controller驱动是在pinctrl驱动中实现的。它实在pinctrl驱动中进行对gpio_chip结构体的分配、设置和注册的。 我们再来看它的驱动代码: int stm32_pctl_probe(struct platform_device *pdev)
{
struct device_node *np = pdev->dev.of_node;
struct device_node *child;
const struct of_device_id *match;
struct device *dev = &pdev->dev;
struct stm32_pinctrl *pctl;
struct pinctrl_pin_desc *pins;
int i, ret, hwlock_id, banks = 0;
.........
..........
for_each_available_child_of_node(np, child)
if (of_property_read_bool(child, "gpio-controller"))
banks++;
if (!banks) {
dev_err(dev, "at least one GPIO bank is required\n");
return -EINVAL;
}
pctl->banks = devm_kcalloc(dev, banks, sizeof(*pctl->banks),
GFP_KERNEL);
if (!pctl->banks)
return -ENOMEM;
i = 0;
for_each_available_child_of_node(np, child) {
struct stm32_gpio_bank *bank = &pctl->banks[i];
if (of_property_read_bool(child, "gpio-controller")) {
bank->rstc = of_reset_control_get_exclusive(child,
NULL);
if (PTR_ERR(bank->rstc) == -EPROBE_DEFER)
return -EPROBE_DEFER;
bank->clk = of_clk_get_by_name(child, NULL);
if (IS_ERR(bank->clk)) {
if (PTR_ERR(bank->clk) != -EPROBE_DEFER)
dev_err(dev,
"failed to get clk (%ld)\n",
PTR_ERR(bank->clk));
return PTR_ERR(bank->clk);
}
i++;
}
}
for_each_available_child_of_node(np, child) {
if (of_property_read_bool(child, "gpio-controller")) {
ret = stm32_gpiolib_register_bank(pctl, child);
if (ret) {
of_node_put(child);
return ret;
}
pctl->nbanks++;
}
}
dev_info(dev, "Pinctrl STM32 initialized\n");
return 0;
}
stm32_gpiolib_register_bank()函数解析 static int stm32_gpiolib_register_bank(struct stm32_pinctrl *pctl,
struct device_node *np)
{
struct stm32_gpio_bank *bank = &pctl->banks[pctl->nbanks];
int bank_ioport_nr;
struct pinctrl_gpio_range *range = &bank->range;
struct of_phandle_args args;
struct device *dev = pctl->dev;
struct resource res;
int npins = STM32_GPIO_PINS_PER_BANK;
int bank_nr, err;
if (!IS_ERR(bank->rstc))
reset_control_deassert(bank->rstc);
if (of_address_to_resource(np, 0, &res))
return -ENODEV;
bank->base = devm_ioremap_resource(dev, &res);
if (IS_ERR(bank->base))
return PTR_ERR(bank->base);
err = clk_prepare(bank->clk);
if (err) {
dev_err(dev, "failed to prepare clk (%d)\n", err);
return err;
}
bank->gpio_chip = stm32_gpio_template;
of_property_read_string(np, "st,bank-name", &bank->gpio_chip.label);
if (!of_parse_phandle_with_fixed_args(np, "gpio-ranges", 3, 0, &args)) {
bank_nr = args.args[1] / STM32_GPIO_PINS_PER_BANK;
bank->gpio_chip.base = args.args[1];
} else {
bank_nr = pctl->nbanks;
bank->gpio_chip.base = bank_nr * STM32_GPIO_PINS_PER_BANK;
range->name = bank->gpio_chip.label;
range->id = bank_nr;
range->pin_base = range->id * STM32_GPIO_PINS_PER_BANK;
range->base = range->id * STM32_GPIO_PINS_PER_BANK;
range->npins = npins;
range->gc = &bank->gpio_chip;
pinctrl_add_gpio_range(pctl->pctl_dev,
&pctl->banks[bank_nr].range);
}
if (of_property_read_u32(np, "st,bank-ioport", &bank_ioport_nr))
bank_ioport_nr = bank_nr;
bank->gpio_chip.base = bank_nr * STM32_GPIO_PINS_PER_BANK;
bank->gpio_chip.ngpio = npins;
bank->gpio_chip.of_node = np;
bank->gpio_chip.parent = dev;
bank->bank_nr = bank_nr;
bank->bank_ioport_nr = bank_ioport_nr;
spin_lock_init(&bank->lock);
bank->fwnode = of_node_to_fwnode(np);
bank->domain = irq_domain_create_hierarchy(pctl->domain, 0,
STM32_GPIO_IRQ_LINE, bank->fwnode,
&stm32_gpio_domain_ops, bank);
if (!bank->domain)
return -ENODEV;
err = gpiochip_add_data(&bank->gpio_chip, bank);
if (err) {
dev_err(dev, "Failed to add gpiochip(%d)!\n", bank_nr);
return err;
}
dev_info(dev, "%s bank added\n", bank->gpio_chip.label);
return 0;
}
最后调用gpiochip_add_data()注册gpio_chip结构体,接下来我们分析gpiochip_add_data()函数,在include/linux/gpio/driver.h文件中被宏定义: #define gpiochip_add_data(chip, data) gpiochip_add_data_with_key(chip, data, NULL, NULL)
所以最终使用的函数为gpiochip_add_data_with_key();在drivers/gpio/gpiolib.c文件中定义 int gpiochip_add_data_with_key(struct gpio_chip *chip, void *data,
struct lock_class_key *lock_key,
struct lock_class_key *request_key)
{
unsigned long flags;
int ret = 0;
unsigned i;
int base = chip->base;
struct gpio_device *gdev;
gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
if (!gdev)
return -ENOMEM;
gdev->dev.bus = &gpio_bus_type;
gdev->chip = chip;
chip->gpiodev = gdev;
if (chip->parent) {
gdev->dev.parent = chip->parent;
gdev->dev.of_node = chip->parent->of_node;
}
#ifdef CONFIG_OF_GPIO
if (chip->of_node)
gdev->dev.of_node = chip->of_node;
else
chip->of_node = gdev->dev.of_node;
#endif
gdev->id = ida_simple_get(&gpio_ida, 0, 0, GFP_KERNEL);
if (gdev->id < 0) {
ret = gdev->id;
goto err_free_gdev;
}
dev_set_name(&gdev->dev, "gpiochip%d", gdev->id);
device_initialize(&gdev->dev);
dev_set_drvdata(&gdev->dev, gdev);
if (chip->parent && chip->parent->driver)
gdev->owner = chip->parent->driver->owner;
else if (chip->owner)
gdev->owner = chip->owner;
else
gdev->owner = THIS_MODULE;
#define FLAG_REQUESTED 0
#define FLAG_IS_OUT 1
#define FLAG_EXPORT 2
#define FLAG_SYSFS 3
#define FLAG_ACTIVE_LOW 6
#define FLAG_OPEN_DRAIN 7
#define FLAG_OPEN_SOURCE 8
#define FLAG_USED_AS_IRQ 9
#define FLAG_IRQ_IS_ENABLED 10
#define FLAG_IS_HOGGED 11
#define FLAG_TRANSITORY 12
#define FLAG_PULL_UP 13
#define FLAG_PULL_DOWN 14
gdev->descs = kcalloc(chip->ngpio, sizeof(gdev->descs[0]), GFP_KERNEL);
if (!gdev->descs) {
ret = -ENOMEM;
goto err_free_ida;
}
if (chip->ngpio == 0) {
chip_err(chip, "tried to insert a GPIO chip with zero lines\n");
ret = -EINVAL;
goto err_free_descs;
}
if (chip->ngpio > FASTPATH_NGPIO)
chip_warn(chip, "line cnt %u is greater than fast path cnt %u\n",
chip->ngpio, FASTPATH_NGPIO);
gdev->label = kstrdup_const(chip->label ?: "unknown", GFP_KERNEL);
if (!gdev->label) {
ret = -ENOMEM;
goto err_free_descs;
}
gdev->ngpio = chip->ngpio;
gdev->data = data;
spin_lock_irqsave(&gpio_lock, flags);
if (base < 0) {
base = gpiochip_find_base(chip->ngpio);
if (base < 0) {
ret = base;
spin_unlock_irqrestore(&gpio_lock, flags);
goto err_free_label;
}
chip->base = base;
}
gdev->base = base;
ret = gpiodev_add_to_list(gdev);
if (ret) {
spin_unlock_irqrestore(&gpio_lock, flags);
goto err_free_label;
}
spin_unlock_irqrestore(&gpio_lock, flags);
for (i = 0; i < chip->ngpio; i++)
gdev->descs[i].gdev = gdev;
#ifdef CONFIG_PINCTRL
INIT_LIST_HEAD(&gdev->pin_ranges);
#endif
ret = gpiochip_set_desc_names(chip);
if (ret)
goto err_remove_from_list;
ret = gpiochip_alloc_valid_mask(chip);
if (ret)
goto err_remove_from_list;
ret = of_gpiochip_add(chip);
if (ret)
goto err_free_gpiochip_mask;
ret = gpiochip_init_valid_mask(chip);
if (ret)
goto err_remove_of_chip;
for (i = 0; i < chip->ngpio; i++) {
struct gpio_desc *desc = &gdev->descs[i];
if (chip->get_direction && gpiochip_line_is_valid(chip, i)) {
if (!chip->get_direction(chip, i))
set_bit(FLAG_IS_OUT, &desc->flags);
else
clear_bit(FLAG_IS_OUT, &desc->flags);
} else {
if (!chip->direction_input)
set_bit(FLAG_IS_OUT, &desc->flags);
else
clear_bit(FLAG_IS_OUT, &desc->flags);
}
}
...............................
}
STM32MP157的gpio controller驱动程序我们就分析到这里,看上去很复杂,其实也就是实现前面所分析的三步:第一步:分配gpio_chip结构体;第二步:设置gpio_chip结构体;第三步:注册gpio_chip结构体
|