驱动篇:底层驱动移植(三)(摘录)

驱动篇:底层驱动移植(三)(摘录)

GPIO 驱动
在 drivers/gpio 下实现了通用的基于 gpiolib 的 GPIO 驱动,其中定义了一个通用的用于描述底层 GPIO 控制器的gpio_chip 结构体,并要求具体的 SoC 实现 gpio_chip 结构体的成员函数,最后通过 gpiochip_add ()注册 gpio_chip 。GPIO 驱动可以存在于 drivers/gpio 目录中,但是在 GPIO 兼有多种功能且需要复杂配置的情况下, GPIO 的驱动部分往往直接移到 drivers/pinctrl 目录下并连同 pinmux 一起实现,而不存在于 drivers/gpio 目录中。

gpio_chip 结构体封装了底层硬件的 GPIO enable () /disable ()等操作,它的定义如代码清单所示:
gpio_chip 结构体

struct gpio_chip {
	const char		*label;
	struct gpio_device	*gpiodev;
	struct device		*parent;
	struct module		*owner;

	int			(*request)(struct gpio_chip *chip,
						unsigned offset);
	void			(*free)(struct gpio_chip *chip,
						unsigned offset);
	int			(*get_direction)(struct gpio_chip *chip,
						unsigned offset);
	int			(*direction_input)(struct gpio_chip *chip,
						unsigned offset);
	int			(*direction_output)(struct gpio_chip *chip,
						unsigned offset, int value);
	int			(*get)(struct gpio_chip *chip,
						unsigned offset);
	void			(*set)(struct gpio_chip *chip,
						unsigned offset, int value);
	void			(*set_multiple)(struct gpio_chip *chip,
						unsigned long *mask,
						unsigned long *bits);
	int			(*set_config)(struct gpio_chip *chip,
					      unsigned offset,
					      unsigned long config);
	int			(*to_irq)(struct gpio_chip *chip,
						unsigned offset);

	void			(*dbg_show)(struct seq_file *s,
						struct gpio_chip *chip);
	int			base;
	u16			ngpio;
	const char		*const *names;
	bool			can_sleep;

#if IS_ENABLED(CONFIG_GPIO_GENERIC)
	unsigned long (*read_reg)(void __iomem *reg);
	void (*write_reg)(void __iomem *reg, unsigned long data);
	unsigned long (*pin2mask)(struct gpio_chip *gc, unsigned int pin);
	void __iomem *reg_dat;
	void __iomem *reg_set;
	void __iomem *reg_clr;
	void __iomem *reg_dir;
	int bgpio_bits;
	spinlock_t bgpio_lock;
	unsigned long bgpio_data;
	unsigned long bgpio_dir;
#endif

#ifdef CONFIG_GPIOLIB_IRQCHIP
	/*
	 * With CONFIG_GPIOLIB_IRQCHIP we get an irqchip inside the gpiolib
	 * to handle IRQs for most practical cases.
	 */

	/**
	 * @irq:
	 *
	 * Integrates interrupt chip functionality with the GPIO chip. Can be
	 * used to handle IRQs for most practical cases.
	 */
	struct gpio_irq_chip irq;
#endif

#if defined(CONFIG_OF_GPIO)
	/*
	 * If CONFIG_OF is enabled, then all GPIO controllers described in the
	 * device tree automatically may have an OF translation
	 */

	/**
	 * @of_node:
	 *
	 * Pointer to a device tree node representing this GPIO controller.
	 */
	struct device_node *of_node;

	/**
	 * @of_gpio_n_cells:
	 *
	 * Number of cells used to form the GPIO specifier.
	 */
	unsigned int of_gpio_n_cells;

	/**
	 * @of_xlate:
	 *
	 * Callback to translate a device tree GPIO specifier into a chip-
	 * relative GPIO number and flags.
	 */
	int (*of_xlate)(struct gpio_chip *gc,
			const struct of_phandle_args *gpiospec, u32 *flags);
#endif
};

通过这层封装,每个具体的要用到 GPIO 的设备驱动都使用通用的 GPIO API 来操作 GPIO ,这些 API 主要用于GPIO 的申请、释放和设置:

