我是靠谱客的博主 重要哈密瓜,最近开发中收集的这篇文章主要介绍Linux内核4.14版本——GPIO子系统(2)——gpio control driver分析1. DTS文件2. gpio control driver,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

目录

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所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(53)

评论列表共有 0 条评论

立即
投稿
返回
顶部