(转)Linux芯片级移植与底层驱动(基于3.7.4内核)(二)
5. DEBUG_LL和EARLY_PRINTK在Linux启动的早期,console驱动还没有投入运行。当我们把Linux移植到一个新的SoC的时候,工程师一般非常需要早期就可以执行printk()功能以跟踪调试启动过程。内核的DEBUG_LL和EARLY_PRINTK选项为我们提供了这样的支持。而在Bootloader引导内核执行的bootargs中,则需要使能earlyprintk选项。
为了让DEBUG_LL和EARLY_PRINTK可以运行,Linux内核中需实现早期解压过程打印需要的putc()和后续的addruart、senduart和waituart等宏。以CSR SiRFprimaII为例,putc()的实现位于arch/arm/mach-prima2/include/mach/uncompress.h:
22static __inline__ void putc(char c)
23{
24 /*
25 * during kernel decompression, all mappings are flat:
26 * virt_addr == phys_addr
27 */
28 while (__raw_readl((void __iomem *)SIRFSOC_UART1_PA_BASE +SIRFSOC_UART_TXFIFO_STATUS)
29 &SIRFSOC_UART1_TXFIFO_FULL)
30 barrier();
31
32 __raw_writel(c, (void __iomem *)SIRFSOC_UART1_PA_BASE +SIRFSOC_UART_TXFIFO_DATA);
33}
由于解压过程中,MMU还没有初始化,所以这个时候的打印是直接往UART端口FIFO对应的物理地址丢打印字符。
addruart、senduart和waituart等宏的实现位于每个SoC对应的MACHINE代码目录的include/mach/debug-macro.S,SiRFprimaII的实现mach-prima2/include/mach/debug-macro.S如下:
12 .macro addruart, rp, rv, tmp
13 ldr \rp,=SIRFSOC_UART1_PA_BASE @physical
14 ldr \rv,=SIRFSOC_UART1_VA_BASE @ virtual
15 .endm
16
17 .macro senduart,rd,rx
18 str \rd, [\rx,#SIRFSOC_UART_TXFIFO_DATA]
19 .endm
20
21 .macro busyuart,rd,rx
22 .endm
23
24 .macro waituart,rd,rx
251001: ldr \rd, [\rx,#SIRFSOC_UART_TXFIFO_STATUS]
26 tst \rd,#SIRFSOC_UART1_TXFIFO_EMPTY
27 beq 1001b
28 .endm
其中的senduart完成了往UART的FIFO丢打印字符的过程。waituart则相当于一个流量握手,等待FIFO为空。这些宏最终会被内核的arch/arm/kernel/debug.S引用。
6. GPIO驱动在drivers/gpio下实现了通用的基于gpiolib的GPIO驱动,其中定义了一个通用的用于描述底层GPIO控制器的gpio_chip结构体,并要求具体的SoC实现gpio_chip结构体的成员函数,最后透过gpiochip_add()注册gpio_chip。
gpio_chip结构体封装了底层的硬件的GPIO enable/disable等操作,它定义为:
94struct gpio_chip {
95 const char *label;
96 struct device *dev;
97 struct module *owner;
98
99 int (*request)(struct gpio_chip *chip,
100 unsigned offset);
101 void (*free)(struct gpio_chip *chip,
102 unsigned offset);
103
104 int (*direction_input)(struct gpio_chip *chip,
105 unsigned offset);
106 int (*get)(struct gpio_chip *chip,
107 unsigned offset);
108 int (*direction_output)(structgpio_chip *chip,
109 unsigned offset, int value);
110 int (*set_debounce)(struct gpio_chip *chip,
111 unsigned offset, unsigned debounce);
112
113 void (*set)(struct gpio_chip *chip,
114 unsigned offset, int value);
115
116 int (*to_irq)(struct gpio_chip *chip,
117 unsigned offset);
118
119 void (*dbg_show)(struct seq_file *s,
120 struct gpio_chip *chip);
121 int base;
122 u16 ngpio;
123 const char *const*names;
124 unsigned can_sleep:1;
125 unsigned exported:1;
126
127#if defined(CONFIG_OF_GPIO)
128 /*
129 * If CONFIG_OF is enabled, then all GPIOcontrollers described in the
130 * device tree automatically may have an OF translation
131 */
132 struct device_node *of_node;
133 int of_gpio_n_cells;
134 int (*of_xlate)(struct gpio_chip *gc,
135 const structof_phandle_args *gpiospec, u32 *flags);
136#endif
137};
透过这层封装,每个具体的要用到GPIO的设备驱动都使用通用的GPIO API来操作GPIO,这些API主要用于GPIO的申请、释放和设置:
intgpio_request(unsigned gpio, const char *label);
voidgpio_free(unsigned gpio);
intgpio_direction_input(unsigned gpio);
intgpio_direction_output(unsigned gpio, int value);
intgpio_set_debounce(unsigned gpio, unsigned debounce);
intgpio_get_value_cansleep(unsigned gpio);
voidgpio_set_value_cansleep(unsigned gpio, int value);
intgpio_request_one(unsigned gpio, unsigned long flags, const char *label);
intgpio_request_array(const struct gpio *array, size_t num);
voidgpio_free_array(const struct gpio *array, size_t num);
intdevm_gpio_request(struct device *dev, unsigned gpio, const char *label);
intdevm_gpio_request_one(struct device *dev, unsigned gpio,
unsigned long flags,const char *label);
voiddevm_gpio_free(struct device *dev, unsigned int gpio);
注意,内核中针对内存、IRQ、时钟、GPIO、pinctrl都有devm_开头的API,使用这部分API的时候,内核会有类似于Java资源自动回收机制,因此在代码中做出错处理时,无需释放相关的资源。
对于GPIO而言,特别值得一提的是,内核会创建/sys结点 /sys/class/gpio/gpioN/,透过它我们可以echo值从而改变GPIO的方向、设置和获取GPIO的值。
在拥有Device Tree支持的情况之下,我们可以透过Device Tree来描述某GPIO控制器提供的GPIO引脚被具体设备使用的情况。在GPIO控制器对应的结点中,需定义#gpio-cells 和gpio-controller属性,具体的设备结点则透过xxx-gpios属性来引用GPIO控制器结点及GPIO引脚。
如VEXPRESS电路板 DT文件arch/arm/boot/dts/vexpress-v2m.dtsi中拥有如下GPIO控制器结点:
73 v2m_sysreg:sysreg@00000 {
74 compatible ="arm,vexpress-sysreg";
75 reg = <0x000000x1000>;
76 gpio-controller;
77 #gpio-cells =<2>;
78 };
VEXPRESS电路板上的MMC控制器会使用该结点GPIO控制器提供的GPIO引脚,则具体的mmci@05000设备结点的会通过-gpios属性引用GPIO:
111 mmci@05000 {
112 compatible ="arm,pl180", "arm,primecell";
113 reg =<0x05000 0x1000>;
114 interrupts =<9 10>;
115 cd-gpios = <&v2m_sysreg 0 0>;
116 wp-gpios =<&v2m_sysreg 1 0>;
117 …
121 };
其中的cd-gpios用于SD/MMC卡的detection,而wp-gpios用于写保护,MMC主机控制器驱动会透过如下方法获取这2个GPIO,详见于drivers/mmc/host/mmci.c:
1220static void mmci_dt_populate_generic_pdata(struct device_node *np,
1221 structmmci_platform_data *pdata)
1222{
1223 int bus_width = 0;
1224
1225 pdata->gpio_wp =of_get_named_gpio(np, "wp-gpios", 0);
1226 pdata->gpio_cd =of_get_named_gpio(np, "cd-gpios", 0);
…
}
7. pinctrl驱动许多SoC内部都包含pin控制器,通过pin控制器的寄存器,我们可以配置一个或者一组引脚的功能和特性。在软件上,Linux内核的pinctrl驱动可以操作pin控制器为我们完成如下工作:
§ 枚举并且命名pin控制器可控制的所有引脚;
§ 提供引脚复用的能力;
§ 提供配置引脚的能力,如驱动能力、上拉下拉、开漏(open drain)等。
pinctrl和引脚
在特定SoC的pinctrl驱动中,我们需要定义引脚。假设有一个PGA封装的芯片的引脚排布如下:
A B C D E F G H
8 o o o o o o o o
7 o o o o o o o o
6 o o o o o o o o
5 o o o o o o o o
4 o o o o o o o o
3 o o o o o o o o
2 o o o o o o o o
1 o o o o o o o o
在pinctrl驱动初始化的时候,需要向pinctrl子系统注册一个pinctrl_desc描述符,在该描述符中包含所有引脚的列表。可以通过如下代码来注册这个pin控制器并命名其所有引脚:
59#include <linux/pinctrl/pinctrl.h>
60
61const struct pinctrl_pin_descfoo_pins[] = {
62 PINCTRL_PIN(0, "A8"),
63 PINCTRL_PIN(1, "B8"),
64 PINCTRL_PIN(2, "C8"),
65 ...
66 PINCTRL_PIN(61, "F1"),
67 PINCTRL_PIN(62, "G1"),
68 PINCTRL_PIN(63, "H1"),
69};
70
71static struct pinctrl_descfoo_desc = {
72 .name = "foo",
73 .pins = foo_pins,
74 .npins = ARRAY_SIZE(foo_pins),
75 .maxpin = 63,
76 .owner = THIS_MODULE,
77};
78
79int __init foo_probe(void)
80{
81 struct pinctrl_dev *pctl;
82
83 pctl = pinctrl_register(&foo_desc,<PARENT>, NULL);
84 if (IS_ERR(pctl))
85 pr_err("could not registerfoo pin driver\n");
86}
引脚组(pin group)
在pinctrl子系统中,支持将一组引脚绑定为同一功能。假设{ 0, 8, 16, 24 }这一组引脚承担SPI的功能,而{ 24, 25 }这一组引脚承担I2C接口功能。在驱动的代码中,需要体现这个分组关系,并且为这些分组实现pinctrl_ops的成员函数get_groups_count、get_groups_count和get_groups_count,将pinctrl_ops填充到前文pinctrl_desc的实例foo_desc中。
130#include <linux/pinctrl/pinctrl.h>
131
132struct foo_group {
133 const char *name;
134 const unsigned int *pins;
135 const unsigned num_pins;
136};
137
138static const unsigned int spi0_pins[] = { 0, 8, 16, 24 };
139static const unsigned int i2c0_pins[] = { 24, 25 };
140
141static const struct foo_group foo_groups[] = {
142 {
143 .name = "spi0_grp",
144 .pins = spi0_pins,
145 .num_pins =ARRAY_SIZE(spi0_pins),
146 },
147 {
148 .name = "i2c0_grp",
149 .pins = i2c0_pins,
150 .num_pins =ARRAY_SIZE(i2c0_pins),
151 },
152};
153
154
155static int foo_get_groups_count(struct pinctrl_dev *pctldev)
156{
157 return ARRAY_SIZE(foo_groups);
158}
159
160static const char *foo_get_group_name(struct pinctrl_dev *pctldev,
161 unsigned selector)
162{
163 return foo_groups[selector].name;
164}
165
166static int foo_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
167 unsigned **const pins,
168 unsigned *const num_pins)
169{
170 *pins = (unsigned *) foo_groups[selector].pins;
171 *num_pins =foo_groups[selector].num_pins;
172 return 0;
173}
174
175static struct pinctrl_opsfoo_pctrl_ops = {
176 .get_groups_count =foo_get_groups_count,
177 .get_group_name = foo_get_group_name,
178 .get_group_pins = foo_get_group_pins,
179};
180
181
182static struct pinctrl_descfoo_desc = {
183 ...
184 .pctlops = &foo_pctrl_ops,
185};
get_groups_count()成员函数用于告知pinctrl子系统该SoC中合法的被选引脚组有多少个,而get_group_name()则提供引脚组的名字,get_group_pins()提供引脚组的引脚表。在设备驱动调用pinctrl通用API使能某一组引脚的对应功能时,pinctrl子系统的核心层会调用上述callback函数。
引脚配置
设备驱动有时候需要配置引脚,譬如可能把引脚设置为高阻或者三态(达到类似断连引脚的效果),或通过某阻值将引脚上拉/下拉以确保默认状态下引脚的电平状态。驱动中可以自定义相应板级引脚配置API的细节,譬如某设备驱动可能通过如下代码将某引脚上拉:
#include<linux/pinctrl/consumer.h>
ret= pin_config_set("foo-dev", "FOO_GPIO_PIN",PLATFORM_X_PULL_UP);
其中的PLATFORM_X_PULL_UP由特定的pinctrl驱动定义。在特定的pinctrl驱动中,需要实现完成这些配置所需要的callback函数(pinctrl_desc的confops成员函数):
222#include <linux/pinctrl/pinctrl.h>
223#include <linux/pinctrl/pinconf.h>
224#include "platform_x_pindefs.h"
225
226static int foo_pin_config_get(struct pinctrl_dev *pctldev,
227 unsigned offset,
228 unsigned long *config)
229{
230 struct my_conftype conf;
231
232 ... Find setting for pin @ offset ...
233
234 *config = (unsigned long) conf;
235}
236
237static int foo_pin_config_set(struct pinctrl_dev *pctldev,
238 unsigned offset,
239 unsigned long config)
240{
241 struct my_conftype *conf = (structmy_conftype *) config;
242
243 switch (conf) {
244 case PLATFORM_X_PULL_UP:
245 ...
246 }
247 }
248}
249
250static int foo_pin_config_group_get (struct pinctrl_dev *pctldev,
251 unsigned selector,
252 unsigned long *config)
253{
254 ...
255}
256
257static int foo_pin_config_group_set (struct pinctrl_dev *pctldev,
258 unsigned selector,
259 unsigned long config)
260{
261 ...
262}
263
264static struct pinconf_opsfoo_pconf_ops = {
265 .pin_config_get = foo_pin_config_get,
266 .pin_config_set = foo_pin_config_set,
267 .pin_config_group_get =foo_pin_config_group_get,
268 .pin_config_group_set =foo_pin_config_group_set,
269};
270
271/* Pin config operations are handled by some pin controller */
272static struct pinctrl_descfoo_desc = {
273 ...
274 .confops = &foo_pconf_ops,
275};
其中的pin_config_group_get()、pin_config_group_set()针对的是可同时配置一个引脚组的状态情况,而pin_config_get()、pin_config_set()针对的则是单个引脚的配置。
与GPIO子系统的交互
pinctrl驱动中所覆盖的引脚可能同时可作为GPIO用,内核的GPIO子系统和pinctrl子系统本来是并行工作的,但是有时候需要交叉映射,这种情况下,需要在pinctrl驱动中告知pinctrl子系统核心层GPIO与底层pinctrl驱动所管理的引脚之间的映射关系。假设pinctrl驱动中定义的引脚32~47与gpio_chip实例chip_a的GPIO对应,引脚64~71与gpio_chip实例chip_b的GPIO对应,即映射关系为:
chip a:
- GPIO range :[32 .. 47]
- pinrange : [32 .. 47]
chip b:
- GPIO range :[48 .. 55]
- pinrange : [64 .. 71]
则在特定pinctrl驱动中可以透过如下代码注册2个GPIO范围:
305struct gpio_chip chip_a;
306struct gpio_chip chip_b;
307
308static struct pinctrl_gpio_range gpio_range_a = {
309 .name = "chip a",
310 .id = 0,
311 .base = 32,
312 .pin_base = 32,
313 .npins = 16,
314 .gc = &chip_a;
315};
316
317static struct pinctrl_gpio_range gpio_range_b = {
318 .name = "chip b",
319 .id = 0,
320 .base = 48,
321 .pin_base = 64,
322 .npins = 8,
323 .gc = &chip_b;
324};
325
326{
327 struct pinctrl_dev *pctl;
328 ...
329 pinctrl_add_gpio_range(pctl,&gpio_range_a);
330 pinctrl_add_gpio_range(pctl,&gpio_range_b);
331}
在基于内核gpiolib的GPIO驱动中,若设备驱动需进行GPIO申请gpio_request()和释放gpio_free(),GPIO驱动则会调用pinctrl子系统中的pinctrl_request_gpio()和pinctrl_free_gpio()通用API,pinctrl子系统会查找申请的GPIO和pin的映射关系,并确认引脚是否被其他复用功能所占用。与pinctrl子系统通用层pinctrl_request_gpio()和pinctrl_free_gpio()API对应,在底层的具体pinctrl驱动中,需要实现pinmux_ops结构体的gpio_request_enable()和gpio_disable_free()成员函数。
除了gpio_request_enable()和gpio_disable_free()成员函数外,pinmux_ops结构体主要还用来封装pinmux功能enable/disable的callback函数,下面可以看到它更多的细节。
引脚复用(pinmux)
pinctrl驱动中可处理引脚复用,它定义了FUNCTIONS(功能),驱动可以设置某FUNCTIONS的enable或者disable。各个FUNCTIONS联合起来组成一个一维数组,譬如{ spi0, i2c0, mmc0 }就描述了3个不同的FUNCTIONS。
一个特定的功能总是要求一些引脚组(pingroup)来完成,引脚组的数量为可以为1个或者多个。假设对前文所描述的PGA封装的SoC而言,如下图:
387
388 A B C D E F G H
389 +---+
390 8 | o | o o o o o o o
391 | |
392 7 | o | o o o o o o o
393 | |
394 6 | o | o o o o o o o
395 +---+---+
396 5 | o | o | o o o o o o
397 +---+---+ +---+
398 4 o o o o o o | o | o
399 | |
400 3 o o o o o o | o | o
401 | |
402 2 o o o o o o | o | o
403 +-------+-------+-------+---+---+
404 1 | o o| o o | o o | o | o |
405 +-------+-------+-------+---+---+
I2C功能由{ A5, B5 }引脚组成,而在定义引脚描述的pinctrl_pin_desc结构体实例foo_pins的时候,将它们的序号定义为了{ 24, 25 } ;而SPI功能则由可以由{ A8, A7, A6, A5 }和 { G4, G3, G2, G1 }, 也即{ 0, 8, 16, 24 }和{ 38, 46, 54, 62 }两个引脚组完成(注意在整个系统中,引脚组的名字不会重叠)。
由此,功能和引脚组的组合就可以决定一组引脚在系统里的作用,因此在设置某组引脚的作用时,pinctrl的核心层会将功能的序号以及引脚组的序号传递给底层pinctrl驱动中相关的callback函数。
在整个系统中,驱动或板级代码调用pinmux相关的API获取引脚后,会形成一个(pinctrl、使用引脚的设备、功能、引脚组)的映射关系,假设在某电路板上,将让spi0 设备使用pinctrl0的fspi0功能以及gspi0引脚组,让i2c0设备使用pinctrl0的fi2c0功能和gi2c0引脚组,我们将得到如下的映射关系:
502 {
503 {"map-spi0", spi0, pinctrl0, fspi0,gspi0},
504 {"map-i2c0", i2c0, pinctrl0,fi2c0, gi2c0}
505 }
pinctrl子系统的核心会保证每个引脚的排他性,因此一个引脚如果已经被某设备用掉了,而其他的设备又申请该引脚行驶其他的功能或GPIO,则pinctrl核心层会让该次申请失败。
特定pinctrl驱动中pinmux相关的代码主要处理如何enable/disable某一{功能,引脚组}的组合,譬如,当spi0设备申请pinctrl0的fspi0功能和gspi0引脚组以便将gspi0引脚组配置为SPI接口时,相关的callback被组织进一个pinmux_ops结构体,而该结构体的实例最终成为前文pinctrl_desc的pmxops成员:
562#include <linux/pinctrl/pinctrl.h>
563#include <linux/pinctrl/pinmux.h>
564
565struct foo_group {
566 const char *name;
567 const unsigned int *pins;
568 const unsigned num_pins;
569};
570
571static const unsigned spi0_0_pins[] = { 0, 8, 16, 24 };
572static const unsigned spi0_1_pins[] = { 38, 46, 54, 62 };
573static const unsigned i2c0_pins[] = { 24, 25 };
574static const unsigned mmc0_1_pins[] = { 56, 57 };
575static const unsigned mmc0_2_pins[] = { 58, 59 };
576static const unsigned mmc0_3_pins[] = { 60, 61, 62, 63 };
577
578static const struct foo_group foo_groups[] = {
579 {
580 .name ="spi0_0_grp",
581 .pins = spi0_0_pins,
582 .num_pins =ARRAY_SIZE(spi0_0_pins),
583 },
584 {
585 .name ="spi0_1_grp",
586 .pins = spi0_1_pins,
587 .num_pins = ARRAY_SIZE(spi0_1_pins),
588 },
589 {
590 .name = "i2c0_grp",
591 .pins = i2c0_pins,
592 .num_pins =ARRAY_SIZE(i2c0_pins),
593 },
594 {
595 .name ="mmc0_1_grp",
596 .pins = mmc0_1_pins,
597 .num_pins =ARRAY_SIZE(mmc0_1_pins),
598 },
599 {
600 .name ="mmc0_2_grp",
601 .pins = mmc0_2_pins,
602 .num_pins =ARRAY_SIZE(mmc0_2_pins),
603 },
604 {
605 .name ="mmc0_3_grp",
606 .pins = mmc0_3_pins,
607 .num_pins =ARRAY_SIZE(mmc0_3_pins),
608 },
609};
610
611
612static int foo_get_groups_count(struct pinctrl_dev *pctldev)
613{
614 return ARRAY_SIZE(foo_groups);
615}
616
617static const char *foo_get_group_name(struct pinctrl_dev *pctldev,
618 unsigned selector)
619{
620 return foo_groups[selector].name;
621}
622
623static int foo_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
624 unsigned **const pins,
625 unsigned *const num_pins)
626{
627 *pins = (unsigned *) foo_groups[selector].pins;
628 *num_pins =foo_groups[selector].num_pins;
629 return 0;
630}
631
632static struct pinctrl_ops foo_pctrl_ops = {
633 .get_groups_count =foo_get_groups_count,
634 .get_group_name = foo_get_group_name,
635 .get_group_pins = foo_get_group_pins,
636};
637
638struct foo_pmx_func {
639 const char *name;
640 const char * const *groups;
641 const unsigned num_groups;
642};
643
644static const char * const spi0_groups[] = { "spi0_0_grp","spi0_1_grp" };
645static const char * const i2c0_groups[] = { "i2c0_grp" };
646static const char * const mmc0_groups[] = { "mmc0_1_grp","mmc0_2_grp",
647 "mmc0_3_grp" };
648
649static const struct foo_pmx_func foo_functions[] = {
650 {
651 .name = "spi0",
652 .groups = spi0_groups,
653 .num_groups =ARRAY_SIZE(spi0_groups),
654 },
655 {
656 .name = "i2c0",
657 .groups = i2c0_groups,
658 .num_groups =ARRAY_SIZE(i2c0_groups),
659 },
660 {
661 .name = "mmc0",
662 .groups = mmc0_groups,
663 .num_groups =ARRAY_SIZE(mmc0_groups),
664 },
665};
666
667int foo_get_functions_count(struct pinctrl_dev *pctldev)
668{
669 return ARRAY_SIZE(foo_functions);
670}
671
672const char *foo_get_fname(struct pinctrl_dev *pctldev, unsigned selector)
673{
674 return foo_functions[selector].name;
675}
676
677static int foo_get_groups(struct pinctrl_dev *pctldev, unsigned selector,
678 const char * const**groups,
679 unsigned * constnum_groups)
680{
681 *groups = foo_functions[selector].groups;
682 *num_groups =foo_functions[selector].num_groups;
683 return 0;
684}
685
686int foo_enable(struct pinctrl_dev *pctldev, unsigned selector,
687 unsigned group)
688{
689 u8 regbit = (1 << selector + group);
690
691 writeb((readb(MUX)|regbit), MUX)
692 return 0;
693}
694
695void foo_disable(struct pinctrl_dev *pctldev, unsigned selector,
696 unsigned group)
697{
698 u8 regbit = (1 << selector +group);
699
700 writeb((readb(MUX) & ~(regbit)),MUX)
701 return 0;
702}
703
704 structpinmux_ops foo_pmxops = {
705 .get_functions_count =foo_get_functions_count,
706 .get_function_name = foo_get_fname,
707 .get_function_groups = foo_get_groups,
708 .enable = foo_enable,
709 .disable = foo_disable,
710};
711
712/* Pinmux operations are handled by some pin controller */
713static struct pinctrl_desc foo_desc = {
714 ...
715 .pctlops = &foo_pctrl_ops,
716 .pmxops = &foo_pmxops,
717};
718
具体的pinctrl、使用引脚的设备、功能、引脚组的映射关系,可以在板文件中透过定义pinctrl_map结构体的实例来展开,如:
828static struct pinctrl_map __initdata mapping[] = {
829 PIN_MAP_MUX_GROUP("foo-i2c.o", PINCTRL_STATE_DEFAULT,"pinctrl-foo", NULL, "i2c0"),
830};
PIN_MAP_MUX_GROUP是一个快捷的宏,用于赋值pinctrl_map的各个成员:
88#define PIN_MAP_MUX_GROUP(dev, state, pinctrl, grp, func) \
89 { \
90 .dev_name = dev, \
91 .name = state, \
92 .type =PIN_MAP_TYPE_MUX_GROUP, \
93 .ctrl_dev_name = pinctrl, \
94 .data.mux = { \
95 .group = grp, \
96 .function = func, \
97 }, \
98 }
99
当然,这种映射关系最好是在DeviceTree中透过结点的属性进行,具体的结点属性的定义方法依赖于具体的pinctrl驱动,最终在pinctrl驱动中透过pinctrl_ops结构体的.dt_node_to_map()成员函数读出属性并建立映射表。
又由于1个功能可能可由2个不同的引脚组实现,可能形成如下对于同1个功能有2个可选引脚组的pinctrl_map:
staticstruct pinctrl_map __initdata mapping[] = {
PIN_MAP_MUX_GROUP("foo-spi.0","spi0-pos-A", "pinctrl-foo", "spi0_0_grp", "spi0"),
PIN_MAP_MUX_GROUP("foo-spi.0","spi0-pos-B", "pinctrl-foo", "spi0_1_grp", "spi0"),
};
在运行时,我们可以透过类似的API去查找并设置位置A的引脚组行驶SPI接口的功能:
954 p = devm_pinctrl_get(dev);
955 s = pinctrl_lookup_state(p, "spi0-pos-A");
956 ret = pinctrl_select_state(p, s);
或者可以更加简单地使用:
p =devm_pinctrl_get_select(dev, "spi0-pos-A");
若想运行时切换位置A和B的引脚组行使SPI的接口功能,代码结构类似:
1163foo_probe()
1164{
1165 /* Setup */
1166 p = devm_pinctrl_get(&device);
1167 if (IS_ERR(p))
1168 ...
1169
1170 s1 = pinctrl_lookup_state(foo->p," spi0-pos-A");
1171 if (IS_ERR(s1))
1172 ...
1173
1174 s2 = pinctrl_lookup_state(foo->p," spi0-pos-B");
1175 if (IS_ERR(s2))
1176 ...
1177}
1178
1179foo_switch()
1180{
1181 /* Enable on position A */
1182 ret = pinctrl_select_state(s1);
1183 if (ret < 0)
1184 ...
1185
1186 ...
1187
1188 /* Enable on position B */
1189 ret = pinctrl_select_state(s2);
1190 if (ret < 0)
1191 ...
1192
1193 ...
1194}
pinctrl子系统中定义了pinctrl_get_select_default()以及有devm_前缀的devm_ pinctrl_get_select
评论