int gpio_request(unsigned gpio, const char *label);
void gpio_free(unsigned gpio);
int gpio_direction_input(unsigned gpio);
int gpio_direction_output(unsigned gpio, int value);
int gpio_set_debounce(unsigned gpio, unsigned debounce);
int gpio_get_value_cansleep(unsigned gpio);
void gpio_set_value_cansleep(unsigned gpio, int value);
int gpio_request_one(unsigned gpio, unsigned long flags, const char *label);
int gpio_request_array(const struct gpio *array, size_t num);
void gpio_free_array(const struct gpio *array, size_t num);
int devm_gpio_request(struct device *dev, unsigned gpio, const char *label);
int devm_gpio_request_one(struct device *dev, unsigned gpio,unsigned long flags, 
const char *label);
void devm_gpio_free(struct device *dev, unsigned int gpio);

注意: 内核中针对内存、 IRQ 、时钟、 GPIO 、 pinctrl 、 Regulator 都有以 devm_ 开头的
API ,使用这部分 API 的时候,内核会有类似于 Java 的资源自动回收机制,因此在代码中进行出错处理
时,无须释放相关的资源。

对于 GPIO 而言,特别值得一提的是,内核会创建 /sys 节点 /sys/class/gpio/gpioN/ ,通过它我们可以 echo 值从而改变GPIO 的方向、设置并获取 GPIO 的值。

在拥有设备树支持的情况下,我们可以通过设备树来描述某 GPIO 控制器提供的 GPIO 引脚被具体设备使用的情况。在 GPIO 控制器对应的节点中,需定义 #gpio-cells 和 gpio-controller 属性,具体的设备节点则通过 xxx-gpios 属性来引用 GPIO 控制器节点及 GPIO 引脚。

如 VEXPRESS 电路板 DT 文件 arch/arm/boot/dts/vexpress-v2m.dtsi 中有如下 GPIO 控制器节点:

v2m_sysreg: sysreg@00000 {
compatible = "arm,vexpress-sysreg";
reg = <0x00000 0x1000>;
gpio-controller;
#gpio-cells = <2>;
};

VEXPRESS 电路板上的 MMC 控制器会使用该节点 GPIO 控制器提供的 GPIO 引脚,则具体的 mmci@05000 设备节点会通过 -gpios 属性引用 GPIO :

mmci@05000 {
compatible = "arm,pl180", "arm,primecell";
reg = <0x05000 0x1000>;
interrupts = <9 10>;
cd-gpios = <&v2m_sysreg 0 0>;
wp-gpios = <&v2m_sysreg 1 0>;
...
};

其中的 cd-gpios 用于 SD/MMC 卡的探测,而 wp-gpios 用于写保护

MMC 主机控制器驱动会通过如下方法获取这两个 GPIO ,详见于 drivers/mmc/host/mmci.c :

static void mmci_dt_populate_generic_pdata(struct device_node *np,
struct mmci_platform_data *pdata)
{
...
pdata->gpio_wp = of_get_named_gpio(np, "wp-gpios", 0);
pdata->gpio_cd = of_get_named_gpio(np, "cd-gpios", 0);
...
}

pinctrl 驱动
许多 SoC 内部都包含 pin 控制器,通过 pin 控制器的寄存器,我们可以配置一个或者一组引脚的功能和特性。在软件上, Linux 内核的 pinctrl 驱动可以操作 pin 控制器为我们完成如下工作:
· 枚举并且命名 pin 控制器可控制的所有引脚;
· 提供引脚复用的能力;
· 提供配置引脚的能力,如驱动能力、上拉下拉、开漏( Open Drain )等。
1.pinctrl 和引脚
在特定 SoC 的 pinctrl 驱动中,我们需要定义引脚。假设有一个 PGA 封装的芯片的引脚排布如图所示。
在这里插入图片描述在 pinctrl 驱动初始化的时候,需要向 pinctrl 子系统注册一个 pinctrl_desc 描述符,该描述符的 pins 成员中包含所有引脚的列表。可以通过代码清单的方法来注册这个 pin 控制器并命名它的所有引脚:
代码清单 pinctrl 引脚描述

#include <linux/pinctrl/pinctrl.h>

