【学习分享】全志T507平台GPIO寄存器控制原理


概述

本文浅要讲解下全志T507 gpio口的控制原理。


GPIO控制原理

首先看一下pinctrl的如下设备树

		pio: pinctrl@0300b000 {
			compatible = "allwinner,sun50iw9p1-pinctrl";
			reg = <0x0 0x0300b000 0x0 0x400>;
			interrupts = <GIC_SPI 51 IRQ_TYPE_LEVEL_HIGH>,  /* AW1823_GIC_Spec: GPIOA: 83-32=51 */
				     <GIC_SPI 52 IRQ_TYPE_LEVEL_HIGH>,
				     <GIC_SPI 53 IRQ_TYPE_LEVEL_HIGH>,
				     <GIC_SPI 43 IRQ_TYPE_LEVEL_HIGH>,
				     <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>,
				     <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>,
				     <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>,
				     <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>;
			device_type = "pio";
			clocks = <&clk_pio>, <&clk_losc>, <&clk_hosc>;
			gpio-controller;
			interrupt-controller;
			#interrupt-cells = <3>;
			#size-cells = <0>;
			#gpio-cells = <6>;
			/* takes the debounce time in usec as argument */
			input-debounce = <0 0 0 0 0 0 0>;

			/*clk_losc_pins_a: clk_losc@0 {
				allwinner,pins = "PG10";
				allwinner,function = "x32kfout";
				allwinner,muxsel = <3>;
				allwinner,drive = <2>;
				allwinner,pull = <1>;
			};*/
			...//中间省略
			uart0_pins_a: uart0@0 {
				allwinner,pins = "PH0", "PH1";
				allwinner,pname = "uart0_tx", "uart0_rx";
				allwinner,function = "uart0";
				allwinner,muxsel = <2>;
				allwinner,drive = <1>;
				allwinner,pull = <1>;
			};

			uart0_pins_b: uart0@1 {
				allwinner,pins = "PH0", "PH1";
				allwinner,function = "uart0";  /* io_disabled */
				allwinner,muxsel = <7>;
				allwinner,drive = <1>;
				allwinner,pull = <0>;
			};
			...//以下省略

实际应用过程中,我们只需要在设备树中配置Pin脚的复用功能,拉高拉低就可以了。
如果想深入了解下其中的原理呢,linux系统是如何去控制对应寄存器以实现我们的需求的呢?

以串口uart0的pin脚为例

		uart0: uart@05000000 {
			compatible = "allwinner,sun50i-uart";
			device_type = "uart0";
			reg = <0x0 0x05000000 0x0 0x400>;
			interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
			clocks = <&clk_uart0>;
			pinctrl-names = "default", "sleep";
			pinctrl-0 = <&uart0_pins_a>;      //引用pin脚
			pinctrl-1 = <&uart0_pins_b>;      //引用pin脚
			uart0_port = <0>;
			uart0_type = <2>;
			status = "okay";
		};

问题来了,如何使用两个pin脚,需要看源代码,如何查看源代码呢?
要想查找源码,这一句是关键。设备和驱动probe的匹配就是通过compatible关键字匹配上的。

compatible = "allwinner,sun50i-uart"

搜索关键字,可以找到在kernel/linux-4.9/drivers/tty/serial/sunxi-uart.c中

static const struct of_device_id sunxi_uart_match[] = {
	{ .compatible = "allwinner,sun8i-uart", },
	{ .compatible = "allwinner,sun50i-uart", },
	{ .compatible = "allwinner,sun3i-uart", },
	{},
};

找到uart的probe

static int sw_uart_probe(struct platform_device *pdev)
port->ops = &sw_uart_ops
		.config_port = sw_uart_config_port
				sw_uart_request_port
					sw_uart_request_gpio
						
static int sw_uart_request_port(struct uart_port *port)
{
	struct sw_uart_port *sw_uport = UART_TO_SPORT(port);
	struct platform_device *pdev;
	struct resource	*mem_res;
	int ret;

	SERIAL_DBG("request port(ioremap & request io) %d\n", sw_uport->id);

	pdev = to_platform_device(port->dev);
	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 
	/*获取uart0的物理地址,这个在哪里呢?看设备树:uart0: uart@05000000 
	基地址找到了,0x05000000*/
		
	if (mem_res == NULL) {
		SERIAL_MSG("uart%d, get MEM resource failed\n", sw_uport->id);
		ret = -ENXIO;
	}

	/* request memory resource */
	if (!request_mem_region(mem_res->start, resource_size(mem_res), SUNXI_UART_DEV_NAME)) {
		SERIAL_MSG("uart%d, request mem region failed\n", sw_uport->id);
		return -EBUSY;
	}

	port->membase = ioremap(mem_res->start, resource_size(mem_res));
	if (!port->membase) {
		SERIAL_MSG("uart%d, ioremap failed\n", sw_uport->id);
		release_mem_region(mem_res->start, resource_size(mem_res));
		return -EBUSY;
	}

	/* request io resource */
	ret = sw_uart_request_gpio(sw_uport);//获取uart0的gpio资源
	if (ret < 0) {
		release_mem_region(mem_res->start, resource_size(mem_res));
		return ret;
	}

	return 0;
}

然后看sw_uart_request_gpio函数

static int sw_uart_request_gpio(struct sw_uart_port *sw_uport)
{
	if (sw_uport->card_print)
		return 0;

	sw_uport->pctrl = devm_pinctrl_get(sw_uport->port.dev);//此时pinctrl子系统该登场了

	if (IS_ERR_OR_NULL(sw_uport->pctrl)) {
		SERIAL_MSG("UART%d devm_pinctrl_get() failed! return %ld\n", sw_uport->id, PTR_ERR(sw_uport->pctrl));
		return -1;
	}

	return sw_uart_select_gpio_state(sw_uport->pctrl, PINCTRL_STATE_DEFAULT, sw_uport->id);
}

从pinctrl子系统获取我们所需的接口函数。

static int sw_uart_select_gpio_state(struct pinctrl *pctrl, char *name, u32 no)
{
	int ret = 0;
	struct pinctrl_state *pctrl_state = NULL;

	pctrl_state = pinctrl_lookup_state(pctrl, name); //查找pinctrl状态
	if (IS_ERR(pctrl_state)) {
		SERIAL_MSG("UART%d pinctrl_lookup_state(%s) failed! return %p \n", no, name, pctrl_state);
		return -1;
	}

	ret = pinctrl_select_state(pctrl, pctrl_state);//设置pinctrl状态
	if (ret < 0)
		SERIAL_MSG("UART%d pinctrl_select_state(%s) failed! return %d \n", no, name, ret);

	return ret;
}

直接查找相应函数pinctrl_lookup_state,可以找到如下定义

/**
 * pinctrl_lookup_state() - retrieves a state handle from a pinctrl handle
 * @p: the pinctrl handle to retrieve the state from
 * @name: the state name to retrieve
 */
struct pinctrl_state *pinctrl_lookup_state(struct pinctrl *p,
						 const char *name)
{
	struct pinctrl_state *state;

	state = find_state(p, name);
	if (!state) {
		if (pinctrl_dummy_state) {
			/* create dummy state */
			dev_dbg(p->dev, "using pinctrl dummy state (%s)\n",
				name);
			state = create_state(p, name);
		} else
			state = ERR_PTR(-ENODEV);
	}

	return state;
}
EXPORT_SYMBOL_GPL(pinctrl_lookup_state);

这些都是Pinctrl子系统提供给外部的接口。

pinctrl子系统

看下pinctrl的注册过程,通过pinctrl的compatilbe值进行查找

static int sun50iw9p1_pinctrl_probe(struct platform_device *pdev)
{
	return sunxi_pinctrl_init(pdev, &sun50iw9p1_pinctrl_data);
	//初始化的Pin脚是对应的T507 sun50iw9p1的pin脚。
}

static struct of_device_id sun50iw9p1_pinctrl_match[] = {
	{ .compatible = "allwinner,sun50iw9p1-pinctrl", },
	{}
};

t507的pinctrl寄存器基地址及偏移量。

static const struct sunxi_pinctrl_desc sun50iw9p1_pinctrl_data = {
	.pins = sun50iw9p1_pins,
	.npins = ARRAY_SIZE(sun50iw9p1_pins),
	.pin_base = 0,
	.banks = ARRAY_SIZE(sun50iw9p1_bank_base),
	.bank_base = sun50iw9p1_bank_base,
	.irq_banks = ARRAY_SIZE(sun50iw9p1_irq_bank_base),
	.irq_bank_base = sun50iw9p1_irq_bank_base,
};

找到对应 pinctrl probe,这个函数比较重要,所以代码都贴了。

int sunxi_pinctrl_init(struct platform_device *pdev,
		       const struct sunxi_pinctrl_desc *desc)
{
	struct device_node *node = pdev->dev.of_node;
	struct pinctrl_desc *pctrl_desc;
	struct pinctrl_pin_desc *pins;
	struct sunxi_pinctrl *pctl;
	struct pinmux_ops *pmxops;
	struct resource *res;
	int i, ret, last_pin;
	struct clk *clk;
	struct sunxi_pinctrl_function *func;

	pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL);
	if (!pctl)
		return -ENOMEM;
	platform_set_drvdata(pdev, pctl);

	raw_spin_lock_init(&pctl->lock);

	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	//此处获取pinctrl的物理地址,在设备树pio: pinctrl@0300b000
	pctl->membase = devm_ioremap_resource(&pdev->dev, res);
	if (IS_ERR(pctl->membase))
		return PTR_ERR(pctl->membase);

	pctl->dev = &pdev->dev;
	pctl->desc = desc;

	pctl->irq_array = devm_kcalloc(&pdev->dev,
				       IRQ_PER_BANK * pctl->desc->irq_banks,
				       sizeof(*pctl->irq_array),
				       GFP_KERNEL);
	if (!pctl->irq_array)
		return -ENOMEM;
	pctl->wake_mask = devm_kcalloc(&pdev->dev,
					pctl->desc->irq_banks,
					sizeof(*pctl->wake_mask),
					GFP_KERNEL);
	if (!pctl->wake_mask)
		return -ENOMEM;
	pctl->cur_mask = devm_kcalloc(&pdev->dev,
					pctl->desc->irq_banks,
					sizeof(*pctl->cur_mask),
					GFP_KERNEL);
	if (!pctl->cur_mask)
		return -ENOMEM;

	/*
	 * alloc a temp buffer for count the pin
	 * functions,and then we shuold free it
	 */
	func = kzalloc(pctl->desc->npins * sizeof(*pctl->functions) * 7,
		       GFP_KERNEL);
	if (!func)
		return -ENOMEM;
	/* count the pin functions for using in sunxi_pinctrl_build_state */
	pctl->nfunctions = sunxi_pinctrl_count_function(pctl, func);

	kfree(func);

	ret = sunxi_pinctrl_build_state(pdev);
	if (ret) {
		dev_err(&pdev->dev, "dt probe failed: %d\n", ret);
		return ret;
	}

	pins = devm_kzalloc(&pdev->dev,
			    pctl->desc->npins * sizeof(*pins),
			    GFP_KERNEL);
	if (!pins)
		return -ENOMEM;

	for (i = 0; i < pctl->desc->npins; i++)
		pins[i] = pctl->desc->pins[i].pin;

	pctrl_desc = devm_kzalloc(&pdev->dev,
				  sizeof(*pctrl_desc),
				  GFP_KERNEL);
	if (!pctrl_desc)
		return -ENOMEM;

	pctrl_desc->name = dev_name(&pdev->dev);
	pctrl_desc->owner = THIS_MODULE;
	pctrl_desc->pins = pins;
	pctrl_desc->npins = pctl->desc->npins;
	pctrl_desc->confops = &sunxi_pconf_ops;
	pctrl_desc->pctlops = &sunxi_pctrl_ops;

	pmxops = devm_kmemdup(&pdev->dev, &sunxi_pmx_ops, sizeof(sunxi_pmx_ops),
			      GFP_KERNEL);
	if (!pmxops)
		return -ENOMEM;

	if (desc->disable_strict_mode)
		pmxops->strict = false;

	pctrl_desc->pmxops = pmxops;

	pctl->pctl_dev = pinctrl_register(pctrl_desc,
					  &pdev->dev, pctl);
	if (IS_ERR(pctl->pctl_dev)) {
		dev_err(&pdev->dev, "couldn't register pinctrl driver\n");
		return PTR_ERR(pctl->pctl_dev);
	}

	pctl->regs_backup = devm_kzalloc(&pdev->dev,
		pctl->desc->banks * BANK_MEM_SIZE +  /* bank regs */
		pctl->desc->irq_banks * IRQ_MEM_SIZE + /* irq regs */
		4, /* power mode reg */
		GFP_KERNEL);

	pctl->chip = devm_kzalloc(&pdev->dev, sizeof(*pctl->chip), GFP_KERNEL);
	if (!pctl->chip) {
		ret = -ENOMEM;
		goto pinctrl_error;
	}

	last_pin = pctl->desc->pins[pctl->desc->npins - 1].pin.number;
	pctl->chip->owner = THIS_MODULE;
	pctl->chip->request = gpiochip_generic_request,
	pctl->chip->free = gpiochip_generic_free,
	pctl->chip->direction_input = sunxi_pinctrl_gpio_direction_input,
	pctl->chip->direction_output = sunxi_pinctrl_gpio_direction_output,
	pctl->chip->get = sunxi_pinctrl_gpio_get,
	pctl->chip->set = sunxi_pinctrl_gpio_set,
	pctl->chip->set_debounce = sunxi_pinctrl_gpio_set_debounce,
	pctl->chip->of_xlate = sunxi_pinctrl_gpio_of_xlate,
	pctl->chip->to_irq = sunxi_pinctrl_gpio_to_irq,
	pctl->chip->of_gpio_n_cells = 6,
	pctl->chip->can_sleep = false,
	pctl->chip->ngpio = round_up(last_pin + 1, PINS_PER_BANK) -
			    pctl->desc->pin_base;
	pctl->chip->label = dev_name(&pdev->dev);
	pctl->chip->parent = &pdev->dev;
	pctl->chip->base = pctl->desc->pin_base;

	ret = gpiochip_add(pctl->chip);
	if (ret)
		goto pinctrl_error;

	for (i = 0; i < pctl->desc->npins; i++) {
		const struct sunxi_desc_pin *pin = pctl->desc->pins + i;

		ret = gpiochip_add_pin_range(pctl->chip, dev_name(&pdev->dev),
					     pin->pin.number - pctl->desc->pin_base,
					     pin->pin.number, 1);
		if (ret)
			goto gpiochip_error;
	}

	clk = devm_clk_get(&pdev->dev, NULL);
	if (IS_ERR(clk)) {
		ret = PTR_ERR(clk);
		goto gpiochip_error;
	}

	ret = clk_prepare_enable(clk);
	if (ret)
		goto gpiochip_error;

	pctl->irq = devm_kcalloc(&pdev->dev,
				 pctl->desc->irq_banks,
				 sizeof(*pctl->irq),
				 GFP_KERNEL);
	if (!pctl->irq) {
		ret = -ENOMEM;
		goto clk_error;
	}

	for (i = 0; i < pctl->desc->irq_banks; i++) {
		pctl->irq[i] = platform_get_irq(pdev, i);
		if (pctl->irq[i] < 0) {
			ret = pctl->irq[i];
			goto clk_error;
		}
	}

	pctl->domain = irq_domain_add_linear(node,
					     pctl->desc->irq_banks * IRQ_PER_BANK,
					     &sunxi_pinctrl_irq_domain_ops,
					     pctl);
	if (!pctl->domain) {
		dev_err(&pdev->dev, "Couldn't register IRQ domain\n");
		ret = -ENOMEM;
		goto clk_error;
	}

	for (i = 0; i < (pctl->desc->irq_banks * IRQ_PER_BANK); i++) {
		int irqno = irq_create_mapping(pctl->domain, i);

		irq_set_chip_and_handler(irqno, &sunxi_pinctrl_edge_irq_chip,
					 handle_edge_irq);
		irq_set_chip_data(irqno, pctl);
	}

	for (i = 0; i < pctl->desc->irq_banks; i++) {
		/* Mask and clear all IRQs before registering a handler */
		unsigned bank_base = pctl->desc->irq_bank_base[i];

		writel(0, pctl->membase +
			sunxi_irq_ctrl_reg_from_bank(i, bank_base));
		writel(0xffffffff, pctl->membase +
			sunxi_irq_status_reg_from_bank(i, bank_base));

		irq_set_chained_handler_and_data(pctl->irq[i],
						 sunxi_pinctrl_irq_handler,
						 pctl);
	}

#ifdef CONFIG_DEBUG_FS
	sunxi_pinctrl_debugfs();
#endif

	sunxi_pinctrl_setup_debounce(pctl, node);

	dev_info(&pdev->dev, "initialized sunXi PIO driver\n");

	return 0;

clk_error:
	clk_disable_unprepare(clk);
gpiochip_error:
	gpiochip_remove(pctl->chip);
pinctrl_error:
	pinctrl_unregister(pctl->pctl_dev);
	return ret;
}

