gpio子系统与pinctrl子系统三:pinctrl

以全志A40i芯片+Linux-3.10内核为例分析。

对于A40i+Linux-3.10代码中已经没有单独的drivers/gpio/gpio-*.c驱动。主要是因为gpio驱动和

pinctrl驱动有着紧密联系,所以在pinctrl驱动里会同时向gpio子系统注册struct gpio_chip

因此对于A40i而言,我们只要分析pinctrl驱动就够了。

文件与代码

此处主要针对于平台的gpio控制器,一般这部分的代码都是由芯片或者方案厂家完成。

  • 设备端(device),主要是设备树文件:

  linux-3.10\arch\arm\boot\dts\sun8iw11p1-pinctrl.dtsi        (每个平台的文件各有不同)

soc@01c00000{
		pio: pinctrl@01c20800 {
			compatible = "allwinner,sun8iw11p1-pinctrl";
			reg = <0x0 0x01c20800 0x0 0x400>;
			interrupts = <GIC_SPI 28 IRQ_TYPE_LEVEL_HIGH>;
			device_type = "pio";
			clocks = <&clk_pio>;
			gpio-controller;
			interrupt-controller;
			#interrupt-cells = <2>;
			#size-cells = <0>;
			#gpio-cells = <6>;
            
            ......
            	uart0_pins_b: uart0@1 {
				allwinner,pins = "PB22", "PB23";
				allwinner,function = "io_disabled";
				allwinner,muxsel = <7>;
				allwinner,drive = <1>;
				allwinner,pull = <1>;
			};
            ......
        }
  • 在驱动端(driver)文件:

linux-3.10\drivers\pinctrl\sunxi\pinctrl-sun8iw11p1.c

linux-3.10\drivers\pinctrl\sunxi\pinctrl-sunxi.c

  • pinctrl-sun8iw11p1.c 文件

<1> —>  入口:postcore_initcall(sun8iw11p1_pio_init);        // 与module_init类似的调用接口  

static int __init sun8iw11p1_pio_init(void)
{
	int ret;
	ret = platform_driver_register(&sun8iw11p1_pinctrl_driver);
	if (IS_ERR_VALUE(ret)) {
		pr_debug("register sun8iw11p1 pio controller failed\n");
		return -EINVAL;
	}
	return 0;
}

<2> —>关注其中的 sun8iw11p1_pinctrl_driver 结构体。

static struct platform_driver sun8iw11p1_pinctrl_driver = {
	.probe	= sun8iw11p1_pinctrl_probe,
	.driver	= {
		.name		= "sun8iw11p1-pinctrl",
		.owner		= THIS_MODULE,
		.of_match_table	= sun8iw11p1_pinctrl_match,
	},
};

其中的 sun8iw11p1_pinctrl_match 中的 "allwinner,sun8iw11p1-pinctrl"与设备树中的匹配一致.

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

<3> —> 关注其中的 sun8iw11p1_pinctrl_probe 回调函数。

static int sun8iw11p1_pinctrl_probe(struct platform_device *pdev)
{
	return sunxi_pinctrl_init(pdev,
				  &sun8iw11p1_pinctrl_data);
}

该回调函数只是二次封装了下 pinctrl-sunxi.c 文件中的 sunxi_pinctrl_init 函数。

而 sunxi_pinctrl_init 函数有个参数 const struct sunxi_pinctrl_desc *desc 在此处定义:

static const struct sunxi_pinctrl_desc sun8iw11p1_pinctrl_data = {
	.pins = sun8iw11p1_pins,
	.npins = ARRAY_SIZE(sun8iw11p1_pins),
	.pin_base = 0,
	.irq_banks = 1,
};

<4> —> 关注其中的 sun8iw11p1_pins结构体数组。

static const struct sunxi_desc_pin sun8iw11p1_pins[] = {
	SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 0),
		  SUNXI_FUNCTION(0x0, "gpio_in"),
		  SUNXI_FUNCTION(0x1, "gpio_out"),
		  SUNXI_FUNCTION(0x2, "emac0"),		/* ERXD3 */
		  SUNXI_FUNCTION(0x3, "spi1"),		/* CS0 */
		  SUNXI_FUNCTION(0x4, "uart2"),		/* RTS */
		  SUNXI_FUNCTION(0x5, "gmac0"),		/* GRXD3 */
		  SUNXI_FUNCTION(0x7, "io_disabled")),
	SUNXI_PIN(SUNXI_PINCTRL_PIN(A, 1),
		  SUNXI_FUNCTION(0x0, "gpio_in"),
		  SUNXI_FUNCTION(0x1, "gpio_out"),
		  SUNXI_FUNCTION(0x2, "emac0"),		/* ERXD2 */
		  SUNXI_FUNCTION(0x3, "spi1"),		/* CLK */
		  SUNXI_FUNCTION(0x4, "uart2"),		/* CTS */
		  SUNXI_FUNCTION(0x5, "gmac0"),		/* GRXD2 */
		  SUNXI_FUNCTION(0x7, "io_disabled")),
        
        ......
    };