const struct pinctrl_pin_desc foo_pins[] = {
 PINCTRL_PIN(0, "A8"),
 PINCTRL_PIN(1, "B8"),
 PINCTRL_PIN(2, "C8"),
 ...
 PINCTRL_PIN(61, "F1"),
 PINCTRL_PIN(62, "G1"),
 PINCTRL_PIN(63, "H1"),
};

static struct pinctrl_desc foo_desc = {
 .name = "foo",
 .pins = foo_pins,
 .npins = ARRAY_SIZE(foo_pins),
 .maxpin = 63,
 .owner = THIS_MODULE,
};

int __init foo_probe(void)
{

 struct pinctrl_dev *pctl;
 pctl = pinctrl_register(&foo_desc, <PARENT>, NULL);
 if (IS_ERR(pctl))

pr_err("could not register foo pin driver\n");
}

2 . 引脚组( Pin Group )
在 pinctrl 子系统中,支持将一组引脚绑定为同一功能。假设 {0 , 8 , 16 , 24} 这一组引脚承担 SPI 的功能,而{24 , 25} 这一组引脚承担 I 2 C 接口功能。在驱动的代码中,需要体现这个分组关系,并且为这些分组实现pinctrl_ops 的成员函数 get_groups_count ()、 get_group_name ()和 get_group_pins (),将 pinctrl_ops 填充到前文pinctrl_desc 的实例 foo_desc 中,如代码清单所示:
pinctrl 驱动对引脚分组

#include <linux/pinctrl/pinctrl.h>

struct foo_group {
 const char *name;
 const unsigned int *pins;
 const unsigned num_pins;
};

static const unsigned int spi0_pins[] = { 0, 8, 16, 24 };
static const unsigned int i2c0_pins[] = { 24, 25 };

static const struct foo_group foo_groups[] = {

{ .name = "spi0_grp",
 .pins = spi0_pins,
 .num_pins = ARRAY_SIZE(spi0_pins),
 },
 {
 .name = "i2c0_grp",
 .pins = i2c0_pins,
 .num_pins = ARRAY_SIZE(i2c0_pins),

},
};


static int foo_get_groups_count(struct pinctrl_dev *pctldev)
{

return ARRAY_SIZE(foo_groups);
}

static const char *foo_get_group_name(struct pinctrl_dev *pctldev,

unsigned selector)
{

return foo_groups[selector].name;
}

static int foo_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
 unsigned ** const pins,
 unsigned * const num_pins)
{
 *pins = (unsigned *) foo_groups[selector].pins;
 *num_pins = foo_groups[selector].num_pins;
 return 0;
}

static struct pinctrl_ops foo_pctrl_ops = {47 .get_groups_count = foo_get_groups_count,
 .get_group_name = foo_get_group_name,
 .get_group_pins = foo_get_group_pins,
};


static struct pinctrl_desc foo_desc = {
 ...
 .pctlops = &foo_pctrl_ops,
};


get_groups_count ()成员函数用于告知 pinctrl 子系统该 SoC 中合法的被选引脚组有多少个,而 
get_group_name ()则提供引脚组的名字, get_group_pins ()提供引脚组的引脚表。在设备驱动
调用 pinctrl 通用 API 使能某一组引脚的对应功能时, pinctrl 子系统的核心层会调用上述回调函数。

3 . 引脚配置
设备驱动有时候需要配置引脚,譬如可能把引脚设置为高阻或者三态(达到类似断连引脚的效果),或通过某阻值将引脚上拉 / 下拉以确保默认状态下引脚的电平状态。在驱动中可以自定义相应板级引脚配置 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 驱动中,需要实现完成这些配置所需要的回调函数( pinctrl_desc 的 confops 成员函数),如代码清单所示:
引脚的配置

#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinconf.h>
#include "platform_x_pindefs.h"

static int foo_pin_config_get(struct pinctrl_dev *pctldev,
 unsigned offset,
 unsigned long *config)
{
struct my_conftype conf;
... Find setting for pin @ offset ...

*config = (unsigned long) conf;
}

