pinctrl框架【转】

简介: 转自:http://www.cnblogs.com/kevinhwang/p/5703192.html pinctrl框架是linux系统为统一各SOC厂家pin管理,目的是为了减少SOC厂家系统移植工作量。

转自:http://www.cnblogs.com/kevinhwang/p/5703192.html

pinctrl框架是linux系统为统一各SOC厂家pin管理,目的是为了减少SOC厂家系统移植工作量。

通常通过设备树初始化pinctrl,并提供调用io接口,以下为全志A64平台的实例:

在drivers/pinctrl/sunxi/pinctrl-sun50iw1p1.c:

复制代码
 1 static const struct sunxi_desc_pin sun50iw1p1_pins[] = {
 2 SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 0),  3 SUNXI_FUNCTION(0x0, "gpio_in"),  4 SUNXI_FUNCTION(0x1, "gpio_out"),  5 SUNXI_FUNCTION(0x2, "uart2"), /*TX*/  6 SUNXI_FUNCTION(0x3, "vdevice"), /*vdevice for test */  7 SUNXI_FUNCTION(0x4, "jtag0"), /*MS0*/  8 SUNXI_FUNCTION(0x7, "io_disabled"),  9 SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 0)), /*PB_EINT0 */ 10 SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 1), 11 SUNXI_FUNCTION(0x0, "gpio_in"), 12 SUNXI_FUNCTION(0x1, "gpio_out"), 13 SUNXI_FUNCTION(0x2, "uart2"), /*RX*/ 14 SUNXI_FUNCTION(0x3, "vdevice"), /*vdevice for test */ 15 SUNXI_FUNCTION(0x4, "jtag0"), /*CK0*/ 16 SUNXI_FUNCTION(0x5, "sim0"), /*PWREN*/ 17 SUNXI_FUNCTION(0x7, "io_disabled"), 18 SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 1)), /*PB_EINT1 */ 19  ...... 20  ...... 21  ...... 22 }; 23 24 static const struct sunxi_pinctrl_desc sun50iw1p1_pinctrl_data = { 25 .pins = sun50iw1p1_pins, 26 .npins = ARRAY_SIZE(sun50iw1p1_pins), 27 .irq_banks = 3, 28 }; 29 30 static int sun50iw1p1_pinctrl_probe(struct platform_device *pdev) 31 { 32 pr_warn("[%s][%d]\n", __func__, __LINE__); 33 return sunxi_pinctrl_init(pdev, 34 &sun50iw1p1_pinctrl_data); 35 } 36 37 static struct of_device_id sun50iw1p1_pinctrl_match[] = { 38 { .compatible = "allwinner,sun50i-pinctrl", }, 39  {} 40 }; 41 MODULE_DEVICE_TABLE(of, sun50iw1p1_pinctrl_match); 42 43 static struct platform_driver sun50iw1p1_pinctrl_driver = { 44 .probe = sun50iw1p1_pinctrl_probe, 45 .driver = { 46 .name = "sun50i-pinctrl", 47 .owner = THIS_MODULE, 48 .of_match_table = sun50iw1p1_pinctrl_match, 49  }, 50 }; 51 static int __init sun50iw1p1_pio_init(void) 52 { 53 int ret; 54 ret = platform_driver_register(&sun50iw1p1_pinctrl_driver); 55 if (IS_ERR_VALUE(ret)) { 56 pr_debug("register sun50i pio controller failed\n"); 57 return -EINVAL; 58 } 59 return 0; 60 } 61 postcore_initcall(sun50iw1p1_pio_init);
复制代码

start_kernel----rest_init----kernel_init----kernel_init_freeable----do_basic_setup----do_initcalls调用postcore_initcall。

注册平台驱动,匹配设备最后调用sunxi_pinctrl_init(pdev, &sun50iw1p1_pinctrl_data);

sun50iw1p1_pins为全志平台的PIN IO和IRQ的定义,里面定义引脚名字name,复用功能muxval,中断号irqnum。

 

忽略部分代码的drivers/pinctrl/sunxi/pinctrl-sunxi.c:

复制代码
  1 int sunxi_pinctrl_init(struct platform_device *pdev,
  2                const struct sunxi_pinctrl_desc *desc)  3 {  4 struct device_node *node = pdev->dev.of_node;  5 struct pinctrl_desc *pctrl_desc;  6 struct pinctrl_pin_desc *pins;  7 struct sunxi_pinctrl *pctl;  8 struct resource *res;  9 int i, ret, last_pin;  10 struct clk *clk;  11 pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL);  12  13  platform_set_drvdata(pdev, pctl);  14  15 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);  16 pctl->membase = devm_ioremap_resource(&pdev->dev, res); //返回设备的MEM资源的虚拟地址  17  18  19 pctl->dev = &pdev->dev;  20 pctl->desc = desc;  21  22 pctl->irq_array = devm_kcalloc(&pdev->dev, IRQ_PER_BANK * pctl->desc->irq_banks,  23 sizeof(*pctl->irq_array), GFP_KERNEL);  24  25 //把sunxi_pinctrl_desc的内容全部映射到sunxi_pinctrl的成员(sunxi_pinctrl_group、irq_array和sunxi_pinctrl_function)  26 ret = sunxi_pinctrl_build_state(pdev);  27  28 pins = devm_kzalloc(&pdev->dev, pctl->desc->npins * sizeof(*pins), GFP_KERNEL);  29  30 for (i = 0; i < pctl->desc->npins; i++)  31 pins[i] = pctl->desc->pins[i].pin;  32  33 //注册pctldev,这里把平台硬件相关函数向上映射到pinctrl子系统  34 pctrl_desc = devm_kzalloc(&pdev->dev, sizeof(*pctrl_desc), GFP_KERNEL);  35  36 //confops、pctlops和pmxops都是直接和硬件相关,新设备配置PIN需要用到  37 pctrl_desc->name = dev_name(&pdev->dev);  38 pctrl_desc->owner = THIS_MODULE;  39 pctrl_desc->pins = pins;  40 pctrl_desc->npins = pctl->desc->npins;  41 pctrl_desc->confops = &sunxi_pconf_ops;  //pin config operations vtable  42 pctrl_desc->pctlops = &sunxi_pctrl_ops; //pin control operation vtable  43 pctrl_desc->pmxops = &sunxi_pmx_ops; //pinmux operations vtable  44  45 //把描述表的name和number加入radix_tree,分配pinctrl_dev并把加入双链表,这里没有创建新的pinctrl设备,也没有state  46 pctl->pctl_dev = pinctrl_register(pctrl_desc, &pdev->dev, pctl);  47  48 //注册gpio子系统,把平台硬件相关函数向上映射到gpio子系统  49 pctl->chip = devm_kzalloc(&pdev->dev, sizeof(*pctl->chip), GFP_KERNEL);  50  51 last_pin = pctl->desc->pins[pctl->desc->npins - 1].pin.number;  52 pctl->chip->owner = THIS_MODULE;  53 pctl->chip->request = sunxi_pinctrl_gpio_request,  54 pctl->chip->free = sunxi_pinctrl_gpio_free,  55 pctl->chip->direction_input = sunxi_pinctrl_gpio_direction_input,  56 pctl->chip->direction_output = sunxi_pinctrl_gpio_direction_output,  57 pctl->chip->get = sunxi_pinctrl_gpio_get,  58 pctl->chip->set = sunxi_pinctrl_gpio_set,  59 pctl->chip->set_debounce = sunxi_pinctrl_gpio_set_debounce,  60 pctl->chip->of_xlate = sunxi_pinctrl_gpio_of_xlate,  61 pctl->chip->to_irq = sunxi_pinctrl_gpio_to_irq,  62 pctl->chip->of_gpio_n_cells = 6,  63 pctl->chip->can_sleep = false,  64 pctl->chip->ngpio = round_up(last_pin + 1, PINS_PER_BANK) - pctl->desc->pin_base;  65 pctl->chip->label = dev_name(&pdev->dev);  66 pctl->chip->dev = &pdev->dev;  67 pctl->chip->base = pctl->desc->pin_base;  68  69 ret = gpiochip_add(pctl->chip); //把gpio_chip(A64有两个chip)加入双链表  70  71 for (i = 0; i < pctl->desc->npins; i++) {  72 const struct sunxi_desc_pin *pin = pctl->desc->pins + i;  73 //注册gpio_ranges,并加入双链表  74 ret = gpiochip_add_pin_range(pctl->chip, dev_name(&pdev->dev),  75 pin->pin.number - pctl->desc->pin_base,  76 pin->pin.number, 1);  77  }  78  79 clk = devm_clk_get(&pdev->dev, NULL);  80  81 ret = clk_prepare_enable(clk);  82 if (ret)  83 goto gpiochip_error;  84  85 pctl->irq = devm_kcalloc(&pdev->dev, 86 pctl->desc->irq_banks, 87 sizeof(*pctl->irq), 88 GFP_KERNEL); 89 90 for (i = 0; i < pctl->desc->irq_banks; i++) { 91 pctl->irq[i] = platform_get_irq(pdev, i); //获取中断资源 92 } 93 94 pctl->domain = irq_domain_add_linear(node, pctl->desc->irq_banks * IRQ_PER_BANK, 95 &irq_domain_simple_ops, NULL); //把pinctrl加入线性中断域,pin中断都域中搜索 96 97 98 for (i = 0; i < (pctl->desc->irq_banks * IRQ_PER_BANK); i++) { 99 int irqno = irq_create_mapping(pctl->domain, i); //获取线性中断域的irq号 100 101 irq_set_chip_and_handler(irqno, &sunxi_pinctrl_edge_irq_chip, 102 handle_edge_irq); //sunxi_pinctrl_edge_irq_chip为全志底层irq_chip操作 103 irq_set_chip_data(irqno, pctl); 104 }; 105 106 for (i = 0; i < pctl->desc->irq_banks; i++) { 107 /* Mask and clear all IRQs before registering a handler */ 108 writel(0, pctl->membase + sunxi_irq_ctrl_reg_from_bank(i)); 109 writel(0xffffffff, pctl->membase + sunxi_irq_status_reg_from_bank(i)); 110 if(pctl->desc->pin_base >= PL_BASE){ 111 ret = devm_request_irq(&pdev->dev, pctl->irq[i], sunxi_pinctrl_irq_handler, 112 IRQF_SHARED | IRQF_NO_SUSPEND, "PIN_GRP", pctl); 113 //同一个bank的gpio中断均共享一个中断,在sunxi_pinctrl_irq_handler通过gpio的中断号 114 //启动对应generic_handle_irq 115 }else{ 116 ret = devm_request_irq(&pdev->dev, pctl->irq[i], sunxi_pinctrl_irq_handler, 117 IRQF_SHARED, "PIN_GRP", pctl); //同上 118 } 119 } 120 121 return 0; 122 }
