概述
以全志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.10archarmbootdtssun8iw11p1-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.10driverspinctrlsunxipinctrl-sun8iw11p1.c
linux-3.10driverspinctrlsunxipinctrl-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 failedn");
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 文件的主要作用就是:
- 驱动入口:postcore_initcall(sun8iw11p1_pio_init),该接口与module_init类似。
- 利用 pinctrl_pin_desc(通用)结构体 和 全志自定义 sunxi_desc_function 结构体,定义了所有 pin引脚 和 引脚功能(sunxi_desc_pin sun8iw11p1_pins[] 数组)。
- 与设备树文件匹配:"allwinner,sun8iw11p1-pinctrl" 名称。
- sun8iw11p1_pinctrl_probe 函数调用了 pinctrl-sunxi.c 文件的 sunxi_pinctrl_init函数,并传入sun8iw11p1_pins[] 数组,开始初始化流程。
-
pinctrl-sunxi.c 文件
从sunxi_pinctrl_init函数开始,该函数中主要做了几件事:
- 申请platform_device资源并初始化。
- 初始化 struct pinctrl_desc *pctrl_desc 结构体,并调用pinctrl_register()接口注册进pinctrl子系统。
- 初始化struct gpio_chip *chip结构体(☆关键结构体),并调用gpiochip_add()接口,向Gpiolib注册gpio_chip(一个该结构体代表一个控制器)。注册后内核的Gpiolib就有了控制引脚的能力(设置输入输出/读写高低电平),但是还无法感知CPU有多少引脚可用。
- 调用gpiochip_add_pin_range函数感知所有引脚,之后CPU就知道有多少pin引脚可用。
- 准备并使能clk时钟。
- 获取并注册中断资源,并创建irq号与硬件号的对应关系。
- 最后调用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: %dn", 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 drivern");
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 domainn");
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 %dn", pctl->irq[i]);
return ret;
}
}
#ifdef CONFIG_DEBUG_FS
sunxi_pinctrl_debugfs();
#endif
dev_info(&pdev->dev, "initialized sunXi PIO drivern");
return 0;
clk_error:
clk_disable_unprepare(clk);
gpiochip_error:
if (gpiochip_remove(pctl->chip))
dev_err(&pdev->dev, "failed to remove gpio chipn");
pinctrl_error:
pinctrl_unregister(pctl->pctl_dev);
return ret;
}
最后
以上就是秀丽金针菇为你收集整理的gpio子系统与pinctrl子系统三:pinctrl的全部内容,希望文章能够帮你解决gpio子系统与pinctrl子系统三:pinctrl所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复