static int foo_pin_config_set(struct pinctrl_dev *pctldev,
 unsigned offset,
 unsigned long config)
{

struct my_conftype *conf = (struct my_conftype *) config;

switch (conf) {
 case PLATFORM_X_PULL_UP:
 ...
 }

}
}
static int foo_pin_config_group_get (struct pinctrl_dev *pctldev,
 unsigned selector,
 unsigned long *config)
{

...
}

static int foo_pin_config_group_set (struct pinctrl_dev *pctldev,
 unsigned selector,
 unsigned long config)
{

...
}

static struct pinconf_ops foo_pconf_ops = {
 .pin_config_get = foo_pin_config_get,
 .pin_config_set = foo_pin_config_set,
 .pin_config_group_get = foo_pin_config_group_get,
 .pin_config_group_set = foo_pin_config_group_set,
};

/* Pin config operations are handled by some pin controller */
static struct pinctrl_desc foo_desc = {
 ...
 .confops = &foo_pconf_ops,
};

其中的 pin_config_group_get ()、 pin_config_group_set ()针对的是可同时配置一个引脚组的
状态情况,而pin_config_get ()、 pin_config_set ()针对的则是单个引脚的配置。

4 . 与 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]
- pin range : [32 .. 47]
chip b:
- GPIO range : [48 .. 55]
- pin range : [64 .. 71]

则在特定 pinctrl 驱动中可以通过如下代码注册两个 GPIO 范围,如代码清单所示:
GPIO 与 pinctrl 引脚的映射

struct gpio_chip chip_a;
struct gpio_chip chip_b;

static struct pinctrl_gpio_range gpio_range_a = {
 .name = "chip a",
 .id = 0, 
 .base = 32,
 .pin_base = 32,
 .npins = 16,
 .gc = &chip_a;
};
static struct pinctrl_gpio_range gpio_range_b = {
 .name = "chip b",
 .id = 0,
 .base = 48,
 .pin_base = 64,
 .npins = 8,
 .gc = &chip_b;
};

{
 struct pinctrl_dev *pctl;
 ...
 pinctrl_add_gpio_range(pctl, &gpio_range_a);
 pinctrl_add_gpio_range(pctl, &gpio_range_b);
}

在基于内核 gpiolib 的 GPIO 驱动中,若设备驱动需进行 GPIO 申请 gpio_request ()和释放 gpio_free (), GPIO 驱动则会调用 pinctrl 子系统中的 pinctrl_request_gpio ()和 pinctrl_free_gpio ()通用 API , pinctrl 子系统会查找申请的GPIO 和引脚的映射关系,并确认引脚是否被其他复用功能所占用。与 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 功能使能 / 禁止的回调函数,下面可以看到它的更多细节。

