概述
目录
1. DTS文件
2. gpio control driver
2.1 dwapb_gpio_get_pdata
2.2 dwapb_gpio_add_port
2.2.1 gpio的参数设置,set、dat、dirout等(bgpio_init)
2.2.2 配置中断(dwapb_configure_irqs)
2.3 把gpio control注册到kernel(gpiochip_add_data)
源码:driversgpiogpio-dwapb.c
1. DTS文件
pinctrl: pinctrl@xxx {
compatible = "zynq,zynq-pinctrl";
reg = <0x0 xxx 0x0 0x1000>;
tsm,npins = <97>;
};
gpio0: gpio@xxx {
#address-cells = <1>;
#size-cells = <0>;
compatible = "snps,dw-apb-gpio";
reg = <0x0 xxx 0x0 0x400>;
porta: gpio-controller@0 {
compatible = "snps,dw-apb-gpio-port";
gpio-controller;
#gpio-cells = <2>;
snps,nr-gpios = <32>;
reg = <0>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&gic>;
interrupts = <0 2 4>;
gpio-ranges = <&pinctrl 0 0 5>, <&pinctrl 5 96 1>, <&pinctrl 6 6 26>;
};
};
gpio1: gpio@xxx {
#address-cells = <1>;
#size-cells = <0>;
compatible = "snps,dw-apb-gpio";
reg = <0x0 xxxx 0x0 0x400>;
portb: gpio-controller@0 {
compatible = "snps,dw-apb-gpio-port";
gpio-controller;
#gpio-cells = <2>;
snps,nr-gpios = <32>;
reg = <0>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&gic>;
interrupts = <0 2 4>;
gpio-ranges = <&pinctrl 0 32 32>;
};
};
gpio2: gpio@xxxx {
#address-cells = <1>;
#size-cells = <0>;
compatible = "snps,dw-apb-gpio";
reg = <0x0 xxxx 0x0 0x400>;
portc: gpio-controller@0 {
compatible = "snps,dw-apb-gpio-port";
gpio-controller;
#gpio-cells = <2>;
snps,nr-gpios = <32>;
reg = <0>;
interrupt-controller;
#interrupt-cells = <2>;
interrupt-parent = <&gic>;
interrupts = <0 2 4>;
gpio-ranges = <&pinctrl 0 64 32>;
};
};
其他字段不用讲,主要讲一下gpio-ranges,这个是gpio和pinctrl映射的关键,详细见文章。
2. gpio control driver
static const struct of_device_id dwapb_of_match[] = {
{ .compatible = "snps,dw-apb-gpio", .data = (void *)0},
{ .compatible = "apm,xgene-gpio-v2", .data = (void *)GPIO_REG_OFFSET_V2},
{ /* Sentinel */ }
};
当名字一致的时候,调用probe函数。
static int dwapb_gpio_probe(struct platform_device *pdev)
{
unsigned int i;
struct resource *res;
struct dwapb_gpio *gpio;
int err;
struct device *dev = &pdev->dev;
struct dwapb_platform_data *pdata = dev_get_platdata(dev);
if (!pdata) {
pdata = dwapb_gpio_get_pdata(dev);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
}
if (!pdata->nports)
return -ENODEV;
gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL);
if (!gpio)
return -ENOMEM;
gpio->dev = &pdev->dev;
gpio->nr_ports = pdata->nports;
gpio->ports = devm_kcalloc(&pdev->dev, gpio->nr_ports,
sizeof(*gpio->ports), GFP_KERNEL);
if (!gpio->ports)
return -ENOMEM;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
gpio->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(gpio->regs))
return PTR_ERR(gpio->regs);
gpio->flags = 0;
......
for (i = 0; i < gpio->nr_ports; i++) {
err = dwapb_gpio_add_port(gpio, &pdata->properties[i], i);
if (err)
goto out_unregister;
}
platform_set_drvdata(pdev, gpio);
return 0;
out_unregister:
dwapb_gpio_unregister(gpio);
dwapb_irq_teardown(gpio);
return err;
}
probe函数中先调用dwapb_gpio_get_pdata函数得到gpio的信息,然后dwapb_gpio_add_port加入到kernel中。
2.1 dwapb_gpio_get_pdata
static struct dwapb_platform_data *
dwapb_gpio_get_pdata(struct device *dev)
{
struct fwnode_handle *fwnode;
struct dwapb_platform_data *pdata;
struct dwapb_port_property *pp;
int nports;
int i;
nports = device_get_child_node_count(dev); // (1)
if (nports == 0)
return ERR_PTR(-ENODEV);
pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return ERR_PTR(-ENOMEM);
pdata->properties = devm_kcalloc(dev, nports, sizeof(*pp), GFP_KERNEL);
if (!pdata->properties)
return ERR_PTR(-ENOMEM);
pdata->nports = nports;
i = 0;
device_for_each_child_node(dev, fwnode) { // (2)
pp = &pdata->properties[i++];
pp->fwnode = fwnode;
if (fwnode_property_read_u32(fwnode, "reg", &pp->idx) ||
pp->idx >= DWAPB_MAX_PORTS) {
dev_err(dev,
"missing/invalid port index for port%dn", i);
fwnode_handle_put(fwnode);
return ERR_PTR(-EINVAL);
}
if (fwnode_property_read_u32(fwnode, "snps,nr-gpios",
&pp->ngpio)) {
dev_info(dev,
"failed to get number of gpios for port%dn",
i);
pp->ngpio = 32;
}
/*
* Only port A can provide interrupts in all configurations of
* the IP.
*/
if (dev->of_node && pp->idx == 0 &&
fwnode_property_read_bool(fwnode,
"interrupt-controller")) {
pp->irq = irq_of_parse_and_map(to_of_node(fwnode), 0);
if (!pp->irq)
dev_warn(dev, "no irq for port%dn", pp->idx);
}
if (has_acpi_companion(dev) && pp->idx == 0)
pp->irq = platform_get_irq(to_platform_device(dev), 0);
pp->irq_shared = true; // (3)
pp->gpio_base = -1;
}
return pdata;
}
(1) 先调用device_get_child_node_count得到该gpio control有几个bank,从第1节的dts中可以看到,总共有3个gpio control,每个gpio control有一个bank。
(2) 循环中处理每个bank节点的数据。
(3) 这个很重要,一般GPIO是公用一个中断号的,此处要设为共享中断标志。后面会用到。
2.2 dwapb_gpio_add_port
2.2.1 gpio的参数设置,set、dat、dirout等(bgpio_init)
static int dwapb_gpio_add_port(struct dwapb_gpio *gpio,
struct dwapb_port_property *pp,
unsigned int offs)
{
struct dwapb_gpio_port *port;
void __iomem *dat, *set, *dirout;
int err;
port = &gpio->ports[offs];
port->gpio = gpio;
port->idx = pp->idx;
#ifdef CONFIG_PM_SLEEP
port->ctx = devm_kzalloc(gpio->dev, sizeof(*port->ctx), GFP_KERNEL);
if (!port->ctx)
return -ENOMEM;
#endif
dat = gpio->regs + GPIO_EXT_PORTA + (pp->idx * GPIO_EXT_PORT_SIZE);
set = gpio->regs + GPIO_SWPORTA_DR + (pp->idx * GPIO_SWPORT_DR_SIZE);
dirout = gpio->regs + GPIO_SWPORTA_DDR +
(pp->idx * GPIO_SWPORT_DDR_SIZE);
err = bgpio_init(&port->gc, gpio->dev, 4, dat, set, NULL, dirout,
NULL, false);
设置gpio的dat、set、dir等的参数,然后通过bgpio_init告诉kernel的gpio core,后面通过这个值来设置gpio的状态、读gpio的值。bgpio_init暂不做分析。
2.2.2 配置中断(dwapb_configure_irqs)
static void dwapb_configure_irqs(struct dwapb_gpio *gpio,
struct dwapb_gpio_port *port,
struct dwapb_port_property *pp)
{
struct gpio_chip *gc = &port->gc;
struct fwnode_handle *fwnode = pp->fwnode;
struct irq_chip_generic *irq_gc = NULL;
unsigned int hwirq, ngpio = gc->ngpio;
struct irq_chip_type *ct;
int err, i;
gpio->domain = irq_domain_create_linear(fwnode, ngpio,
&irq_generic_chip_ops, gpio);
if (!gpio->domain)
return;
err = irq_alloc_domain_generic_chips(gpio->domain, ngpio, 2,
"gpio-dwapb", handle_level_irq,
IRQ_NOREQUEST, 0,
IRQ_GC_INIT_NESTED_LOCK);
if (err) {
dev_info(gpio->dev, "irq_alloc_domain_generic_chips failedn");
irq_domain_remove(gpio->domain);
gpio->domain = NULL;
return;
}
irq_gc = irq_get_domain_generic_chip(gpio->domain, 0);
if (!irq_gc) {
irq_domain_remove(gpio->domain);
gpio->domain = NULL;
return;
}
irq_gc->reg_base = gpio->regs;
irq_gc->private = gpio;
for (i = 0; i < 2; i++) {
ct = &irq_gc->chip_types[i];
ct->chip.irq_ack = irq_gc_ack_set_bit;
ct->chip.irq_mask = irq_gc_mask_set_bit;
ct->chip.irq_unmask = irq_gc_mask_clr_bit;
ct->chip.irq_set_type = dwapb_irq_set_type;
ct->chip.irq_enable = dwapb_irq_enable;
ct->chip.irq_disable = dwapb_irq_disable;
ct->chip.irq_request_resources = dwapb_irq_reqres;
ct->chip.irq_release_resources = dwapb_irq_relres;
ct->regs.ack = gpio_reg_convert(gpio, GPIO_PORTA_EOI);
ct->regs.mask = gpio_reg_convert(gpio, GPIO_INTMASK);
ct->type = IRQ_TYPE_LEVEL_MASK;
}
irq_gc->chip_types[0].type = IRQ_TYPE_LEVEL_MASK;
irq_gc->chip_types[1].type = IRQ_TYPE_EDGE_BOTH;
irq_gc->chip_types[1].handler = handle_edge_irq;
if (!pp->irq_shared) {
irq_set_chained_handler_and_data(pp->irq, dwapb_irq_handler,
gpio);
} else {
/*
* Request a shared IRQ since where MFD would have devices
* using the same irq pin
*/
err = devm_request_irq(gpio->dev, pp->irq,
dwapb_irq_handler_mfd,
IRQF_SHARED, "gpio-dwapb-mfd", gpio);
if (err) {
dev_err(gpio->dev, "error requesting IRQn");
irq_domain_remove(gpio->domain);
gpio->domain = NULL;
return;
}
}
for (hwirq = 0 ; hwirq < ngpio ; hwirq++)
irq_create_mapping(gpio->domain, hwirq);
port->gc.to_irq = dwapb_gpio_to_irq;
}
配置中断,主要是设置共享中断。
2.3 把gpio control注册到kernel(gpiochip_add_data)
gpiochip_add_data
of_gpiochip_add
of_gpiochip_add_pin_range(gpio和pinctrl耦合绑定)
最后
以上就是重要哈密瓜为你收集整理的Linux内核4.14版本——GPIO子系统(2)——gpio control driver分析1. DTS文件2. gpio control driver的全部内容,希望文章能够帮你解决Linux内核4.14版本——GPIO子系统(2)——gpio control driver分析1. DTS文件2. gpio control driver所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复