该数组基本按照芯片手册上的定义,将PA到PI的引脚和功能都定义了一遍。

<5> —> 另外就是该文件中的sunxi_pinctrl_desc结构体: 

struct sunxi_pinctrl_desc {
	const struct sunxi_desc_pin	*pins;
	int				npins;
	unsigned			pin_base;
	unsigned			irq_banks;
};

赋值的就是“sun8iw11p1_pins”数组。

static const struct sunxi_pinctrl_desc sun8iw11p1_pinctrl_data = {
	.pins = sun8iw11p1_pins,
	.npins = ARRAY_SIZE(sun8iw11p1_pins),
	.pin_base = 0,
	.irq_banks = 1,
};
  • 在该sunxi_pinctrl_desc结构体中包含了一个结构体变量: struct pinctrl_pin_desc

       该变量的定义类型就是所有pinctrl驱动通用的 “pinctrl_pin_desc”结构体(☆关键结构体)

/**
 * struct pinctrl_pin_desc - boards/machines provide information on their
 * pins, pads or other muxable units in this struct
 * @number: unique pin number from the global pin number space
 * @name: a name for this pin
 * @drv_data: driver-defined per-pin data. pinctrl core does not touch this
 */
struct pinctrl_pin_desc {
	unsigned number;
	const char *name;
	void *drv_data;
};

       引用一段网友的解释:

* struct pinctrl_pin_desc用于描述一个引脚,例如:

对于PA0引脚,成员number=0,成员name="PA0",类似一个map(key=0,value="PA0");

  • 在该sunxi_pinctrl_desc结构体中包含了另一个结构体变量: struct sunxi_desc_function

        再引用一段网友的解释: 

struct sunxi_desc_function用于描述一个引脚功能,对于PA0引脚,共有4个function(gpio_in、gpio_out、uart2[看来只是功能分组,并没有指定具体是哪一个引脚功能]、jtag),第一个function的muxval=0x0(寄存器里的偏移值),name="gpio_in";

因此 pinctrl-sun8iw11p1.c 文件的主要作用就是:

  1.  驱动入口:postcore_initcall(sun8iw11p1_pio_init),该接口与module_init类似。
  2. 利用 pinctrl_pin_desc(通用)结构体 和 全志自定义 sunxi_desc_function 结构体,定义了所有 pin引脚 和 引脚功能(sunxi_desc_pin sun8iw11p1_pins[] 数组)。
  3. 与设备树文件匹配:"allwinner,sun8iw11p1-pinctrl" 名称。
  4. sun8iw11p1_pinctrl_probe 函数调用了 pinctrl-sunxi.c 文件的 sunxi_pinctrl_init函数,并传入sun8iw11p1_pins[] 数组,开始初始化流程。

  • pinctrl-sunxi.c 文件

        从sunxi_pinctrl_init函数开始,该函数中主要做了几件事: 

  1. 申请platform_device资源并初始化。
  2. 初始化 struct pinctrl_desc *pctrl_desc 结构体,并调用pinctrl_register()接口注册进pinctrl子系统。
  3. 初始化struct gpio_chip *chip结构体(☆关键结构体),并调用gpiochip_add()接口,向Gpiolib注册gpio_chip(一个该结构体代表一个控制器)。注册后内核的Gpiolib就有了控制引脚的能力(设置输入输出/读写高低电平),但是还无法感知CPU有多少引脚可用。
  4. 调用gpiochip_add_pin_range函数感知所有引脚,之后CPU就知道有多少pin引脚可用。
  5. 准备并使能clk时钟。
  6. 获取并注册中断资源,并创建irq号与硬件号的对应关系。
  7. 最后调用sunxi_pinctrl_debugfs(),创建debugfs接口。

        该文件中的其他部分,基本上是内部函数与debugfs的实现。