5 . 引脚复用( pinmux )
在 pinctrl 驱动中可处理引脚复用,它定义了功能( FUNCTIONS ),驱动可以设置某功能的使能或者禁止。各个功能联合起来组成一个一维数组,譬如 {spi0 , i2c0 , mmc0} 就描述了 3 个不同的功能。一个特定的功能总是要求由一些引脚组来完成,引脚组的数量可以为 1 个或者多个。假设对前文所描述的 PGA 封装的 SoC 而言,引脚分组如图 20.10 所示。假设 I 2 C 功能由 {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 驱动中相关的回调函数。
在这里插入图片描述在整个系统中,驱动或板级代码调用 pinmux 相关的 API 获取引脚后,会形成一个 pinctrl 、使用引脚的设备、功能、引脚组的映射关系,假设在某电路板上,将让 spi0 设备使用 pinctrl0 的 fspi0 功能以及 gspi0 引脚组,让 i2c0 设备使用 pinctrl0 的 fi2c0 功能和 gi2c0 引脚组,我们将得到如下的映射关系:

{
{"map-spi0", spi0, pinctrl0, fspi0, gspi0},
{"map-i2c0", i2c0, pinctrl0, fi2c0, gi2c0}
}

pinctrl 子系统的核心会保证每个引脚的排他性,因此一个引脚如果已经被某设备用掉了,而其他的设备又申请该引脚以行使其他的功能或 GPIO ,则 pinctrl 核心层会让该次申请失败。在特定 pinctrl 驱动中 pinmux 相关的代码主要处理如何使能 / 禁止某一 { 功能,引脚组 } 的组合,譬如,当 spi0 设备申请 pinctrl0 的 fspi0 功能和 gspi0 引脚组以便将 gspi0 引脚组配置为 SPI 接口时,相关的回调函数被组织进一个pinmux_ops 结构体中,而该结构体的实例最终成为前文 pinctrl_desc 的 pmxops 成员,如代码清单所示:
pinmux 的实现

#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
struct foo_group {
 const char *name;
 const unsigned int *pins;
 const unsigned num_pins;
};

static const unsigned spi0_0_pins[] = { 0, 8, 16, 24 };
static const unsigned spi0_1_pins[] = { 38, 46, 54, 62 };
static const unsigned i2c0_pins[] = { 24, 25 };
static const unsigned mmc0_1_pins[] = { 56, 57 };
static const unsigned mmc0_2_pins[] = { 58, 59 };
static const unsigned mmc0_3_pins[] = { 60, 61, 62, 63 };

static const struct foo_group foo_groups[] = {

{
 .name = "spi0_0_grp",
 .pins = spi0_0_pins,
 .num_pins = ARRAY_SIZE(spi0_0_pins),
 },
 {
 .name = "spi0_1_grp",
 .pins = spi0_1_pins,
 .num_pins = ARRAY_SIZE(spi0_1_pins),
 },
 {
 .name = "i2c0_grp",
 .pins = i2c0_pins,
 .num_pins = ARRAY_SIZE(i2c0_pins),
 },
 {
 .name = "mmc0_1_grp",
 .pins = mmc0_1_pins,36
.num_pins = ARRAY_SIZE(mmc0_1_pins),
 },
 {
 .name = "mmc0_2_grp",
 .pins = mmc0_2_pins,
 .num_pins = ARRAY_SIZE(mmc0_2_pins),
 },
 {
 .name = "mmc0_3_grp",
 .pins = mmc0_3_pins,
 .num_pins = ARRAY_SIZE(mmc0_3_pins),

},
};


static int foo_get_groups_count(struct pinctrl_dev *pctldev)
{

return ARRAY_SIZE(foo_groups);
}

static const char *foo_get_group_name(struct pinctrl_dev *pctldev,

unsigned selector)
{

return foo_groups[selector].name;
}

static int foo_get_group_pins(struct pinctrl_dev *pctldev, unsigned selector,
 unsigned ** const pins,
 unsigned * const num_pins)
{
 *pins = (unsigned *) foo_groups[selector].pins;
 *num_pins = foo_groups[selector].num_pins;68
return 0;
}

static struct pinctrl_ops foo_pctrl_ops = {
 .get_groups_count = foo_get_groups_count,
 .get_group_name = foo_get_group_name,
 .get_group_pins = foo_get_group_pins,
};

struct foo_pmx_func {
 const char *name;
 const char * const *groups;
 const unsigned num_groups;
};

static const char * const spi0_groups[] = { "spi0_0_grp", "spi0_1_grp" };
static const char * const i2c0_groups[] = { "i2c0_grp" };
static const char * const mmc0_groups[] = { "mmc0_1_grp", "mmc0_2_grp","mmc0_3_grp" };

static const struct foo_pmx_func foo_functions[] = {

{
.name = "spi0",
.groups = spi0_groups,
.num_groups = ARRAY_SIZE(spi0_groups),
},
{
.name = "i2c0",
.groups = i2c0_groups,
.num_groups = ARRAY_SIZE(i2c0_groups),
},
{

.name = "mmc0",101 .groups = mmc0_groups,
.num_groups = ARRAY_SIZE(mmc0_groups),

},
};

int foo_get_functions_count(struct pinctrl_dev *pctldev)
{

return ARRAY_SIZE(foo_functions);
}

const char *foo_get_fname(struct pinctrl_dev *pctldev, unsigned selector)
{

return foo_functions[selector].name;
}

static int foo_get_groups(struct pinctrl_dev *pctldev, unsigned selector,
 const char * const **groups,
 unsigned * const num_groups)
{
 *groups = foo_functions[selector].groups;
 *num_groups = foo_functions[selector].num_groups;
 return 0;
}

int foo_enable(struct pinctrl_dev *pctldev, unsigned selector,

unsigned group)
{

regbit = (1 << selector + group);

 writeb((readb(MUX)|regbit), MUX)
 return 0;
}
void foo_disable(struct pinctrl_dev *pctldev, unsigned selector,

unsigned group)
{

u8 regbit = (1 << selector + group);

 writeb((readb(MUX) & ~(regbit)), MUX)
 return 0;
}

struct pinmux_ops foo_pmxops = {
 .get_functions_count = foo_get_functions_count,
 .get_function_name = foo_get_fname,
 .get_function_groups = foo_get_groups,
 .enable = foo_enable,
 .disable = foo_disable,
};

/* Pinmux operations are handled by some pin controller */
static struct pinctrl_desc foo_desc = {
 ...
 .pctlops = &foo_pctrl_ops,
 .pmxops = &foo_pmxops,
};

具体的 pinctrl 、使用引脚的设备、功能、引脚组的映射关系,可以在板文件中通过定义 pinctrl_map 结构体的实例来展开,如:

static struct pinctrl_map __initdata mapping[] = {
PIN_MAP_MUX_GROUP("foo-i2c.o", PINCTRL_STATE_DEFAULT, "pinctrl-foo", NULL,
"i2c0"),
};

又由于 1 个功能可由两个不同的引脚组实现,所以对于同 1 个功能可能形成有两个可选引脚组的 pinctrl_map :

static struct 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"),
};

