• pinctrl框架【转】


    转自: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]
    ", __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
    ");
    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.
    ", __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
    ");
    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
    "
    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!
    ", 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 !
    ");
    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
    ");
    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");
    复制代码

    一般应用是

  • 相关阅读:
    使用C#直接修改表结构(添加列,删除列)【MS SQL SEVER】
    Nuget-ConsoleExtClass给控制台添加颜色
    Thread线程Join()的使用
    C#将List集合类转换成DataTable-帮助类
    C#动态拼接Linq
    C#使用AutoMapper
    go GOPROXY=http://goproxy.io 设置
    mysql5.6切到5.7(阿里云RDS换到自建库)
    vue学习之----如何在谷歌浏览器中使用vue调试工具
    vue学习之----兄弟组件之间通信方式
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/7717286.html
Copyright © 2020-2023  润新知