// A40i板子GPIO控制函数入口, sunxi_pinctrl初始化函数
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;// pinctrl_desc用于表示一个pin控制器,可通过[devm_]
	struct pinctrl_pin_desc *pins;
	struct sunxi_pinctrl *pctl;
	struct resource *res;
	int i, ret, last_pin;
	struct clk *clk;
	pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL);
	if (!pctl)
		return -ENOMEM;
	platform_set_drvdata(pdev, pctl);

	spin_lock_init(&pctl->lock);
    
    // 1: platform_device 
	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
	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;

	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;
    
    // 2: pctrl_desc 
    // 初始化pctrl_desc结构体
	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;

	if (pctl->desc->pin_base < PL_BASE)
		pctrl_desc->name = SUNXI_PINCTRL;
	else
		pctrl_desc->name = SUNXI_R_PINCTRL;

	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;
	pctrl_desc->pmxops =  &sunxi_pmx_ops;

    // 注册进pinctrl子系统
	pctl->pctl_dev = pinctrl_register(pctrl_desc,
					  &pdev->dev, pctl);
	if (!pctl->pctl_dev) {
		dev_err(&pdev->dev, "couldn't register pinctrl driver\n");
		return -EINVAL;
	}
    
    // 3: gpio_chip 
    // 初始化gpio_chip结构体
	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 = sunxi_pinctrl_gpio_request,
	pctl->chip->free = sunxi_pinctrl_gpio_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->dev = &pdev->dev;
	pctl->chip->base = pctl->desc->pin_base;

    // 调用gpiochip_add()接口,向Gpiolib注册gpio_chip 
    // 将struct gpio_chip注册给gpio子系统后,内核就有了控制引脚的能力(设置输入输出/读写高低电平)
    // 但是还无法感知CPU有多少引脚可用.
	ret = gpiochip_add(pctl->chip);
	if (ret)
		goto pinctrl_error;
    
    //4: 感知所有引脚
	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相关
	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,
					     &irq_domain_simple_ops,
					     NULL);
	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号与硬件号的对应关系

		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 */
		writel(0, pctl->membase + sunxi_irq_ctrl_reg_from_bank(i));
		writel(0xffffffff, pctl->membase + sunxi_irq_status_reg_from_bank(i));
		if(pctl->desc->pin_base >= PL_BASE){
			ret = devm_request_irq(&pdev->dev, pctl->irq[i], sunxi_pinctrl_irq_handler,
						       IRQF_SHARED | IRQF_NO_SUSPEND, "PIN_GRP", pctl);
		}else{
			ret = devm_request_irq(&pdev->dev, pctl->irq[i], sunxi_pinctrl_irq_handler,
						       IRQF_SHARED, "PIN_GRP", pctl);
		}
		if (IS_ERR_VALUE(ret)) {
				pr_err("unable to request eint irq %d\n", pctl->irq[i]);
				return ret;
		}
	}

#ifdef CONFIG_DEBUG_FS
	sunxi_pinctrl_debugfs();
#endif

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

	return 0;

clk_error:
	clk_disable_unprepare(clk);
gpiochip_error:
	if (gpiochip_remove(pctl->chip))
		dev_err(&pdev->dev, "failed to remove gpio chip\n");
pinctrl_error:
	pinctrl_unregister(pctl->pctl_dev);
	return ret;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值