其中调用的 PIN_MAP_MUX_GROUP 是一个快捷宏,用于赋值 pinctrl_map 的各个成员:

#define PIN_MAP_MUX_GROUP(dev, state, pinctrl, grp, func)		\
	{								\
		.dev_name = dev,					\
		.name = state,						\
		.type = PIN_MAP_TYPE_MUX_GROUP,				\
		.ctrl_dev_name = pinctrl,				\
		.data.mux = {						\
			.group = grp,					\
			.function = func,				\
		},							\
	}

当然,这种映射关系最好是在设备树中通过节点的属性进行,具体的节点属性的定义方法依赖于具体的 pinctrl 驱动,最终在 pinctrl 驱动中通过 pinctrl_ops 结构体的 .dt_node_to_map ()成员函数读出属性并建立映射表
在运行时,我们可以通过类似的 API 去查找并设置位置 A 的引脚组以行驶 SPI 接口的功能:

p = devm_pinctrl_get(dev);
s = pinctrl_lookup_state(p, "spi0-pos-A ");
ret = pinctrl_select_state(p, s)

或者可以更加简单地使用:

p = devm_pinctrl_get_select(dev, "spi0-pos-A");

若想在运行时切换位置 A 和 B 的引脚组以行使 SPI 的接口功能,代码结构类似清单所示:

pinctrl_lookup_state ()和 pinctrl_select_state ()
foo_probe()
{

/* Setup */ 
 p = devm_pinctrl_get(&device);
 if (IS_ERR(p))
...

 s1 = pinctrl_lookup_state(foo->p, " spi0-pos-A");
 if (IS_ERR(s1))

...
 s2 = pinctrl_lookup_state(foo->p, " spi0-pos-B");
 if (IS_ERR(s2))

...
}

foo_switch()
{
 /* Enable on position A */
 ret = pinctrl_select_state(s1);
 if (ret < 0)
 ...
 ...

 /* Enable on position B */
 ret = pinctrl_select_state(s2);
 if (ret < 0)
 ...
 ...
}

对于 “default” 状态下的引脚配置,驱动一般不需要完成 devm_pinctrl_get_select ( dev , “default” )的调用。譬如对于 arch/arm/boot/dts/prima2-evb.dts 中的如下引脚组:

peri-iobg {
uart@b0060000 {
pinctrl-names = "default";
pinctrl-0 = <&uart1_pins_a>;
};
spi@b00d0000 {
pinctrl-names = "default";
pinctrl-0 = <&spi0_pins_a>;
};
spi@b0170000 {
pinctrl-names = "default";
pinctrl-0 = <&spi1_pins_a>;
};
};

由于 pinctrl-names 都是 “default” 的,所以 pinctrl 核实际会自动做类似 devm_pinctrl_get_select ( dev , “default” )的操作。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值