复制代码

 

设备树实例(一)中的:

复制代码
 1      soc@01c00000 {
 2          compatible = "simple-bus";  3 #address-cells = <0x2>;  4 #size-cells = <0x2>;  5  ranges;  6 device_type = "soc";  7  8  pinctrl@01c20800 {  9 compatible = "allwinner,sun50i-pinctrl"; 10 reg = <0x0 0x1c20800 0x0 0x400>; 11 interrupts = <0x0 0xb 0x4 0x0 0x11 0x4 0x0 0x15 0x4>; 12 device_type = "pio"; 13 clocks = <0xa>; 14 gpio-controller; 15 interrupt-controller; 16 #interrupt-cells = <0x2>; 17 #size-cells = <0x0>; 18 #gpio-cells = <0x6>; 19 linux,phandle = <0x30>; 20 phandle = <0x30>; 21 22 uart0@0 { 23 allwinner,pins = "PB8", "PB9"; 24 allwinner,pname = "uart0_tx", "uart0_rx"; 25 allwinner,function = "uart0"; 26 allwinner,muxsel = <0x4>; 27 allwinner,drive = <0x1>; 28 allwinner,pull = <0x1>; 29 linux,phandle = <0x19>; 30 phandle = <0x19>; 31  }; 32 33 uart0@1 { 34 allwinner,pins = "PB8", "PB9"; 35 allwinner,function = "io_disabled"; 36 allwinner,muxsel = <0x7>; 37 allwinner,drive = <0x1>; 38 allwinner,pull = <0x1>; 39 linux,phandle = <0x1a>; 40 phandle = <0x1a>; 41  }; 42 43  ...... 44  ...... 45  ...... 46  }; 47 48  uart@01c28000 { 49 compatible = "allwinner,sun50i-uart"; 50 device_type = "uart0"; 51 reg = <0x0 0x1c28000 0x0 0x400>; 52 interrupts = <0x0 0x0 0x4>; 53 clocks = <0x18>; 54 pinctrl-names = "default", "sleep"; 55 pinctrl-0 = <0x19>; 56 pinctrl-1 = <0x1a>; 57 uart0_port = <0x0>; 58 uart0_type = <0x2>; 59 status = "disabled"; 60 };
复制代码

pinctrl-names = "default", "sleep";的两种状态的phandle对应于pinctrl-0(uart0@0)和pinctrl-1(uart0@1),uart0@0里面的配置是全志硬件平台相关,跟confops、pctlops和pmxops相关。

 

如何开启设备树对应的pin配置?

一般在driver程序里面调用devm_pinctrl_get_select_default就可以。

定义在linux3-10/include/pintrl/consumer.h:

复制代码
 1 static inline struct pinctrl * __must_check devm_pinctrl_get_select(
 2                     struct device *dev, const char *name)  3 {  4 struct pinctrl *p;  5 struct pinctrl_state *s;  6 int ret;  7  8 //通过device获取phandle,若是新设备则会调用创建head_list,找到pinctrl父设备sun50i-pinctrl ,解析设备树,  9 //创建states,获取硬件相关配置,最后add devices。 10 p = devm_pinctrl_get(dev); 11 if (IS_ERR(p)) 12 return p; 13 14 s = pinctrl_lookup_state(p, name); 15 if (IS_ERR(s)) { 16  devm_pinctrl_put(p); 17 return ERR_CAST(s); 18  } 19 //设置state(默认是DEFAULT)从设备树对应的配置 20 ret = pinctrl_select_state(p, s); 21 if (ret < 0) { 22  devm_pinctrl_put(p); 23 return ERR_PTR(ret); 24  } 25 26 return p; 27 }
复制代码

 

调用IO口或者中断可以参考linux-3.10/drivers/pinctrl/sunxi/pinctrl-sunxi-test.c

复制代码
 1 #include <linux/io.h>
 2 #include <linux/clk.h>
 3 #include <linux/gpio.h>
 4 #include <linux/interrupt.h>
 5 #include <linux/module.h>
 6 #include <linux/of.h>  7 #include <linux/of_platform.h>  8 #include <linux/of_address.h>  9 #include <linux/of_device.h> 10 #include <linux/of_gpio.h> 11 #include <linux/pinctrl/consumer.h> 12 #include <linux/platform_device.h> 13 #include <linux/slab.h> 14 #include <linux/sys_config.h> 15 #include <linux/string.h> 16 17 static struct of_device_id sunxi_pinctrl_test_match[] = { 18 { .compatible = "allwinner,sun50i-vdevice"}, 19 }; //这里要在dts文件建立compatible = "allwinner,sun50i-vdevice"的node才能匹配driver 20 21 static irqreturn_t test_sunxi_pinctrl_irq_handler(int irq, void *dev_id) 22 { 23 pr_warn("[%s] handler for test pinctrl eint api.\n", __func__); 24  disable_irq_nosync(irq); 25 return IRQ_HANDLED; 26 27 } 28 29 30 static int sunxi_pinctrl_test_probe(struct platform_device *pdev) 31 { 32 struct gpio_config config; 33 unsigned int ret; 34 struct device_node *node; 35 36 int req_irq_status; 37 int req_status; 38 int virq; 39 40 node=of_find_node_by_type(NULL, "vdevice"); //通过属性device_type = "vdevice"找到设备树节点 41 if(!node){ 42 printk("find node\n"); 43  } 44 ret = of_get_named_gpio_flags(node, "test-gpios", 0, (enum of_gpio_flags *)&config); 45 //找到设备树属性test-gpios,解析GPIO配置,这步是重点(不同硬件平台不一定相同) 46 if (!gpio_is_valid(ret)) { 47 return -EINVAL; 48  } 49 printk("config: gpio=%d mul=%d drive=%d pull=%d data=%d\n" 50  , config.gpio 51  , config.mul_sel 52  , config.drv_level 53  , config.pull 54  , config.data); 55 req_status = gpio_request(config.gpio, NULL); 56 if (0 != req_status) 57 return -EINVAL; 58 virq = gpio_to_irq(config.gpio); 59 if (IS_ERR_VALUE(virq)) { 60 pr_warn("gpio[%d]get virq[%d] failed!\n", config.gpio, virq); 61 return -EINVAL; 62  } 63 req_irq_status = devm_request_irq(&pdev->dev, virq, 64  test_sunxi_pinctrl_irq_handler, 65 IRQF_TRIGGER_LOW, "GPIO_EINT", NULL); 66 if (IS_ERR_VALUE(req_irq_status)) { 67 pr_warn("request irq failed !\n"); 68 return -EINVAL; 69  } 70 return 0; 71 } 72 73 static struct platform_driver sunxi_pinctrl_test_driver = { 74 .probe = sunxi_pinctrl_test_probe, 75 .driver = { 76 .name = "vdevice", 77 .owner = THIS_MODULE, 78 .of_match_table = sunxi_pinctrl_test_match, 79 }, 80 }; 81 82 static int __init sunxi_pinctrl_test_init(void) 83 { 84 int ret; 85 ret = platform_driver_register(&sunxi_pinctrl_test_driver); 86 if (IS_ERR_VALUE(ret)) { 87 pr_warn("register sunxi pinctrl platform driver failed\n"); 88 return -EINVAL; 89 } 90 return 0; 91 } 92 late_initcall(sunxi_pinctrl_test_init); 93 94 MODULE_AUTHOR("Huangshr<huangshr@allwinnertech.com"); 95 MODULE_DESCRIPTION("Allwinner SUNXI Pinctrl driver test"); 96 MODULE_LICENSE("GPL");
复制代码

 

一般应用是

【作者】 张昺华
【新浪微博】 张昺华--sky
【twitter】 @sky2030_
【facebook】 张昺华 zhangbinghua
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
目录
相关文章
GPIO简介
本文所述IO(Input/Output PAD)主要是指集成在CMOS芯片中的连接模块,其负责芯片引脚的外部信号与芯片内部的数字/模拟模块的交互,它是一颗完整芯片设计中不可或缺的组成部分。 文章首先根据IO的使用类型,介绍了IO的分类;紧接着重点介绍了GPIO的输入/输出功能和模式...
GPIO简介
|
3月前
|
芯片
STM32 GPIO工作原理详解
STM32 GPIO工作原理详解
|
8月前
|
XML 数据格式 SoC
深入理解AMBA总线(十)AHB Bus Matrix以及AHB的局限性
深入理解AMBA总线(十)AHB Bus Matrix以及AHB的局限性
189 0
|
9月前
|
API SoC
pinctrl和gpio子系统
pinctrl和gpio子系统
84 0
|
11天前
|
Linux API 芯片
嵌入式Linux中pinctrl 子系统和 gpio 子系统分析
嵌入式Linux中pinctrl 子系统和 gpio 子系统分析
43 0
|
8月前
|
SoC 内存技术
深入理解AMBA总线(五)AHB-lite Transfer进阶
深入理解AMBA总线(五)AHB-lite Transfer进阶
173 0
|
8月前
|
传感器
STM32F103C8T6使用HAL库驱动GY906
STM32F103C8T6使用HAL库驱动GY906
|
9月前
|
存储 算法 芯片
IMX6ULL的I2C驱动详细分析
IMX6ULL的I2C驱动详细分析
161 0
IMX6ULL的I2C驱动详细分析
|
10月前
|
移动开发 API
STM32使用HAL库操作GPIO
使用HAL库的优点在于不用手动添加初始化的代码了,CubeMX会根据软件设置自动生成
143 0
|
11月前
|
Linux 芯片
GPIO和Pinctrl子系统的使用
GPIO和Pinctrl子系统的使用
63 0