有几个重要的部分
1、sunxi_pctrl_ops

static const struct pinctrl_ops sunxi_pctrl_ops = {
	.dt_node_to_map		= sunxi_pctrl_dt_node_to_map,
	.dt_free_map		= sunxi_pctrl_dt_free_map,
	.get_groups_count	= sunxi_pctrl_get_groups_count,
	.get_group_name		= sunxi_pctrl_get_group_name,
	.get_group_pins		= sunxi_pctrl_get_group_pins,
};

sunxi_pctrl_dt_node_to_map可以查到

sunxi_pctrl_parse_muxsel_prop
sunxi_pctrl_parse_function_prop
sunxi_pctrl_find_pins_prop

看其中一个函数,就明白了,这里解析到设备树的allwinner,muxsel的对应值。其他的设备树值也在其他几个函数解析,这里不展开说。
static int sunxi_pctrl_parse_muxsel_prop(struct device_node *node,
u32 *muxsel)
{
int ret;

ret = of_property_read_u32(node, "muxsel", muxsel);
if (!ret)
	return ret;

return of_property_read_u32(node, "allwinner,muxsel", muxsel) ;

}

2、pctl->chip->set = sunxi_pinctrl_gpio_set
这一行定义了gpio设置的操作方法,其他的设置方向等也一起做了定义。
再往下看,终于看到操作寄存器了!


static void sunxi_pinctrl_gpio_set(struct gpio_chip *chip,
				unsigned offset, int value)
{
	struct sunxi_pinctrl *pctl = dev_get_drvdata(chip->parent);
	u32 reg = sunxi_data_reg(offset);
	u8 index = sunxi_data_offset(offset);
	unsigned long flags;
	u32 regval;

	raw_spin_lock_irqsave(&pctl->lock, flags);

	regval = readl(pctl->membase + reg);

	if (value)
		regval |= BIT(index);
	else
		regval &= ~(BIT(index));

	writel(regval, pctl->membase + reg);//寄存器层面操作

	raw_spin_unlock_irqrestore(&pctl->lock, flags);
}

看到这里的彦祖们应该能够理清楚pinctrl如何操作到寄存器的情况了,主要是通过pinctrl子系统找到设备树中的对应pin寄存器。这一部分只是举例说明,有兴趣的可以自行挖掘下。


总结

通过以上分析,主要是理清一下gpio口、pinctrl和设备树之间的关系,控制过程及原理,看问题可以更全面到位。

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Jason Yan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值